pashage

Yet Another Opinionated Re-engineering of the Unix Password Store
git clone https://git.instinctive.eu/pashage.git
Log | Files | Refs | README | LICENSE

internal_spec.sh (9378B)


      1 # pashage - age-backed POSIX password manager
      2 # Copyright (C) 2024  Natasha Kerensikova
      3 #
      4 # This program is free software; you can redistribute it and/or
      5 # modify it under the terms of the GNU General Public License
      6 # as published by the Free Software Foundation; either version 2
      7 # of the License, or (at your option) any later version.
      8 #
      9 # This program is distributed in the hope that it will be useful,
     10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 # GNU General Public License for more details.
     13 #
     14 # You should have received a copy of the GNU General Public License
     15 # along with this program; if not, write to the Free Software
     16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     17 
     18 # This test file covers all internal helper functions,
     19 # These functions are fundemantal so there is no need for mocking,
     20 # except for the interactive path in `yesno`.
     21 
     22 Describe 'Internal Helper Functions'
     23   Include src/pashage.sh
     24   if [ "${SHELLSPEC_SHELL_TYPE}" = sh ]; then
     25     Set 'errexit:on' 'nounset:on'
     26   else
     27     Set 'errexit:on' 'nounset:on' 'pipefail:on'
     28   fi
     29 
     30   Describe 'check_sneaky_path'
     31     It 'accept an empty path'
     32       When run check_sneaky_path ''
     33       The status should be success
     34       The error should be blank
     35       The output should be blank
     36     End
     37 
     38     It 'accepts a file name'
     39       When run check_sneaky_path 'a'
     40       The status should be success
     41       The error should be blank
     42       The output should be blank
     43     End
     44 
     45     It 'accepts an absolute path'
     46       When run check_sneaky_path '/a/b/c'
     47       The status should be success
     48       The error should be blank
     49       The output should be blank
     50     End
     51 
     52     It 'accepts a relative path'
     53       When run check_sneaky_path 'a/b/c/'
     54       The status should be success
     55       The error should be blank
     56       The output should be blank
     57     End
     58 
     59     It 'aborts when .. is a path component'
     60       When run check_sneaky_path 'a/../b'
     61       The error should equal 'Encountered path considered sneaky: "a/../b"'
     62       The output should be blank
     63       The status should equal 1
     64     End
     65 
     66     It 'aborts when .. is a path prefix'
     67       When run check_sneaky_path '../a/b'
     68       The error should equal 'Encountered path considered sneaky: "../a/b"'
     69       The output should be blank
     70       The status should equal 1
     71     End
     72 
     73     It 'aborts when .. is a path suffix'
     74       When run check_sneaky_path '/a/..'
     75       The error should equal 'Encountered path considered sneaky: "/a/.."'
     76       The output should be blank
     77       The status should equal 1
     78     End
     79 
     80     It 'aborts when .. is the whole path'
     81       When run check_sneaky_path '..'
     82       The error should equal 'Encountered path considered sneaky: ".."'
     83       The output should be blank
     84       The status should equal 1
     85     End
     86   End
     87 
     88   Describe 'check_sneaky_paths'
     89     It 'aborts when all paths are bad'
     90       When run check_sneaky_paths ../a b/../c
     91       The error should equal 'Encountered path considered sneaky: "../a"'
     92       The output should be blank
     93       The status should equal 1
     94     End
     95 
     96     It 'accepts several good paths'
     97       When run check_sneaky_paths a b/c /d/e/f
     98       The status should be success
     99       The error should be blank
    100       The output should be blank
    101     End
    102 
    103     It 'accepts an empty argument list'
    104       When run check_sneaky_paths
    105       The status should be success
    106       The error should be blank
    107       The output should be blank
    108     End
    109 
    110     It 'aborts when a single path is bad'
    111       When run check_sneaky_paths a b/../c /d/e/f
    112       The error should equal 'Encountered path considered sneaky: "b/../c"'
    113       The output should be blank
    114       The status should equal 1
    115     End
    116   End
    117 
    118   Describe 'checked'
    119     echo_ret() { printf '%s\n' "$1"; return $2; }
    120 
    121     It 'aborts on command failure and reports it'
    122       When run checked echo_ret 'it runs' 42
    123       The output should equal 'it runs'
    124       The error should equal 'Fatal(42): echo_ret it runs 42'
    125       The status should equal 42
    126     End
    127 
    128     It 'continues silently when the command is successful'
    129       When run checked echo_ret 'it runs' 0
    130       The status should be success
    131       The output should equal 'it runs'
    132       The error should be blank
    133     End
    134   End
    135 
    136   Specify 'die'
    137     When run die Message Word
    138     The error should equal 'Message Word'
    139     The output should be blank
    140     The status should equal 1
    141   End
    142 
    143   Describe 'set_LOCAL_RECIPIENT_FILE'
    144     PREFIX="${SHELLSPEC_WORKDIR}/prefix/store"
    145     setup() {
    146       @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/special"
    147       echo "Outside recipient" >"${PREFIX}/../.age-recipients"
    148       echo "Toplevel recipient" >"${PREFIX}/.age-recipients"
    149       echo "Subdir recipient" >"${PREFIX}/subdir/.age-recipients"
    150     }
    151     cat() { @cat "$@"; }
    152     cleanup() { @rm -rf "${PREFIX}"; }
    153 
    154     BeforeEach 'setup'
    155     AfterEach 'cleanup'
    156 
    157     It 'returns root from root'
    158       When call set_LOCAL_RECIPIENT_FILE foo
    159       The status should be success
    160       The variable LOCAL_RECIPIENT_FILE should equal \
    161         "${PREFIX}/.age-recipients"
    162       The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient'
    163     End
    164 
    165     It 'returns root from unmarked subdirectory'
    166       When call set_LOCAL_RECIPIENT_FILE special/foo
    167       The status should be success
    168       The variable LOCAL_RECIPIENT_FILE should equal \
    169         "${PREFIX}/.age-recipients"
    170       The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient'
    171     End
    172 
    173     It 'returns subdirectory from itself'
    174       When call set_LOCAL_RECIPIENT_FILE subdir/foo
    175       The status should be success
    176       The variable LOCAL_RECIPIENT_FILE should equal \
    177         "${PREFIX}/subdir/.age-recipients"
    178       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    179     End
    180 
    181     It 'returns subdirectory from sub-subdirectory'
    182       When call set_LOCAL_RECIPIENT_FILE subdir/subsub/foo
    183       The status should be success
    184       The variable LOCAL_RECIPIENT_FILE should equal \
    185         "${PREFIX}/subdir/.age-recipients"
    186       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    187     End
    188 
    189     setup() {
    190       @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/special"
    191       echo "Outside recipient" >"${PREFIX}/../.age-recipients"
    192       echo "Subdir recipient" >"${PREFIX}/subdir/.age-recipients"
    193     }
    194 
    195     It 'returns nothing from empty root'
    196       When call set_LOCAL_RECIPIENT_FILE foo
    197       The status should be success
    198       The variable LOCAL_RECIPIENT_FILE should equal ''
    199       The variable LOCAL_RECIPIENTS should equal ''
    200     End
    201 
    202     It 'returns nothing from unmarked subdirectory below empty root'
    203       When call set_LOCAL_RECIPIENT_FILE special/foo
    204       The status should be success
    205       The variable LOCAL_RECIPIENT_FILE should equal ''
    206       The variable LOCAL_RECIPIENTS should equal ''
    207     End
    208 
    209     It 'returns subdirectory from itself even under empty root'
    210       When call set_LOCAL_RECIPIENT_FILE subdir/foo
    211       The status should be success
    212       The variable LOCAL_RECIPIENT_FILE should equal \
    213         "${PREFIX}/subdir/.age-recipients"
    214       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    215     End
    216 
    217     It 'returns subdirectory from sub-subdirectory even under empty root'
    218       When call set_LOCAL_RECIPIENT_FILE subdir/subsub/foo
    219       The status should be success
    220       The variable LOCAL_RECIPIENT_FILE should equal \
    221         "${PREFIX}/subdir/.age-recipients"
    222       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    223     End
    224   End
    225 
    226   Describe 'strlen'
    227     It 'accepts an ASCII and returns its length'
    228       When call strlen 'abc def'
    229       The output should equal 7
    230     End
    231 
    232     It 'accepts an empty string and returns 0'
    233       When call strlen ''
    234       The output should equal 0
    235     End
    236   End
    237 
    238   Describe 'yesno'
    239     Describe 'Without stty'
    240       It 'accepts an uppercase N'
    241         Data 'N'
    242         When call yesno 'prompt'
    243         The status should be success
    244         The output should equal 'prompt [y/n]'
    245         The variable ANSWER should equal 'N'
    246       End
    247 
    248       It 'accepts an uppercase Y'
    249         Data 'YES'
    250         When call yesno 'prompt'
    251         The status should be success
    252         The output should equal 'prompt [y/n]'
    253         The variable ANSWER should equal 'y'
    254       End
    255     End
    256 
    257     Describe 'Dumb terminal with stty'
    258       stty() { false; }
    259 
    260       It 'accepts a lowercase N'
    261         Data 'no'
    262         When call yesno 'prompt'
    263         The status should be success
    264         The output should equal 'prompt [y/n]'
    265         The variable ANSWER should equal 'n'
    266       End
    267 
    268       It 'accepts an uppercase Y'
    269         Data 'Y'
    270         When call yesno 'prompt'
    271         The status should be success
    272         The output should equal 'prompt [y/n]'
    273         The variable ANSWER should equal 'y'
    274       End
    275     End
    276 
    277     Describe 'Mocking a terminal'
    278       setup() {
    279         %putsn x >|"${SHELLSPEC_WORKDIR}/first-dd"
    280       }
    281       stty() { :; }
    282       dd() {
    283         if [ -f "${SHELLSPEC_WORKDIR}/first-dd" ]; then
    284           %puts x
    285           @rm "${SHELLSPEC_WORKDIR}/first-dd"
    286         else
    287           %puts y
    288         fi
    289       }
    290 
    291       BeforeEach setup
    292 
    293       It 'accepts a lowercase Y after bad input'
    294         When call yesno 'prompt'
    295         The status should be success
    296         The output should equal 'prompt [y/n]'
    297         The variable ANSWER should equal 'y'
    298       End
    299     End
    300   End
    301 End