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 (9761B)


      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 'glob_exists'
    144     It 'answers y when the glob matches something'
    145       When call glob_exists /*
    146       The status should be success
    147       The variable ANSWER should equal y
    148     End
    149 
    150     It 'answers n when the glob does not match anything'
    151       When call glob_exists non-existent/*
    152       The status should be success
    153       The variable ANSWER should equal n
    154     End
    155   End
    156 
    157   Describe 'set_LOCAL_RECIPIENT_FILE'
    158     PREFIX="${SHELLSPEC_WORKDIR}/prefix/store"
    159     setup() {
    160       @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/special"
    161       echo "Outside recipient" >"${PREFIX}/../.age-recipients"
    162       echo "Toplevel recipient" >"${PREFIX}/.age-recipients"
    163       echo "Subdir recipient" >"${PREFIX}/subdir/.age-recipients"
    164     }
    165     cat() { @cat "$@"; }
    166     cleanup() { @rm -rf "${PREFIX}"; }
    167 
    168     BeforeEach 'setup'
    169     AfterEach 'cleanup'
    170 
    171     It 'returns root from root'
    172       When call set_LOCAL_RECIPIENT_FILE foo
    173       The status should be success
    174       The variable LOCAL_RECIPIENT_FILE should equal \
    175         "${PREFIX}/.age-recipients"
    176       The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient'
    177     End
    178 
    179     It 'returns root from unmarked subdirectory'
    180       When call set_LOCAL_RECIPIENT_FILE special/foo
    181       The status should be success
    182       The variable LOCAL_RECIPIENT_FILE should equal \
    183         "${PREFIX}/.age-recipients"
    184       The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient'
    185     End
    186 
    187     It 'returns subdirectory from itself'
    188       When call set_LOCAL_RECIPIENT_FILE subdir/foo
    189       The status should be success
    190       The variable LOCAL_RECIPIENT_FILE should equal \
    191         "${PREFIX}/subdir/.age-recipients"
    192       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    193     End
    194 
    195     It 'returns subdirectory from sub-subdirectory'
    196       When call set_LOCAL_RECIPIENT_FILE subdir/subsub/foo
    197       The status should be success
    198       The variable LOCAL_RECIPIENT_FILE should equal \
    199         "${PREFIX}/subdir/.age-recipients"
    200       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    201     End
    202 
    203     setup() {
    204       @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/special"
    205       echo "Outside recipient" >"${PREFIX}/../.age-recipients"
    206       echo "Subdir recipient" >"${PREFIX}/subdir/.age-recipients"
    207     }
    208 
    209     It 'returns nothing from empty root'
    210       When call set_LOCAL_RECIPIENT_FILE foo
    211       The status should be success
    212       The variable LOCAL_RECIPIENT_FILE should equal ''
    213       The variable LOCAL_RECIPIENTS should equal ''
    214     End
    215 
    216     It 'returns nothing from unmarked subdirectory below empty root'
    217       When call set_LOCAL_RECIPIENT_FILE special/foo
    218       The status should be success
    219       The variable LOCAL_RECIPIENT_FILE should equal ''
    220       The variable LOCAL_RECIPIENTS should equal ''
    221     End
    222 
    223     It 'returns subdirectory from itself even under empty root'
    224       When call set_LOCAL_RECIPIENT_FILE subdir/foo
    225       The status should be success
    226       The variable LOCAL_RECIPIENT_FILE should equal \
    227         "${PREFIX}/subdir/.age-recipients"
    228       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    229     End
    230 
    231     It 'returns subdirectory from sub-subdirectory even under empty root'
    232       When call set_LOCAL_RECIPIENT_FILE subdir/subsub/foo
    233       The status should be success
    234       The variable LOCAL_RECIPIENT_FILE should equal \
    235         "${PREFIX}/subdir/.age-recipients"
    236       The variable LOCAL_RECIPIENTS should equal 'Subdir recipient'
    237     End
    238   End
    239 
    240   Describe 'strlen'
    241     It 'accepts an ASCII and returns its length'
    242       When call strlen 'abc def'
    243       The output should equal 7
    244     End
    245 
    246     It 'accepts an empty string and returns 0'
    247       When call strlen ''
    248       The output should equal 0
    249     End
    250   End
    251 
    252   Describe 'yesno'
    253     Describe 'Without stty'
    254       It 'accepts an uppercase N'
    255         Data 'N'
    256         When call yesno 'prompt'
    257         The status should be success
    258         The output should equal 'prompt [y/n]'
    259         The variable ANSWER should equal 'N'
    260       End
    261 
    262       It 'accepts an uppercase Y'
    263         Data 'YES'
    264         When call yesno 'prompt'
    265         The status should be success
    266         The output should equal 'prompt [y/n]'
    267         The variable ANSWER should equal 'y'
    268       End
    269     End
    270 
    271     Describe 'Dumb terminal with stty'
    272       stty() { false; }
    273 
    274       It 'accepts a lowercase N'
    275         Data 'no'
    276         When call yesno 'prompt'
    277         The status should be success
    278         The output should equal 'prompt [y/n]'
    279         The variable ANSWER should equal 'n'
    280       End
    281 
    282       It 'accepts an uppercase Y'
    283         Data 'Y'
    284         When call yesno 'prompt'
    285         The status should be success
    286         The output should equal 'prompt [y/n]'
    287         The variable ANSWER should equal 'y'
    288       End
    289     End
    290 
    291     Describe 'Mocking a terminal'
    292       setup() {
    293         %putsn x >|"${SHELLSPEC_WORKDIR}/first-dd"
    294       }
    295       stty() { :; }
    296       dd() {
    297         if [ -f "${SHELLSPEC_WORKDIR}/first-dd" ]; then
    298           %puts x
    299           @rm "${SHELLSPEC_WORKDIR}/first-dd"
    300         else
    301           %puts y
    302         fi
    303       }
    304 
    305       BeforeEach setup
    306 
    307       It 'accepts a lowercase Y after bad input'
    308         When call yesno 'prompt'
    309         The status should be success
    310         The output should equal 'prompt [y/n]'
    311         The variable ANSWER should equal 'y'
    312       End
    313     End
    314   End
    315 End