pashage

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

pass_spec.sh (111578B)


      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 should pass with pashage, pass (the original password-store),
     19 # or passage (the pass fork by Filippo Valsorda).
     20 
     21 # The test suite also checks the internal of the store, thanks to mocked
     22 # cryptography. This should ensure that pashage is indeed a drop-in replacement
     23 # for passage, and that both of them can update interchangeably the same
     24 # password store repository.
     25 
     26 # The other password-store scripts can be added to the parameters list
     27 # to run the cases against it.
     28 # bash seems to escape the sandbox by resetting PATH on the slightest
     29 # provocation, while the tests rely heavily on the mocked cryptography
     30 # to check the store behavior.
     31 # So it only works when using shellspec with bash and calling the `pass`
     32 # or `password-store.sh` script directly
     33 # (e.g. on FreeBSD `/usr/local/libexec/password-store/pass`
     34 # instead of `/usr/local/bin/pass`).
     35 
     36 Parameters # script/path           scriptname  encryption
     37 # /usr/bin/pass                      pass         gpg
     38 # /git/passage/src/password-store.sh passage      age
     39   ./src/run.sh                       pashage      age
     40 End
     41 
     42 Describe 'Pass-like command'
     43   check_skip() {
     44     [ -z "${1%%pass*}" ] && ! [ "${SHELLSPEC_SHELL_TYPE}" = bash ]
     45   }
     46 
     47   GITLOG="${SHELLSPEC_WORKDIR}/git-log.txt"
     48   PREFIX="${SHELLSPEC_WORKDIR}/store"
     49 
     50   export PASSWORD_STORE_DIR="${PREFIX}"
     51   export PASSAGE_DIR="${PREFIX}"
     52   export PASSAGE_IDENTITIES_FILE="${SHELLSPEC_WORKDIR}/age-identities"
     53 
     54   git_log() {
     55     @git -C "${PREFIX}" status --porcelain >&2
     56     @git -C "${PREFIX}" log --format='%s' --stat >|"${GITLOG}"
     57   }
     58 
     59   setup_log() { %text
     60     #|Initial setup
     61     #|
     62     #| -g.age                 | 2 ++
     63     #| -g.gpg                 | 2 ++
     64     #| .gpg-id                | 1 +
     65     #| extra.age              | 2 ++
     66     #| extra.gpg              | 2 ++
     67     #| extra/subdir/file.age  | 2 ++
     68     #| extra/subdir/file.gpg  | 2 ++
     69     #| fluff/.age-recipients  | 2 ++
     70     #| fluff/.gpg-id          | 2 ++
     71     #| fluff/one.age          | 3 +++
     72     #| fluff/one.gpg          | 3 +++
     73     #| fluff/three.age        | 5 +++++
     74     #| fluff/three.gpg        | 5 +++++
     75     #| fluff/two.age          | 4 ++++
     76     #| fluff/two.gpg          | 4 ++++
     77     #| shared/.age-recipients | 2 ++
     78     #| shared/.gpg-id         | 2 ++
     79     #| stale.age              | 3 +++
     80     #| stale.gpg              | 3 +++
     81     #| subdir/file.age        | 2 ++
     82     #| subdir/file.gpg        | 2 ++
     83     #| y.txt                  | 3 +++
     84     #| 22 files changed, 58 insertions(+)
     85   }
     86 
     87   setup_id() {
     88     @mkdir -p "${PREFIX}/$1"
     89     @cat >"${PREFIX}/$1/.age-recipients"
     90     @cp -i "${PREFIX}/$1/.age-recipients" "${PREFIX}/$1/.gpg-id"
     91   }
     92 
     93   setup_secret() {
     94     [ "$1" = "${1%/*}" ] || @mkdir -p "${PREFIX}/${1%/*}"
     95     @sed 's/^/age/' >"${PREFIX}/$1.age"
     96     @sed 's/^age/gpg/' "${PREFIX}/$1.age" >"${PREFIX}/$1.gpg"
     97   }
     98 
     99   setup() {
    100     @git init -q -b main "${PREFIX}"
    101     @git -C "${PREFIX}" config --local user.name 'Test User'
    102     @git -C "${PREFIX}" config --local user.email 'test@example.com'
    103     %putsn 'myself' >"${PASSAGE_IDENTITIES_FILE}"
    104     %putsn 'myself' >"${PREFIX}/.gpg-id"
    105     %text | setup_secret 'subdir/file'
    106     #|Recipient:myself
    107     #|:p4ssw0rd
    108     %text | setup_secret 'extra'
    109     #|Recipient:myself
    110     #|:ambiguous
    111     %text | setup_secret 'extra/subdir/file'
    112     #|Recipient:myself
    113     #|:Pa55worD
    114     %text | setup_id 'shared'
    115     #|myself
    116     #|friend
    117     %text | setup_id 'fluff'
    118     #|master
    119     #|myself
    120     %text | setup_secret 'fluff/one'
    121     #|Recipient:master
    122     #|Recipient:myself
    123     #|:1-password
    124     %text | setup_secret 'fluff/two'
    125     #|Recipient:master
    126     #|Recipient:myself
    127     #|:2-password
    128     #|:URL: https://example.com/login
    129     %text | setup_secret 'fluff/three'
    130     #|Recipient:master
    131     #|Recipient:myself
    132     #|:3-password
    133     #|:Username: 3Jane
    134     #|:URL: https://example.com/login
    135     %text | setup_secret 'stale'
    136     #|Recipient:master
    137     #|Recipient:myself
    138     #|:0-password
    139     %text | setup_secret '-g'
    140     #|Recipient:myself
    141     #|:--
    142     %text >"${PREFIX}/y.txt"
    143     #|Unencrypted line 1
    144     #|Unencrypted line 2
    145     #|Unencrypted line 3
    146     @git -C "${PREFIX}" add .
    147     @git -C "${PREFIX}" commit -m 'Initial setup' >/dev/null
    148 
    149     # Check setup_log consistency
    150     git_log
    151     setup_log | @diff -u - "${GITLOG}"
    152   }
    153 
    154   cleanup() {
    155     @rm -rf "${PREFIX}"
    156     @rm -f "${PASSAGE_IDENTITIES_FILE}"
    157   }
    158 
    159   BeforeEach setup
    160   AfterEach cleanup
    161 
    162   Mock age
    163     mock-age "$@"
    164   End
    165 
    166   Mock base64
    167     . "${SHELLSPEC_SUPPORT_BIN}"
    168     invoke base64 "$@"
    169   End
    170 
    171   Mock basename
    172     @basename "$@"
    173   End
    174 
    175   Mock cat
    176     @cat "$@"
    177   End
    178 
    179   Mock cp
    180     @cp "$@"
    181   End
    182 
    183   Mock cut
    184     . "${SHELLSPEC_SUPPORT_BIN}"
    185     invoke cut "$@"
    186   End
    187 
    188   Mock dd
    189     @dd "$@"
    190   End
    191 
    192   Mock diff
    193     @diff "$@"
    194   End
    195 
    196   Mock dirname
    197     @dirname "$@"
    198   End
    199 
    200   Mock ed
    201     . "${SHELLSPEC_SUPPORT_BIN}"
    202     if [ "${1-}" = '-c' ]; then
    203       shift
    204       invoke touch "$@"
    205     fi
    206     invoke ed "$@"
    207   End
    208 
    209   Mock feh
    210     printf '$ feh %s\n' "$*" >&2
    211     @cat >&2
    212   End
    213 
    214   Mock find
    215     . "${SHELLSPEC_SUPPORT_BIN}"
    216     invoke find "$@"
    217   End
    218 
    219   Mock git
    220     @git "$@"
    221   End
    222 
    223   Mock gpg
    224     mock-gpg "$@"
    225   End
    226 
    227   Mock grep
    228     @grep "$@"
    229   End
    230 
    231   Mock head
    232     @head "$@"
    233   End
    234 
    235   Mock mkdir
    236     @mkdir "$@"
    237   End
    238 
    239   Mock mv
    240     @mv "$@"
    241   End
    242 
    243   Mock openssl
    244     . "${SHELLSPEC_SUPPORT_BIN}"
    245     invoke openssl "$@"
    246   End
    247 
    248   Mock qrencode
    249     . "${SHELLSPEC_SUPPORT_BIN}"
    250     invoke od -v -t x1 | sed 's/  */ /g;s/ *$//'
    251   End
    252 
    253   Mock mktemp
    254     . "${SHELLSPEC_SUPPORT_BIN}"
    255     invoke mktemp "$@"
    256   End
    257 
    258   Mock rm
    259     @rm "$@"
    260   End
    261 
    262   Mock rmdir
    263     . "${SHELLSPEC_SUPPORT_BIN}"
    264     invoke rmdir "$@"
    265   End
    266 
    267   Mock sed
    268     @sed "$@"
    269   End
    270 
    271   Mock sleep
    272     :
    273   End
    274 
    275   Mock sort
    276     . "${SHELLSPEC_SUPPORT_BIN}"
    277     invoke sort "$@"
    278   End
    279 
    280   Mock tail
    281     @tail "$@"
    282   End
    283 
    284   Mock tr
    285     @tr "$@"
    286   End
    287 
    288   Mock tree
    289     . "${SHELLSPEC_SUPPORT_BIN}"
    290     invoke tree "$@"
    291   End
    292 
    293   Mock uname
    294     @uname "$@"
    295   End
    296 
    297   Mock which
    298     false
    299   End
    300 
    301   Mock xclip
    302     . "${SHELLSPEC_SUPPORT_BIN}"
    303     if [ "$1" = '-o' ]; then
    304       printf 'previous contents\n'
    305     else
    306       printf '$ xclip %s\n' "$*" >&2
    307       invoke od -v -t x1 | sed 's/  */ /g;s/ *$//' >&2
    308     fi
    309   End
    310 
    311   Describe 'init'
    312     It 're-encrypts the whole store using a new recipient id'
    313       Skip if 'pass(age) needs bash' check_skip $2
    314       Skip if 'passage has no init' [ "$2" = passage ]
    315       When run script $1 init 'new-id'
    316       The status should be success
    317       The output should include 'Password store'
    318       expected_log() {
    319         if [ "$2" = pashage ]; then
    320           %text
    321           #|Set age recipients at store root
    322           #|
    323           #| -g.age                | 2 +-
    324           #| .age-recipients       | 1 +
    325           #| extra.age             | 2 +-
    326           #| extra/subdir/file.age | 2 +-
    327           #| stale.age             | 3 +--
    328           #| subdir/file.age       | 2 +-
    329           #| 6 files changed, 6 insertions(+), 6 deletions(-)
    330         else
    331           %text:expand
    332           #|Reencrypt password store using new GPG id new-id.
    333           #|
    334           #| -g.$1                | 2 +-
    335           #| extra.$1             | 2 +-
    336           #| extra/subdir/file.$1 | 2 +-
    337           #| stale.$1             | 3 +--
    338           #| subdir/file.$1       | 2 +-
    339           #| 5 files changed, 5 insertions(+), 6 deletions(-)
    340           #|Set GPG id to new-id.
    341           #|
    342           #| .gpg-id | 2 +-
    343           #| 1 file changed, 1 insertion(+), 1 deletion(-)
    344         fi
    345         setup_log
    346       }
    347       The result of function git_log should be successful
    348       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
    349     End
    350 
    351     It 're-encrypts a subdirectory using a new recipient id'
    352       Skip if 'pass(age) needs bash' check_skip $2
    353       Skip if 'passage has no init' [ "$2" = passage ]
    354       When run script $1 init -p subdir 'new-id'
    355       The status should be success
    356       The output should start with 'Password store'
    357       The output should include 'subdir'
    358       expected_log() {
    359         if [ "$2" = pashage ]; then
    360           %text
    361           #|Set age recipients at subdir
    362           #|
    363           #| subdir/.age-recipients | 1 +
    364           #| subdir/file.age        | 2 +-
    365           #| 2 files changed, 2 insertions(+), 1 deletion(-)
    366         else
    367           %text:expand
    368           #|Reencrypt password store using new GPG id new-id (subdir).
    369           #|
    370           #| subdir/file.$1 | 2 +-
    371           #| 1 file changed, 1 insertion(+), 1 deletion(-)
    372           #|Set GPG id to new-id (subdir).
    373           #|
    374           #| subdir/.gpg-id | 1 +
    375           #| 1 file changed, 1 insertion(+)
    376         fi
    377         setup_log
    378       }
    379       The result of function git_log should be successful
    380       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
    381     End
    382 
    383     It 're-encrypts a subdirectory after replacing recipient ids'
    384       Skip if 'pass(age) needs bash' check_skip $2
    385       Skip if 'passage has no init' [ "$2" = passage ]
    386       When run script $1 init --path=fluff 'new-id' 'new-master'
    387       The status should be success
    388       The output should start with 'Password store'
    389       The output should include 'fluff'
    390       expected_log() {
    391         if [ "$2" = pashage ]; then
    392           %text
    393           #|Set age recipients at fluff
    394           #|
    395           #| fluff/.age-recipients | 4 ++--
    396           #| fluff/one.age         | 4 ++--
    397           #| fluff/three.age       | 4 ++--
    398           #| fluff/two.age         | 4 ++--
    399           #| 4 files changed, 8 insertions(+), 8 deletions(-)
    400         else
    401           %text:expand
    402           #|Reencrypt password store using new GPG id new-id, new-master (fluff).
    403           #|
    404           #| fluff/one.$1   | 4 ++--
    405           #| fluff/three.$1 | 4 ++--
    406           #| fluff/two.$1   | 4 ++--
    407           #| 3 files changed, 6 insertions(+), 6 deletions(-)
    408           #|Set GPG id to new-id, new-master (fluff).
    409           #|
    410           #| fluff/.gpg-id | 4 ++--
    411           #| 1 file changed, 2 insertions(+), 2 deletions(-)
    412         fi
    413         setup_log
    414       }
    415       The result of function git_log should be successful
    416       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
    417     End
    418 
    419     It 're-encrypts a subdirectory after removing dedicated recipient ids'
    420       Skip if 'pass(age) needs bash' check_skip $2
    421       Skip if 'passage has no init' [ "$2" = passage ]
    422       When run script $1 init -pfluff ''
    423       The status should be successful
    424       expected_log() {
    425         if [ "$2" = pashage ]; then
    426           %text
    427           #|Deinitialize fluff
    428           #|
    429           #| fluff/.age-recipients | 2 --
    430           #| fluff/one.age         | 1 -
    431           #| fluff/three.age       | 1 -
    432           #| fluff/two.age         | 1 -
    433           #| 4 files changed, 5 deletions(-)
    434         else
    435           %text:expand
    436           #|Reencrypt password store using new GPG id  (fluff).
    437           #|
    438           #| fluff/one.$1   | 1 -
    439           #| fluff/three.$1 | 1 -
    440           #| fluff/two.$1   | 1 -
    441           #| 3 files changed, 3 deletions(-)
    442           #|Deinitialize ${PREFIX}/fluff/.gpg-id (fluff).
    443           #|
    444           #| fluff/.gpg-id | 2 --
    445           #| 1 file changed, 2 deletions(-)
    446         fi
    447         setup_log
    448       }
    449       The result of function git_log should be successful
    450       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
    451     End
    452 
    453     It 'creates a new subdirectory with recipient ids'
    454       Skip if 'pass(age) needs bash' check_skip $2
    455       Skip if 'passage has no init' [ "$2" = passage ]
    456       When run script $1 init -p newdir new-id new-master
    457       The status should be successful
    458       The output should include 'newdir'
    459       The directory "${PREFIX}/newdir" should be exist
    460       expected_log() {
    461         if [ "$2" = pashage ]; then
    462           %text
    463           #|Set age recipients at newdir
    464           #|
    465           #| newdir/.age-recipients | 2 ++
    466           #| 1 file changed, 2 insertions(+)
    467         else
    468           %text:expand
    469           #|Set GPG id to new-id, new-master (newdir).
    470           #|
    471           #| newdir/.gpg-id | 2 ++
    472           #| 1 file changed, 2 insertions(+)
    473         fi
    474         setup_log
    475       }
    476       The result of function git_log should be successful
    477       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
    478     End
    479 
    480     It 'does not create a new subdirectory without recipient id'
    481       Skip if 'pass(age) needs bash' check_skip $2
    482       Skip if 'passage has no init' [ "$2" = passage ]
    483       When run script $1 init -p newdir ''
    484       The status should be successful
    485       The output should be blank
    486       The error should include "newdir"
    487       The directory "${PREFIX}/newdir" should not be exist
    488       The result of function git_log should be successful
    489       The contents of file "${GITLOG}" should equal "$(setup_log)"
    490     End
    491 
    492     It 'displays usage when called without argument'
    493       Skip if 'pass(age) needs bash' check_skip $2
    494       Skip if 'passage has no init' [ "$2" = passage ]
    495       When run script $1 init
    496       The status should equal 1
    497       The output should be blank
    498       The error should include 'Usage:'
    499       The error should include ' init '
    500       The result of function git_log should be successful
    501       The contents of file "${GITLOG}" should equal "$(setup_log)"
    502     End
    503 
    504     It 'displays usage when called with incomplete flag'
    505       Skip if 'pass(age) needs bash' check_skip $2
    506       Skip if 'passage has no init' [ "$2" = passage ]
    507       When run script $1 init -p
    508       The status should equal 1
    509       The output should be blank
    510       The error should include 'Usage:'
    511       The error should include ' init '
    512       The result of function git_log should be successful
    513       The contents of file "${GITLOG}" should equal "$(setup_log)"
    514     End
    515 
    516     It 'displays usage when called with unknown flag'
    517       Skip if 'pass(age) needs bash' check_skip $2
    518       Skip if 'passage has no init' [ "$2" = passage ]
    519       When run script $1 init -x new-id-1 new-id-2
    520       The status should equal 1
    521       The output should be blank
    522       The error should include 'Usage:'
    523       The error should include ' init '
    524       The result of function git_log should be successful
    525       The contents of file "${GITLOG}" should equal "$(setup_log)"
    526     End
    527 
    528     It 'rejects a path containing ..'
    529       Skip if 'pass(age) needs bash' check_skip $2
    530       Skip if 'passage has no init' [ "$2" = passage ]
    531       When run script $1 init --path fluff/../newdir new-id
    532       The status should equal 1
    533       The output should be blank
    534       The error should include 'sneaky'
    535       The directory "${PREFIX}/newdir" should not be exist
    536       The result of function git_log should be successful
    537       The contents of file "${GITLOG}" should equal "$(setup_log)"
    538     End
    539   End
    540 
    541   Describe 'ls'
    542     It 'lists a directory'
    543       Skip if 'pass(age) needs bash' check_skip $2
    544       When run script $1 ls subdir
    545       The status should be success
    546       The line 1 of output should include 'subdir'
    547       The line 2 of output should include 'file'
    548     End
    549 
    550     It 'lists a directory implicitly'
    551       Skip if 'pass(age) needs bash' check_skip $2
    552       When run script $1 subdir
    553       The status should be success
    554       The line 1 of output should include 'subdir'
    555       The line 2 of output should include 'file'
    556     End
    557 
    558     It 'lists a directory when called as `show`'
    559       Skip if 'pass(age) needs bash' check_skip $2
    560       When run script $1 show subdir
    561       The status should be success
    562       The line 1 of output should include 'subdir'
    563       The line 2 of output should include 'file'
    564     End
    565 
    566     It 'lists the whole store without argument'
    567       Skip if 'pass(age) needs bash' check_skip $2
    568       When run script $1
    569       The status should be success
    570       if [ $2 = passage ]; then
    571         The line 1 of output should equal 'Passage'
    572       else
    573         The line 1 of output should equal 'Password Store'
    574       fi
    575       The line  2 of output should include '-g'
    576       The line  3 of output should include '-g'
    577       The line  4 of output should include 'extra'
    578       The line  5 of output should include 'subdir'
    579       The line  6 of output should include 'file'
    580       The line  7 of output should include 'file'
    581       The line  8 of output should include 'extra'
    582       The line  9 of output should include 'extra'
    583       The line 10 of output should include 'fluff'
    584       The line 11 of output should include 'one'
    585       The line 12 of output should include 'one'
    586       The line 13 of output should include 'three'
    587       The line 14 of output should include 'three'
    588       The line 15 of output should include 'two'
    589       The line 16 of output should include 'two'
    590       The line 17 of output should include 'shared'
    591       The line 18 of output should include 'stale'
    592       The line 19 of output should include 'stale'
    593       The line 20 of output should include 'subdir'
    594       The line 21 of output should include 'file'
    595       The line 22 of output should include 'file'
    596       if [ $2 = pashage ]; then
    597         The lines of output should equal 22
    598       else
    599         The line 23 of output should include 'y.txt'
    600       fi
    601     End
    602 
    603     It 'does not list a file masquerading as a directory'
    604       Skip if 'pass(age) needs bash' check_skip $2
    605       When run script $1 subdir/file/
    606       The status should equal 1
    607       The error should equal 'Error: subdir/file/ is not in the password store.'
    608     End
    609 
    610     It 'lists a directory having an ambiguous name with `/` suffix'
    611       Skip if 'pass(age) needs bash' check_skip $2
    612       When run script $1 extra/
    613       The status should be success
    614       The line 1 of output should include 'extra'
    615       The line 2 of output should include 'subdir'
    616       The line 3 of output should include 'file'
    617       The line 4 of output should include 'file'
    618     End
    619 
    620     It 'fails to list a non-existent directory'
    621       Skip if 'pass(age) needs bash' check_skip $2
    622       When run script $1 non-existent/
    623       The status should equal 1
    624       The output should be blank
    625       The error should equal \
    626         'Error: non-existent/ is not in the password store.'
    627     End
    628 
    629     It 'fails to list a file masquerading as a directory'
    630       Skip if 'pass(age) needs bash' check_skip $2
    631       When run script $1 stale/
    632       The status should equal 1
    633       The output should be blank
    634       The error should equal 'Error: stale/ is not in the password store.'
    635     End
    636 
    637     It 'rejects a path containing ..'
    638       Skip if 'pass(age) needs bash' check_skip $2
    639       When run script $1 subdir/../fluff/
    640       The status should equal 1
    641       The output should be blank
    642       The error should include 'sneaky'
    643       The result of function git_log should be successful
    644       The contents of file "${GITLOG}" should equal "$(setup_log)"
    645     End
    646   End
    647 
    648   Describe 'find'
    649     It 'lists entries matching a substring'
    650       Skip if 'pass(age) needs bash' check_skip $2
    651       When run script $1 find o
    652       The status should be success
    653       The lines of output should equal 6
    654       The line 1 of output should match pattern 'Search *: o'
    655       The line 2 of output should include 'fluff'
    656       The line 3 of output should include 'one'
    657       The line 4 of output should include 'one'
    658       The line 5 of output should include 'two'
    659       The line 6 of output should include 'two'
    660     End
    661 
    662     It 'reports success even without match'
    663       Skip if 'pass(age) needs bash' check_skip $2
    664       When run script $1 find z
    665       The status should be success
    666       The lines of output should equal 1
    667       The line 1 of output should match pattern 'Search *: z'
    668       The error should be blank
    669     End
    670 
    671     It 'displays usage when called without argument'
    672       Skip if 'pass(age) needs bash' check_skip $2
    673       When run script $1 find
    674       The status should equal 1
    675       The output should be blank
    676       The error should include 'Usage:'
    677       The error should include ' find '
    678       The result of function git_log should be successful
    679       The contents of file "${GITLOG}" should equal "$(setup_log)"
    680     End
    681   End
    682 
    683   Describe 'show'
    684     It 'decrypts a password file'
    685       Skip if 'pass(age) needs bash' check_skip $2
    686       When run script $1 show subdir/file
    687       The status should be success
    688       The output should equal 'p4ssw0rd'
    689     End
    690 
    691     It 'decrypts a password file implicitly'
    692       Skip if 'pass(age) needs bash' check_skip $2
    693       When run script $1 subdir/file
    694       The status should be success
    695       The output should equal 'p4ssw0rd'
    696     End
    697 
    698     It 'fails to decrypt a flag'
    699       Skip if 'pass(age) needs bash' check_skip $2
    700       When run script $1 -g
    701       The status should equal 1
    702       The output should be blank
    703       The error should include 'Usage:'
    704     End
    705 
    706     It 'decrypts a password file named like a flag'
    707       Skip if 'pass(age) needs bash' check_skip $2
    708       When run script $1 -- -g
    709       The status should be success
    710       The output should equal '--'
    711     End
    712 
    713     It 'decrypts a password file even when called as `list`'
    714       Skip if 'pass(age) needs bash' check_skip $2
    715       When run script $1 ls subdir/file
    716       The status should be success
    717       The output should equal 'p4ssw0rd'
    718     End
    719 
    720     It 'decrypts a file having an ambiguous name without suffix'
    721       Skip if 'pass(age) needs bash' check_skip $2
    722       When run script $1 extra
    723       The status should be success
    724       The output should equal 'ambiguous'
    725     End
    726 
    727     It 'displays the password as a QR-code'
    728       export DISPLAY=mock
    729       Skip if 'pass(age) needs bash' check_skip $2
    730       When run script $1 -q fluff/one
    731       The status should be success
    732       expected_err() { %text:expand
    733         #|$ feh -x --title ${1}: fluff/one -g +200+200 -
    734         #|0000000 31 2d 70 61 73 73 77 6f 72 64
    735         #|0000012
    736       }
    737       if [ $2 = pashage ]; then
    738         The error should equal "$(expected_err 'pashage')"
    739       else
    740         The error should equal "$(expected_err 'pass')"
    741       fi
    742     End
    743 
    744     It 'displays the given long-option line as a QR-code'
    745       export DISPLAY=mock
    746       Skip if 'pass(age) needs bash' check_skip $2
    747       When run script $1 --qrcode=2 fluff/three
    748       The status should be success
    749       expected_err() { %text:expand
    750         #|$ feh -x --title ${1}: fluff/three -g +200+200 -
    751         #|0000000 55 73 65 72 6e 61 6d 65 3a 20 33 4a 61 6e 65
    752         #|0000017
    753       }
    754       if [ $2 = pashage ]; then
    755         The error should equal "$(expected_err 'pashage')"
    756       else
    757         The error should equal "$(expected_err 'pass')"
    758       fi
    759     End
    760 
    761     It 'displays the given short-option line as a QR-code'
    762       export DISPLAY=mock
    763       Skip if 'pass(age) needs bash' check_skip $2
    764       When run script $1 -q2 fluff/three
    765       The status should be success
    766       expected_err() { %text:expand
    767         #|$ feh -x --title ${1}: fluff/three -g +200+200 -
    768         #|0000000 55 73 65 72 6e 61 6d 65 3a 20 33 4a 61 6e 65
    769         #|0000017
    770       }
    771       if [ $2 = pashage ]; then
    772         The error should equal "$(expected_err 'pashage')"
    773       else
    774         The error should equal "$(expected_err 'pass')"
    775       fi
    776     End
    777 
    778     It 'pastes into the clipboard'
    779       export DISPLAY=mock
    780       Skip if 'pass(age) needs bash' check_skip $2
    781       When run script $1 show -c fluff/three
    782       The status should be success
    783       The output should start with \
    784         'Copied fluff/three to clipboard. Will clear in 45 seconds.'
    785       expected_err() { %text
    786         #|$ xclip -selection clipboard
    787         #|0000000 33 2d 70 61 73 73 77 6f 72 64
    788         #|0000012
    789       }
    790       The error should start with "$(expected_err)"
    791     End
    792 
    793     It 'pastes a selected long-option line into the clipboard'
    794       export DISPLAY=mock
    795       Skip if 'pass(age) needs bash' check_skip $2
    796       When run script $1 show --clip=2 fluff/three
    797       The status should be success
    798       The output should start with \
    799         'Copied fluff/three to clipboard. Will clear in 45 seconds.'
    800       expected_err() { %text
    801         #|$ xclip -selection clipboard
    802         #|0000000 55 73 65 72 6e 61 6d 65 3a 20 33 4a 61 6e 65
    803         #|0000017
    804       }
    805       The error should start with "$(expected_err)"
    806     End
    807 
    808     It 'pastes a selected short-option line into the clipboard'
    809       export DISPLAY=mock
    810       Skip if 'pass(age) needs bash' check_skip $2
    811       When run script $1 show -c2 fluff/three
    812       The status should be success
    813       The output should start with \
    814         'Copied fluff/three to clipboard. Will clear in 45 seconds.'
    815       expected_err() { %text
    816         #|$ xclip -selection clipboard
    817         #|0000000 55 73 65 72 6e 61 6d 65 3a 20 33 4a 61 6e 65
    818         #|0000017
    819       }
    820       The error should start with "$(expected_err)"
    821     End
    822 
    823     It 'fails to show a non-existent file'
    824       Skip if 'pass(age) needs bash' check_skip $2
    825       When run script $1 non-existent
    826       The status should equal 1
    827       The output should be blank
    828       The error should equal \
    829         'Error: non-existent is not in the password store.'
    830     End
    831 
    832     It 'does not show an unencrypted file in the store'
    833       Skip if 'pass(age) needs bash' check_skip $2
    834       When run script $1 show y.txt
    835       The status should equal 1
    836       The output should be blank
    837       The error should equal 'Error: y.txt is not in the password store.'
    838     End
    839 
    840     It 'displays usage when called with incompatible arguments'
    841       Skip if 'pass(age) needs bash' check_skip $2
    842       When run script $1 show -c -q subdir/file
    843       The status should equal 1
    844       The output should be blank
    845       The error should include 'Usage:'
    846       The error should include 'show'
    847       The result of function git_log should be successful
    848       The contents of file "${GITLOG}" should equal "$(setup_log)"
    849     End
    850 
    851     It 'rejects a path containing ..'
    852       Skip if 'pass(age) needs bash' check_skip $2
    853       When run script $1 subdir/../fluff/one
    854       The status should equal 1
    855       The output should be blank
    856       The error should include 'sneaky'
    857       The result of function git_log should be successful
    858       The contents of file "${GITLOG}" should equal "$(setup_log)"
    859     End
    860   End
    861 
    862   Describe 'grep'
    863     It 'shows decrypted lines matching a regex'
    864       Skip if 'pass(age) needs bash' check_skip $2
    865       When run script $1 grep -i Com
    866       The status should be success
    867       The lines of output should equal 4
    868       The line 1 of output should include 'fluff'
    869       The output should include 'three'
    870       The line 2 of output should include 'https://example.'
    871       The line 3 of output should include 'fluff'
    872       The output should include 'two'
    873       The line 4 of output should include 'https://example.'
    874     End
    875 
    876     It 'is successful even without match'
    877       Skip if 'pass(age) needs bash' check_skip $2
    878       When run script $1 grep nothing.matches
    879       The status should be success
    880       The output should be blank
    881     End
    882 
    883     It 'does not look into unencrypted files in the store'
    884       Skip if 'pass(age) needs bash' check_skip $2
    885       When run script $1 grep Unencrypted
    886       The status should be success
    887       The output should be blank
    888     End
    889 
    890     It 'displays usage when called without argument'
    891       Skip if 'pass(age) needs bash' check_skip $2
    892       When run script $1 grep
    893       The status should equal 1
    894       The output should be blank
    895       The error should include 'Usage:'
    896       The error should include ' grep '
    897       The result of function git_log should be successful
    898       The contents of file "${GITLOG}" should equal "$(setup_log)"
    899     End
    900   End
    901 
    902   Describe 'insert'
    903     It 'inserts a new multi-line entry'
    904       Skip if 'pass(age) needs bash' check_skip $2
    905       Data
    906         #|password
    907         #|Username: tester
    908         #|URL: https://example.com/login
    909       End
    910       When run script $1 insert -m rootpass
    911       The status should be success
    912       The output should include 'rootpass'
    913       The contents of file "${PREFIX}/rootpass.$3" \
    914         should include "$3:Username: tester"
    915       expected_log() { %text:expand
    916         #|Add given password for rootpass to store.
    917         #|
    918         #| rootpass.$1 | 4 ++++
    919         #| 1 file changed, 4 insertions(+)
    920         setup_log
    921       }
    922       The result of function git_log should be successful
    923       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
    924     End
    925 
    926     It 'inserts a new single-line entry'
    927       Skip if 'pass(age) needs bash' check_skip $2
    928       Mock stty
    929         true
    930       End
    931       Data
    932         #|pass-word
    933         #|pass-word
    934       End
    935       When run script $1 insert newdir/newpass
    936       The status should be success
    937       The output should include 'newdir/newpass'
    938       The contents of file "${PREFIX}/newdir/newpass.$3" \
    939         should include "$3:pass-word"
    940       expected_log() { %text:expand
    941         #|Add given password for newdir/newpass to store.
    942         #|
    943         #| newdir/newpass.$1 | 2 ++
    944         #| 1 file changed, 2 insertions(+)
    945         setup_log
    946       }
    947       The result of function git_log should be successful
    948       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
    949     End
    950 
    951     It 'fails to insert an unescaped flag'
    952       Skip if 'pass(age) needs bash' check_skip $2
    953       When run script $1 insert -h
    954       The status should equal 1
    955       The output should be blank
    956       The error should include 'Usage:'
    957       The result of function git_log should be successful
    958       The contents of file "${GITLOG}" should equal "$(setup_log)"
    959     End
    960 
    961     It 'inserts a new single-line entry named like a flag'
    962       Skip if 'pass(age) needs bash' check_skip $2
    963       Data
    964         #|pass-word
    965         #|pass-word
    966       End
    967       When run script $1 insert -- -h
    968       The status should be success
    969       The output should include '-h'
    970       The contents of file "${PREFIX}/-h.$3" \
    971         should include "$3:pass-word"
    972       expected_log() { %text:expand
    973         #|Add given password for -h to store.
    974         #|
    975         #| -h.$1 | 2 ++
    976         #| 1 file changed, 2 insertions(+)
    977         setup_log
    978       }
    979       The result of function git_log should be successful
    980       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
    981     End
    982 
    983     It 'inserts a new single-line entry with echo'
    984       Skip if 'pass(age) needs bash' check_skip $2
    985       Data "pass-word"
    986       When run script $1 insert -e newdir/newpass
    987       The status should be success
    988       The output should include 'newdir/newpass'
    989       The output should not include 'Retype'
    990       The contents of file "${PREFIX}/newdir/newpass.$3" \
    991         should include "$3:pass-word"
    992       expected_log() { %text:expand
    993         #|Add given password for newdir/newpass to store.
    994         #|
    995         #| newdir/newpass.$1 | 2 ++
    996         #| 1 file changed, 2 insertions(+)
    997         setup_log
    998       }
    999       The result of function git_log should be successful
   1000       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1001     End
   1002 
   1003     It 'inserts an entry with local recipient list'
   1004       Skip if 'pass(age) needs bash' check_skip $2
   1005       Data "passWord"
   1006       When run script $1 insert -e shared/newpass
   1007       The status should be success
   1008       The output should include 'shared/newpass'
   1009       The contents of file "${PREFIX}/shared/newpass.$3" \
   1010         should include 'friend'
   1011       The contents of file "${PREFIX}/shared/newpass.$3" \
   1012         should include 'myself'
   1013       The contents of file "${PREFIX}/shared/newpass.$3" \
   1014         should include "$3:passWord"
   1015       expected_log() { %text:expand
   1016         #|Add given password for shared/newpass to store.
   1017         #|
   1018         #| shared/newpass.$1 | 3 +++
   1019         #| 1 file changed, 3 insertions(+)
   1020         setup_log
   1021       }
   1022       The result of function git_log should be successful
   1023       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1024     End
   1025 
   1026     It 'inserts forcefully over an existing single-line entry with echo'
   1027       Skip if 'pass(age) needs bash' check_skip $2
   1028       Data "pass-word"
   1029       When run script $1 insert -e -f subdir/file
   1030       The status should be success
   1031       The output should include 'subdir/file'
   1032       The output should not include 'Retype'
   1033       The contents of file "${PREFIX}/subdir/file.$3" \
   1034         should include "$3:pass-word"
   1035       expected_log() { %text:expand
   1036         #|Add given password for subdir/file to store.
   1037         #|
   1038         #| subdir/file.$1 | 2 +-
   1039         #| 1 file changed, 1 insertion(+), 1 deletion(-)
   1040         setup_log
   1041       }
   1042       The result of function git_log should be successful
   1043       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1044     End
   1045 
   1046     It 'inserts an entry into a new directory'
   1047       Skip if 'pass(age) needs bash' check_skip $2
   1048       Data "drowssap"
   1049       When run script $1 insert -e new-dir/newpass
   1050       The status should be success
   1051       The output should include 'new-dir/newpass'
   1052       The contents of file "${PREFIX}/new-dir/newpass.$3" \
   1053         should include 'myself'
   1054       The contents of file "${PREFIX}/new-dir/newpass.$3" \
   1055         should include "$3:drowssap"
   1056       expected_log() { %text:expand
   1057         #|Add given password for new-dir/newpass to store.
   1058         #|
   1059         #| new-dir/newpass.$1 | 2 ++
   1060         #| 1 file changed, 2 insertions(+)
   1061         setup_log
   1062       }
   1063       The result of function git_log should be successful
   1064       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1065     End
   1066 
   1067     It 'inserts an entry encrypted using an explicit recipient file'
   1068       Skip if 'pass(age) needs bash' check_skip $2
   1069       Skip if 'this is an age recipient test' [ -n "${3#age}" ]
   1070       export PASSAGE_RECIPIENTS_FILE="${PREFIX}/fluff/.age-recipients"
   1071       export PASSAGE_RECIPIENTS='shadowed'
   1072       Data 'pass'
   1073       When run script $1 insert -e shared/new-file
   1074       The status should be success
   1075       The output should include 'shared/new-file'
   1076       expected_file() { %text:expand
   1077         #|$1Recipient:master
   1078         #|$1Recipient:myself
   1079         #|$1:pass
   1080       }
   1081       The contents of file "${PREFIX}/shared/new-file.$3" should \
   1082         equal "$(expected_file $3)"
   1083       expected_log() { %text:expand
   1084         #|Add given password for shared/new-file to store.
   1085         #|
   1086         #| shared/new-file.$1 | 3 +++
   1087         #| 1 file changed, 3 insertions(+)
   1088         setup_log
   1089       }
   1090       The result of function git_log should be successful
   1091       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1092     End
   1093 
   1094     It 'inserts an entry encrypted using explicit recipients'
   1095       Skip if 'pass(age) needs bash' check_skip $2
   1096       Skip if 'this is an age recipient test' [ -n "${3#age}" ]
   1097       export PASSAGE_RECIPIENTS='force-1 force-2'
   1098       Data 'pass'
   1099       When run script $1 insert -e shared/new-file
   1100       The status should be success
   1101       The output should include 'shared/new-file'
   1102       expected_file() { %text:expand
   1103         #|$1Recipient:force-1
   1104         #|$1Recipient:force-2
   1105         #|$1:pass
   1106       }
   1107       The contents of file "${PREFIX}/shared/new-file.$3" should \
   1108         equal "$(expected_file $3)"
   1109       expected_log() { %text:expand
   1110         #|Add given password for shared/new-file to store.
   1111         #|
   1112         #| shared/new-file.$1 | 3 +++
   1113         #| 1 file changed, 3 insertions(+)
   1114         setup_log
   1115       }
   1116       The result of function git_log should be successful
   1117       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1118     End
   1119 
   1120     It 'displays usage when called without argument'
   1121       Skip if 'pass(age) needs bash' check_skip $2
   1122       When run script $1 insert
   1123       The status should equal 1
   1124       The output should be blank
   1125       The error should include 'Usage:'
   1126       The error should include ' insert '
   1127       The result of function git_log should be successful
   1128       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1129     End
   1130 
   1131     It 'displays usage when called with incompatible arguments'
   1132       Skip if 'pass(age) needs bash' check_skip $2
   1133       When run script $1 insert -em new-secret
   1134       The status should equal 1
   1135       The output should be blank
   1136       The error should include 'Usage:'
   1137       The error should include ' insert '
   1138       The result of function git_log should be successful
   1139       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1140     End
   1141 
   1142     It 'rejects a path containing ..'
   1143       Skip if 'pass(age) needs bash' check_skip $2
   1144       When run script $1 insert -e fluff/../new-secret
   1145       The status should equal 1
   1146       The output should be blank
   1147       The error should include 'sneaky'
   1148       The file "${PREFIX}/new-secret.age" should not be exist
   1149       The file "${PREFIX}/new-secret.gpg" should not be exist
   1150       The result of function git_log should be successful
   1151       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1152     End
   1153   End
   1154 
   1155   Describe 'edit'
   1156     EDITOR=ed
   1157     TERM=dumb
   1158 
   1159     It 'creates a file using EDITOR'
   1160       EDITOR='ed -c'
   1161       Skip if 'pass(age) needs bash' check_skip $2
   1162       Data
   1163         #|a
   1164         #|New password
   1165         #|New annotation
   1166         #|.
   1167         #|wq
   1168       End
   1169       When run script $1 edit subdir/new
   1170       The status should be success
   1171       The file "${PREFIX}/subdir/new.$3" should be exist
   1172       expected_file() { %text:expand
   1173         #|$1Recipient:myself
   1174         #|$1:New password
   1175         #|$1:New annotation
   1176       }
   1177       The contents of file "${PREFIX}/subdir/new.$3" should \
   1178         equal "$(expected_file "$3")"
   1179       expected_log() { %text:expand
   1180         #|Add password for subdir/new using ed -c.
   1181         #|
   1182         #| subdir/new.$1 | 3 +++
   1183         #| 1 file changed, 3 insertions(+)
   1184         setup_log
   1185       }
   1186       The result of function git_log should be successful
   1187       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1188     End
   1189 
   1190     It 'creates a file in a new directory'
   1191       EDITOR='ed -c'
   1192       Skip if 'pass(age) needs bash' check_skip $2
   1193       Data
   1194         #|a
   1195         #|p4ssword!
   1196         #|.
   1197         #|wq
   1198       End
   1199       When run script $1 edit new-subdir/new
   1200       The status should be success
   1201       The directory "${PREFIX}/new-subdir" should be exist
   1202       The file "${PREFIX}/new-subdir/new.$3" should be exist
   1203       expected_file() { %text:expand
   1204         #|$1Recipient:myself
   1205         #|$1:p4ssword!
   1206       }
   1207       The contents of file "${PREFIX}/new-subdir/new.$3" should \
   1208         equal "$(expected_file "$3")"
   1209       expected_log() { %text:expand
   1210         #|Add password for new-subdir/new using ed -c.
   1211         #|
   1212         #| new-subdir/new.$1 | 2 ++
   1213         #| 1 file changed, 2 insertions(+)
   1214         setup_log
   1215       }
   1216       The result of function git_log should be successful
   1217       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1218     End
   1219 
   1220     It 'creates a file named like a flag'
   1221       EDITOR='ed -c'
   1222       Skip if 'pass(age) needs bash' check_skip $2
   1223       Data
   1224         #|a
   1225         #|New password
   1226         #|.
   1227         #|wq
   1228       End
   1229       When run script $1 edit -h
   1230       The status should be success
   1231       The file "${PREFIX}/-h.$3" should be exist
   1232       expected_file() { %text:expand
   1233         #|$1Recipient:myself
   1234         #|$1:New password
   1235       }
   1236       The contents of file "${PREFIX}/-h.$3" should \
   1237         equal "$(expected_file "$3")"
   1238       expected_log() { %text:expand
   1239         #|Add password for -h using ed -c.
   1240         #|
   1241         #| -h.$1 | 2 ++
   1242         #| 1 file changed, 2 insertions(+)
   1243         setup_log
   1244       }
   1245       The result of function git_log should be successful
   1246       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1247     End
   1248 
   1249     It 'creates a file named like a directory'
   1250       EDITOR='ed -c'
   1251       Skip if 'pass(age) needs bash' check_skip $2
   1252       Data
   1253         #|a
   1254         #|New ambiguous password
   1255         #|.
   1256         #|wq
   1257       End
   1258       When run script $1 edit fluff
   1259       The status should be success
   1260       The file "${PREFIX}/fluff.$3" should be exist
   1261       expected_file() { %text:expand
   1262         #|$1Recipient:myself
   1263         #|$1:New ambiguous password
   1264       }
   1265       The contents of file "${PREFIX}/fluff.$3" should \
   1266         equal "$(expected_file "$3")"
   1267       expected_log() { %text:expand
   1268         #|Add password for fluff using ed -c.
   1269         #|
   1270         #| fluff.$1 | 2 ++
   1271         #| 1 file changed, 2 insertions(+)
   1272         setup_log
   1273       }
   1274       The result of function git_log should be successful
   1275       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1276     End
   1277 
   1278     It 'creates a secret file named like an unencrypted file'
   1279       EDITOR='ed -c'
   1280       Skip if 'pass(age) needs bash' check_skip $2
   1281       Data
   1282         #|a
   1283         #|New password in a new file
   1284         #|.
   1285         #|wq
   1286       End
   1287       When run script $1 edit y.txt
   1288       The status should be success
   1289       The file "${PREFIX}/y.txt.$3" should be exist
   1290       expected_file() { %text:expand
   1291         #|$1Recipient:myself
   1292         #|$1:New password in a new file
   1293       }
   1294       The contents of file "${PREFIX}/y.txt.$3" should \
   1295         equal "$(expected_file "$3")"
   1296       expected_log() { %text:expand
   1297         #|Add password for y.txt using ed -c.
   1298         #|
   1299         #| y.txt.$1 | 2 ++
   1300         #| 1 file changed, 2 insertions(+)
   1301         setup_log
   1302       }
   1303       The result of function git_log should be successful
   1304       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1305     End
   1306 
   1307     It 'updates a file using EDITOR'
   1308       Skip if 'pass(age) needs bash' check_skip $2
   1309       Data
   1310         #|2i
   1311         #|New line
   1312         #|.
   1313         #|wq
   1314       End
   1315       When run script $1 edit fluff/two
   1316       The status should be success
   1317       expected_file() { %text:expand
   1318         #|$1Recipient:master
   1319         #|$1Recipient:myself
   1320         #|$1:2-password
   1321         #|$1:New line
   1322         #|$1:URL: https://example.com/login
   1323       }
   1324       The contents of file "${PREFIX}/fluff/two.$3" should \
   1325         equal "$(expected_file "$3")"
   1326       expected_log() { %text:expand
   1327         #|Edit password for fluff/two using ed.
   1328         #|
   1329         #| fluff/two.$1 | 1 +
   1330         #| 1 file changed, 1 insertion(+)
   1331         setup_log
   1332       }
   1333       The result of function git_log should be successful
   1334       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1335     End
   1336 
   1337     It 'updates a file having an ambiguous name without suffix'
   1338       Skip if 'pass(age) needs bash' check_skip $2
   1339       Data
   1340         #|1i
   1341         #|New line
   1342         #|.
   1343         #|wq
   1344       End
   1345       When run script $1 edit extra
   1346       The status should be success
   1347       expected_file() { %text:expand
   1348         #|$1Recipient:myself
   1349         #|$1:New line
   1350         #|$1:ambiguous
   1351       }
   1352       The contents of file "${PREFIX}/extra.$3" should \
   1353         equal "$(expected_file "$3")"
   1354       expected_log() { %text:expand
   1355         #|Edit password for extra using ed.
   1356         #|
   1357         #| extra.$1 | 1 +
   1358         #| 1 file changed, 1 insertion(+)
   1359         setup_log
   1360       }
   1361       The result of function git_log should be successful
   1362       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1363     End
   1364 
   1365     It 'reencrypts an updated file using EDITOR'
   1366       Skip if 'pass(age) needs bash' check_skip $2
   1367       Data
   1368         #|a
   1369         #|New option
   1370         #|.
   1371         #|wq
   1372       End
   1373       When run script $1 edit stale
   1374       The status should be success
   1375       expected_file() { %text:expand
   1376         #|$1Recipient:myself
   1377         #|$1:0-password
   1378         #|$1:New option
   1379       }
   1380       The contents of file "${PREFIX}/stale.$3" should \
   1381         equal "$(expected_file "$3")"
   1382       expected_log() { %text:expand
   1383         #|Edit password for stale using ed.
   1384         #|
   1385         #| stale.$1 | 2 +-
   1386         #| 1 file changed, 1 insertion(+), 1 deletion(-)
   1387         setup_log
   1388       }
   1389       The result of function git_log should be successful
   1390       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1391     End
   1392 
   1393     It 'does not reencrypt an unchanged file using EDITOR'
   1394       Skip if 'pass(age) needs bash' check_skip $2
   1395       Data 'q'
   1396       When run script $1 edit stale
   1397       The status should equal \
   1398         "$(if [ $2 = pashage ]; then echo 0; else echo 1; fi)"
   1399       expected_file() { %text:expand
   1400         #|$1Recipient:master
   1401         #|$1Recipient:myself
   1402         #|$1:0-password
   1403       }
   1404       The contents of file "${PREFIX}/stale.$3" should \
   1405         equal "$(expected_file "$3")"
   1406       The result of function git_log should be successful
   1407       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1408     End
   1409 
   1410     It 'allows cancelling file creation'
   1411       Skip if 'pass(age) needs bash' check_skip $2
   1412       Data 'q'
   1413       When run script $1 edit subdir/new
   1414       The status should be successful
   1415       The file "${PREFIX}/subdir/new.age" should not be exist
   1416       The file "${PREFIX}/subdir/new.gpg" should not be exist
   1417       The result of function git_log should be successful
   1418       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1419     End
   1420 
   1421     It 'displays usage when called without argument'
   1422       Skip if 'pass(age) needs bash' check_skip $2
   1423       When run script $1 edit
   1424       The status should equal 1
   1425       The output should be blank
   1426       The error should include 'Usage:'
   1427       The error should include ' edit '
   1428       The result of function git_log should be successful
   1429       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1430     End
   1431 
   1432     It 'rejects a path containing ..'
   1433       Skip if 'pass(age) needs bash' check_skip $2
   1434       EDITOR=true
   1435       When run script $1 edit subdir/../stale
   1436       The status should equal 1
   1437       The output should be blank
   1438       The error should include 'sneaky'
   1439       The result of function git_log should be successful
   1440       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1441     End
   1442   End
   1443 
   1444   Describe 'generate'
   1445     It 'generates a new file'
   1446       Skip if 'pass(age) needs bash' check_skip $2
   1447       When run script $1 generate newdir/newfile
   1448       The status should be success
   1449       The output should include 'The generated password for'
   1450       The file "${PREFIX}/newdir/newfile.$3" should be exist
   1451       The lines of contents of file "${PREFIX}/newdir/newfile.$3" should \
   1452         equal 2
   1453       The line 1 of contents of file "${PREFIX}/newdir/newfile.$3" should \
   1454         equal "$3Recipient:myself"
   1455       The output should \
   1456         include "$(@sed -n "2s/$3://p" "${PREFIX}/newdir/newfile.$3")"
   1457       expected_log() { %text:expand
   1458         #|Add generated password for newdir/newfile.
   1459         #|
   1460         #| newdir/newfile.$1 | 2 ++
   1461         #| 1 file changed, 2 insertions(+)
   1462         setup_log
   1463       }
   1464       The result of function git_log should be successful
   1465       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1466     End
   1467 
   1468     It 'fails to generates a new file named like a flag without escape'
   1469       Skip if 'pass(age) needs bash' check_skip $2
   1470       When run script $1 generate -h
   1471       The status should equal 1
   1472       The output should be blank
   1473       The error should include 'Usage:'
   1474       The result of function git_log should be successful
   1475       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1476     End
   1477 
   1478     It 'generates a new file named like a flag'
   1479       Skip if 'pass(age) needs bash' check_skip $2
   1480       When run script $1 generate -- -h
   1481       The status should be success
   1482       The output should include 'The generated password for'
   1483       The file "${PREFIX}/-h.$3" should be exist
   1484       The lines of contents of file "${PREFIX}/-h.$3" should equal 2
   1485       The line 1 of contents of file "${PREFIX}/-h.$3" should \
   1486         equal "$3Recipient:myself"
   1487       The output should \
   1488         include "$(@sed -n "2s/$3://p" "${PREFIX}/-h.$3")"
   1489       expected_log() { %text:expand
   1490         #|Add generated password for -h.
   1491         #|
   1492         #| -h.$1 | 2 ++
   1493         #| 1 file changed, 2 insertions(+)
   1494         setup_log
   1495       }
   1496       The result of function git_log should be successful
   1497       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1498     End
   1499 
   1500     It 'generates a new file named like a directory'
   1501       Skip if 'pass(age) needs bash' check_skip $2
   1502       When run script $1 generate fluff
   1503       The status should be success
   1504       The output should include 'The generated password for'
   1505       The file "${PREFIX}/fluff.$3" should be exist
   1506       The lines of contents of file "${PREFIX}/fluff.$3" should equal 2
   1507       The line 1 of contents of file "${PREFIX}/fluff.$3" should \
   1508         equal "$3Recipient:myself"
   1509       The output should \
   1510         include "$(@sed -n "2s/$3://p" "${PREFIX}/fluff.$3")"
   1511       expected_log() { %text:expand
   1512         #|Add generated password for fluff.
   1513         #|
   1514         #| fluff.$1 | 2 ++
   1515         #| 1 file changed, 2 insertions(+)
   1516         setup_log
   1517       }
   1518       The result of function git_log should be successful
   1519       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1520     End
   1521 
   1522     It 'generates a new file in a new directory'
   1523       Skip if 'pass(age) needs bash' check_skip $2
   1524       When run script $1 generate brand-new-dir/file
   1525       The status should be success
   1526       The output should include 'The generated password for'
   1527       The file "${PREFIX}/brand-new-dir/file.$3" should be exist
   1528       The lines of contents of file "${PREFIX}/brand-new-dir/file.$3" should \
   1529         equal 2
   1530       The line 1 of contents of file "${PREFIX}/brand-new-dir/file.$3" should \
   1531         equal "$3Recipient:myself"
   1532       The output should \
   1533         include "$(@sed -n "2s/$3://p" "${PREFIX}/brand-new-dir/file.$3")"
   1534       expected_log() { %text:expand
   1535         #|Add generated password for brand-new-dir/file.
   1536         #|
   1537         #| brand-new-dir/file.$1 | 2 ++
   1538         #| 1 file changed, 2 insertions(+)
   1539         setup_log
   1540       }
   1541       The result of function git_log should be successful
   1542       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1543     End
   1544 
   1545     It 'generates a new file without symbols'
   1546       Skip if 'pass(age) needs bash' check_skip $2
   1547       When run script $1 generate -n newfile 4
   1548       The status should be success
   1549       The output should include 'The generated password for'
   1550       The file "${PREFIX}/newfile.$3" should be exist
   1551       The lines of contents of file "${PREFIX}/newfile.$3" should \
   1552         equal 2
   1553       The line 1 of contents of file "${PREFIX}/newfile.$3" should \
   1554         equal "$3Recipient:myself"
   1555       The line 2 of contents of file "${PREFIX}/newfile.$3" should \
   1556         match pattern "$3:[0-9a-zA-z][0-9a-zA-z][0-9a-zA-z][0-9a-zA-z]"
   1557       The output should \
   1558         include "$(@sed -n "2s/$3://p" "${PREFIX}/newfile.$3")"
   1559       expected_log() { %text:expand
   1560         #|Add generated password for newfile.
   1561         #|
   1562         #| newfile.$1 | 2 ++
   1563         #| 1 file changed, 2 insertions(+)
   1564         setup_log
   1565       }
   1566       The result of function git_log should be successful
   1567       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1568     End
   1569 
   1570     It 'replaces an existing file when forced'
   1571       Skip if 'pass(age) needs bash' check_skip $2
   1572       When run script $1 generate -f fluff/three 20
   1573       The status should be success
   1574       The output should include 'The generated password for'
   1575       The lines of contents of file "${PREFIX}/fluff/three.$3" should equal 3
   1576       The output should \
   1577         include "$(@sed -n "3s/$3://p" "${PREFIX}/fluff/three.$3")"
   1578       expected_log() { %text:expand
   1579         #|Add generated password for fluff/three.
   1580         #|
   1581         #| fluff/three.$1 | 4 +---
   1582         #| 1 file changed, 1 insertion(+), 3 deletions(-)
   1583         setup_log
   1584       }
   1585       The result of function git_log should be successful
   1586       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1587     End
   1588 
   1589     It 'replaces the first line of an existing file'
   1590       Skip if 'pass(age) needs bash' check_skip $2
   1591       When run script $1 generate -ni fluff/three 4
   1592       The status should be success
   1593       The output should include 'The generated password for'
   1594       The lines of contents of file "${PREFIX}/fluff/three.$3" should equal 5
   1595       The line 3 of contents of file "${PREFIX}/fluff/three.$3" should \
   1596         match pattern "$3:[0-9a-zA-z][0-9a-zA-z][0-9a-zA-z][0-9a-zA-z]"
   1597       The output should \
   1598         include "$(@sed -n "3s/$3://p" "${PREFIX}/fluff/three.$3")"
   1599       expected_log() { %text:expand
   1600         #|Replace generated password for fluff/three.
   1601         #|
   1602         #| fluff/three.$1 | 2 +-
   1603         #| 1 file changed, 1 insertion(+), 1 deletion(-)
   1604         setup_log
   1605       }
   1606       The result of function git_log should be successful
   1607       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1608     End
   1609 
   1610     It 'replaces the line of an existing one-line file'
   1611       Skip if 'pass(age) needs bash' check_skip $2
   1612       When run script $1 generate -ni fluff/one 4
   1613       The status should be success
   1614       The output should include 'The generated password for'
   1615       The lines of contents of file "${PREFIX}/fluff/one.$3" should equal 3
   1616       The line 3 of contents of file "${PREFIX}/fluff/one.$3" should \
   1617         match pattern "$3:[0-9a-zA-z][0-9a-zA-z][0-9a-zA-z][0-9a-zA-z]"
   1618       The output should \
   1619         include "$(@sed -n "3s/$3://p" "${PREFIX}/fluff/one.$3")"
   1620       expected_log() { %text:expand
   1621         #|Replace generated password for fluff/one.
   1622         #|
   1623         #| fluff/one.$1 | 2 +-
   1624         #| 1 file changed, 1 insertion(+), 1 deletion(-)
   1625         setup_log
   1626       }
   1627       The result of function git_log should be successful
   1628       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1629     End
   1630 
   1631     It 'pastes the generated password into the clipboard'
   1632       export DISPLAY=mock
   1633       Skip if 'pass(age) needs bash' check_skip $2
   1634       When run script $1 generate -nc subdir/new
   1635       The status should be success
   1636       The output should not include 'The generated password for'
   1637       The output should not \
   1638         include "$(@sed -n "2s/$3://p" "${PREFIX}/subdir/new.$3")"
   1639       The output should include \
   1640         'Copied subdir/new to clipboard. Will clear in 45 seconds.'
   1641       The error should start with '$ xclip -selection clipboard'
   1642       expected_log() { %text:expand
   1643         #|Add generated password for subdir/new.
   1644         #|
   1645         #| subdir/new.$1 | 2 ++
   1646         #| 1 file changed, 2 insertions(+)
   1647         setup_log
   1648       }
   1649       The result of function git_log should be successful
   1650       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1651     End
   1652 
   1653     It 'displays the generated password as a QR-code'
   1654       export DISPLAY=mock
   1655       Skip if 'pass(age) needs bash' check_skip $2
   1656       When run script $1 generate -qn new
   1657       The status should be success
   1658       The output should not include 'The generated password for'
   1659       The output should not include "$(@sed -n "2s/$3://p" "${PREFIX}/new.$3")"
   1660       The error should start with '$ feh -x --title pas'
   1661       expected_log() { %text:expand
   1662         #|Add generated password for new.
   1663         #|
   1664         #| new.$1 | 2 ++
   1665         #| 1 file changed, 2 insertions(+)
   1666         setup_log
   1667       }
   1668       The result of function git_log should be successful
   1669       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1670     End
   1671 
   1672     It 'reports a generation error'
   1673       Skip if 'pass(age) needs bash' check_skip $2
   1674       Mock tr
   1675         if [ "$1" = '-dc' ]; then
   1676           %putsn '0123456789'
   1677         else
   1678           @tr "$@"
   1679         fi
   1680       End
   1681       expected_err() {
   1682         if [ "$1" = pashage ]; then
   1683           %putsn 'Error while generating password: 10/12 bytes read'
   1684         else
   1685           %putsn 'Could not generate password from /dev/urandom.'
   1686         fi
   1687       }
   1688       When run script $1 generate -f new-secret 12
   1689       The status should equal 1
   1690       The output should be blank
   1691       The error should equal "$(expected_err $2)"
   1692       The result of function git_log should be successful
   1693       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1694     End
   1695 
   1696     It 'displays usage when called without argument'
   1697       Skip if 'pass(age) needs bash' check_skip $2
   1698       When run script $1 generate
   1699       The status should equal 1
   1700       The output should be blank
   1701       The error should include 'Usage:'
   1702       The error should include ' generate '
   1703       The result of function git_log should be successful
   1704       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1705     End
   1706 
   1707     It 'displays usage when called with incompatible display options'
   1708       Skip if 'pass(age) needs bash' check_skip $2
   1709       When run script $1 generate --clip --qrcode new-secret
   1710       The status should equal 1
   1711       The output should be blank
   1712       The error should include 'Usage:'
   1713       The error should include ' generate '
   1714       The result of function git_log should be successful
   1715       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1716     End
   1717 
   1718     It 'displays usage when called with reversed incompatible display options'
   1719       Skip if 'pass(age) needs bash' check_skip $2
   1720       When run script $1 generate --qrcode --clip new-secret
   1721       The status should equal 1
   1722       The output should be blank
   1723       The error should include 'Usage:'
   1724       The error should include ' generate '
   1725       The result of function git_log should be successful
   1726       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1727     End
   1728 
   1729     It 'displays usage when called with incompatible overwriting options'
   1730       Skip if 'pass(age) needs bash' check_skip $2
   1731       When run script $1 generate --force --in-place new-secret
   1732       The status should equal 1
   1733       The output should be blank
   1734       The error should include 'Usage:'
   1735       The error should include ' generate '
   1736       The result of function git_log should be successful
   1737       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1738     End
   1739 
   1740     It 'displays usage when called with reversed incompatible overwriting opt'
   1741       Skip if 'pass(age) needs bash' check_skip $2
   1742       When run script $1 generate --in-place --force new-secret
   1743       The status should equal 1
   1744       The output should be blank
   1745       The error should include 'Usage:'
   1746       The error should include ' generate '
   1747       The result of function git_log should be successful
   1748       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1749     End
   1750 
   1751     It 'rejects a path containing ..'
   1752       Skip if 'pass(age) needs bash' check_skip $2
   1753       When run script $1 generate subdir/../new-secret
   1754       The status should equal 1
   1755       The output should be blank
   1756       The error should include 'sneaky'
   1757       The file "${PREFIX}/new-secret.age" should not be exist
   1758       The file "${PREFIX}/new-secret.gpg" should not be exist
   1759       The result of function git_log should be successful
   1760       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1761     End
   1762   End
   1763 
   1764   Describe 'rm'
   1765     It 'removes a file without confirmation when forced'
   1766       Skip if 'pass(age) needs bash' check_skip $2
   1767       When run script $1 rm -f subdir/file
   1768       The status should be success
   1769       The output should include 'subdir/file'
   1770       The error should be blank
   1771       The file "${PREFIX}/subdir/file.$3" should not be exist
   1772       expected_log() { %text:expand
   1773         #|Remove subdir/file from store.
   1774         #|
   1775         #| subdir/file.$1 | 2 --
   1776         #| 1 file changed, 2 deletions(-)
   1777         setup_log
   1778       }
   1779       The result of function git_log should be successful
   1780       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1781     End
   1782 
   1783     It 'fails to remove a file named like a flag without escape'
   1784       Skip if 'pass(age) needs bash' check_skip $2
   1785       When run script $1 rm -f -g
   1786       The status should equal 1
   1787       The output should be blank
   1788       The error should include 'Usage:'
   1789       The result of function git_log should be successful
   1790       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1791     End
   1792 
   1793     It 'removes a file named like a flag'
   1794       Skip if 'pass(age) needs bash' check_skip $2
   1795       When run script $1 rm -f -- -g
   1796       The status should be success
   1797       The output should include '-g'
   1798       The error should be blank
   1799       The file "${PREFIX}/-g.$3" should not be exist
   1800       expected_log() { %text:expand
   1801         #|Remove -g from store.
   1802         #|
   1803         #| -g.$1 | 2 --
   1804         #| 1 file changed, 2 deletions(-)
   1805         setup_log
   1806       }
   1807       The result of function git_log should be successful
   1808       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1809     End
   1810 
   1811     It 'removes a file having an ambiguous name without suffix'
   1812       Skip if 'pass(age) needs bash' check_skip $2
   1813       When run script $1 rm -f extra
   1814       The status should be success
   1815       The output should include 'extra'
   1816       The error should be blank
   1817       The file "${PREFIX}/extra.$3" should not be exist
   1818       The directory "${PREFIX}/extra" should be exist
   1819       expected_log() { %text:expand
   1820         #|Remove extra from store.
   1821         #|
   1822         #| extra.$1 | 2 --
   1823         #| 1 file changed, 2 deletions(-)
   1824         setup_log
   1825       }
   1826       The result of function git_log should be successful
   1827       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1828     End
   1829 
   1830     It 'fails to remove a non-existent file'
   1831       Skip if 'pass(age) needs bash' check_skip $2
   1832       When run script $1 rm -f non-existent-file
   1833       The status should equal 1
   1834       The error should include 'non-existent-file'
   1835       The result of function git_log should be successful
   1836       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1837     End
   1838 
   1839     It 'does not remove a directory without `-r` even when forced'
   1840       Skip if 'pass(age) needs bash' check_skip $2
   1841       When run script $1 rm -f fluff
   1842       The status should equal \
   1843         "$(if [ $2 = pashage ]; then echo 1; else echo 0; fi)"
   1844       The error should include 'fluff/'
   1845       The error should include 's a directory'
   1846       The directory "${PREFIX}/fluff" should be exist
   1847       The file "${PREFIX}/fluff/one.$3" should be exist
   1848       The result of function git_log should be successful
   1849       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1850     End
   1851 
   1852     It 'removes a directory when forced and recursive'
   1853       Skip if 'pass(age) needs bash' check_skip $2
   1854       When run script $1 rm -rf fluff
   1855       The status should be success
   1856       The directory "${PREFIX}/fluff" should not be exist
   1857       expected_log() {
   1858         if [ $1 = pashage ]; then
   1859           %putsn 'Remove fluff/ from store.'
   1860         else
   1861           %putsn 'Remove fluff from store.'
   1862         fi
   1863         %text:expand
   1864         #|
   1865         #| fluff/.age-recipients | 2 --
   1866         #| fluff/.gpg-id         | 2 --
   1867         #| fluff/one.age         | 3 ---
   1868         #| fluff/one.gpg         | 3 ---
   1869         #| fluff/three.age       | 5 -----
   1870         #| fluff/three.gpg       | 5 -----
   1871         #| fluff/two.age         | 4 ----
   1872         #| fluff/two.gpg         | 4 ----
   1873         #| 8 files changed, 28 deletions(-)
   1874         setup_log
   1875       }
   1876       The result of function git_log should be successful
   1877       The contents of file "${GITLOG}" should equal "$(expected_log $2)"
   1878     End
   1879 
   1880     It 'removes a directory having an ambiguous name with `/` suffix'
   1881       Skip if 'pass(age) needs bash' check_skip $2
   1882       When run script $1 rm -rf extra/
   1883       The status should be success
   1884       The output should include 'extra'
   1885       The error should be blank
   1886       The file "${PREFIX}/extra.age" should be exist
   1887       The file "${PREFIX}/extra.gpg" should be exist
   1888       The directory "${PREFIX}/extra" should not be exist
   1889       expected_log() { %text:expand
   1890         #|Remove extra/ from store.
   1891         #|
   1892         #| extra/subdir/file.age | 2 --
   1893         #| extra/subdir/file.gpg | 2 --
   1894         #| 2 files changed, 4 deletions(-)
   1895         setup_log
   1896       }
   1897       The result of function git_log should be successful
   1898       The contents of file "${GITLOG}" should equal "$(expected_log $3)"
   1899     End
   1900 
   1901     It 'does not remove anything with `/` suffix but no recursive flag'
   1902       Skip if 'pass(age) needs bash' check_skip $2
   1903       When run script $1 rm -f extra/
   1904       The status should equal \
   1905         "$(if [ $2 = pashage ]; then echo 1; else echo 0; fi)"
   1906       The error should include 'extra/'
   1907       The error should include 's a directory'
   1908       The directory "${PREFIX}/extra" should be exist
   1909       The file "${PREFIX}/extra.age" should be exist
   1910       The file "${PREFIX}/extra.gpg" should be exist
   1911       The result of function git_log should be successful
   1912       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1913     End
   1914 
   1915     It 'fails to remove a non-existent directory'
   1916       Skip if 'pass(age) needs bash' check_skip $2
   1917       When run script $1 rm -rf stale/
   1918       The status should equal 1
   1919       The error should include 'stale/'
   1920       The result of function git_log should be successful
   1921       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1922     End
   1923 
   1924     It 'does not remove an unencrypted file in the store'
   1925       Skip if 'pass(age) needs bash' check_skip $2
   1926       When run script $1 rm -f y.txt
   1927       The status should equal 1
   1928       The output should be blank
   1929       The error should include 'y.txt'
   1930       The file "${PREFIX}/y.txt" should be exist
   1931       The result of function git_log should be successful
   1932       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1933     End
   1934 
   1935     It 'does not remove an unencrypted file with `/` suffix'
   1936       Skip if 'pass(age) needs bash' check_skip $2
   1937       When run script $1 rm -rf y.txt/
   1938       The status should equal 1
   1939       The output should be blank
   1940       The error should include 'y.txt'
   1941       The file "${PREFIX}/y.txt" should be exist
   1942       The result of function git_log should be successful
   1943       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1944     End
   1945 
   1946     It 'displays usage when called without argument'
   1947       Skip if 'pass(age) needs bash' check_skip $2
   1948       When run script $1 delete
   1949       The status should equal 1
   1950       The output should be blank
   1951       The error should include 'Usage:'
   1952       The error should include ' delete '
   1953       The result of function git_log should be successful
   1954       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1955     End
   1956 
   1957     It 'rejects a path containing ..'
   1958       Skip if 'pass(age) needs bash' check_skip $2
   1959       When run script $1 delete subdir/../fluff/one
   1960       The status should equal 1
   1961       The output should be blank
   1962       The error should include 'sneaky'
   1963       The file "${PREFIX}/fluff/one.age" should be exist
   1964       The file "${PREFIX}/fluff/one.gpg" should be exist
   1965       The result of function git_log should be successful
   1966       The contents of file "${GITLOG}" should equal "$(setup_log)"
   1967     End
   1968   End
   1969 
   1970   Describe 'mv'
   1971     It 'renames a file without reencrypting'
   1972       Skip if 'pass(age) needs bash' check_skip $2
   1973       When run script $1 mv subdir/file subdir/renamed
   1974       The status should be success
   1975       The error should be blank
   1976       The file "${PREFIX}/subdir/file.$3" should not be exist
   1977       file_contents() { %text:expand
   1978         #|${1}Recipient:myself
   1979         #|${1}:p4ssw0rd
   1980       }
   1981       The contents of file "${PREFIX}/subdir/renamed.$3" \
   1982         should equal "$(file_contents "$3")"
   1983       expected_log() {
   1984         if [ "$2" = pashage ]; then
   1985           %putsn 'Move subdir/file.age to subdir/renamed.age'
   1986         else
   1987           %putsn 'Rename subdir/file to subdir/renamed.'
   1988         fi
   1989         %text:expand
   1990         #|
   1991         #| subdir/{file.$1 => renamed.$1} | 0
   1992         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   1993         setup_log
   1994       }
   1995       The result of function git_log should be successful
   1996       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   1997     End
   1998 
   1999     It 'fails to rename a file named like a flag without escape'
   2000       Skip if 'pass(age) needs bash' check_skip $2
   2001       When run script $1 mv -g safe-name
   2002       The status should equal 1
   2003       The output should be blank
   2004       The error should include 'Usage:'
   2005       The result of function git_log should be successful
   2006       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2007     End
   2008 
   2009     It 'renames a file named like a flag'
   2010       Skip if 'pass(age) needs bash' check_skip $2
   2011       When run script $1 mv -- -g -h
   2012       The status should be success
   2013       The error should be blank
   2014       The file "${PREFIX}/-g.$3" should not be exist
   2015       file_contents() { %text:expand
   2016         #|${1}Recipient:myself
   2017         #|${1}:--
   2018       }
   2019       The contents of file "${PREFIX}/-h.$3" \
   2020         should equal "$(file_contents "$3")"
   2021       expected_log() {
   2022         if [ "$2" = pashage ]; then
   2023           %putsn 'Move -g.age to -h.age'
   2024         else
   2025           %putsn 'Rename -g to -h.'
   2026         fi
   2027         %text:expand
   2028         #|
   2029         #| -g.$1 => -h.$1 | 0
   2030         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   2031         setup_log
   2032       }
   2033       The result of function git_log should be successful
   2034       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2035     End
   2036 
   2037     It 'reencrypts a moved file'
   2038       Skip if 'pass(age) needs bash' check_skip $2
   2039       When run script $1 mv subdir/file shared/renamed
   2040       The status should be success
   2041       The error should be blank
   2042       The file "${PREFIX}/subdir/file.$3" should not be exist
   2043       file_contents() { %text:expand
   2044         #|${1}Recipient:myself
   2045         #|${1}Recipient:friend
   2046         #|${1}:p4ssw0rd
   2047       }
   2048       The contents of file "${PREFIX}/shared/renamed.$3" \
   2049         should equal "$(file_contents "$3")"
   2050       expected_log() {
   2051         if [ "$2" = pashage ]; then
   2052           %putsn 'Move subdir/file.age to shared/renamed.age'
   2053         else
   2054           %putsn 'Rename subdir/file to shared/renamed.'
   2055         fi
   2056         %text:expand
   2057         #|
   2058         #| subdir/file.$1 => shared/renamed.$1 | 1 +
   2059         #| 1 file changed, 1 insertion(+)
   2060         setup_log
   2061       }
   2062       The result of function git_log should be successful
   2063       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2064     End
   2065 
   2066     It 'moves an unencrypted file without reencrypting it'
   2067       Skip if 'pass(age) needs bash' check_skip $2
   2068       When run script $1 mv y.txt shared
   2069       The status should be success
   2070       The error should be blank
   2071       The file "${PREFIX}/y.txt" should not be exist
   2072       file_contents() { %text
   2073         #|Unencrypted line 1
   2074         #|Unencrypted line 2
   2075         #|Unencrypted line 3
   2076       }
   2077       The contents of file "${PREFIX}/shared/y.txt" \
   2078         should equal "$(file_contents)"
   2079       expected_log() {
   2080         if [ "$2" = pashage ]; then
   2081           %putsn 'Move y.txt to shared/y.txt'
   2082         else
   2083           %putsn 'Rename y.txt to shared.'
   2084         fi
   2085         %text
   2086         #|
   2087         #| y.txt => shared/y.txt | 0
   2088         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   2089         setup_log
   2090       }
   2091       The result of function git_log should be successful
   2092       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2093     End
   2094 
   2095     It 'reencrypts relevant files in a moved directory'
   2096       Skip if 'pass(age) needs bash' check_skip $2
   2097       When run script $1 mv subdir shared/
   2098       The status should be success
   2099       The error should be blank
   2100       The file "${PREFIX}/subdir/file.$3" should not be exist
   2101       file_contents() { %text:expand
   2102         #|${1}Recipient:myself
   2103         #|${1}Recipient:friend
   2104         #|${1}:p4ssw0rd
   2105       }
   2106       The contents of file "${PREFIX}/shared/subdir/file.$3" \
   2107         should equal "$(file_contents "$3")"
   2108       expected_log() {
   2109         if [ "$2" = pashage ]; then
   2110           %putsn 'Move subdir/ to shared/subdir/'
   2111         else
   2112           %putsn 'Rename subdir to shared/.'
   2113         fi
   2114         if [ "$1" = age ]; then
   2115           %text:expand
   2116           #|
   2117           #| {subdir => shared/subdir}/file.age | 1 +
   2118           #| {subdir => shared/subdir}/file.gpg | 0
   2119           #| 2 files changed, 1 insertion(+)
   2120         else
   2121           %text:expand
   2122           #|
   2123           #| {subdir => shared/subdir}/file.age | 0
   2124           #| {subdir => shared/subdir}/file.gpg | 1 +
   2125           #| 2 files changed, 1 insertion(+)
   2126         fi
   2127         setup_log
   2128       }
   2129       The result of function git_log should be successful
   2130       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2131     End
   2132 
   2133     It 'moves a file into a new directory'
   2134       Skip if 'pass(age) needs bash' check_skip $2
   2135       When run script $1 mv subdir/file new-subdir/
   2136       The status should be success
   2137       The error should be blank
   2138       The file "${PREFIX}/subdir/file.$3" should not be exist
   2139       file_contents() { %text:expand
   2140         #|${1}Recipient:myself
   2141         #|${1}:p4ssw0rd
   2142       }
   2143       The contents of file "${PREFIX}/new-subdir/file.$3" \
   2144         should equal "$(file_contents "$3")"
   2145       expected_log() {
   2146         if [ "$2" = pashage ]; then
   2147           %putsn 'Move subdir/file.age to new-subdir/file.age'
   2148         else
   2149           %putsn 'Rename subdir/file to new-subdir/.'
   2150         fi
   2151         %text:expand
   2152         #|
   2153         #| {subdir => new-subdir}/file.$1 | 0
   2154         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   2155         setup_log
   2156       }
   2157       The result of function git_log should be successful
   2158       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2159     End
   2160 
   2161     It 'fails to rename a non-existent file'
   2162       Skip if 'pass(age) needs bash' check_skip $2
   2163       When run script $1 mv non-existent-file new-name
   2164       The status should equal 1
   2165       The error should \
   2166         equal 'Error: non-existent-file is not in the password store.'
   2167       The result of function git_log should be successful
   2168       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2169     End
   2170 
   2171     It 'renames a directory with recipients'
   2172       Skip if 'pass(age) needs bash' check_skip $2
   2173       When run script $1 mv fluff filler
   2174       The status should be success
   2175       The error should be blank
   2176       The directory "${PREFIX}/fluff" should not be exist
   2177       The directory "${PREFIX}/filler" should be exist
   2178       expected_log() {
   2179         if [ "$2" = pashage ]; then
   2180           %putsn 'Move fluff/ to filler/'
   2181         else
   2182           %putsn 'Rename fluff to filler.'
   2183         fi
   2184         %text:expand
   2185         #|
   2186         #| {fluff => filler}/.age-recipients | 0
   2187         #| {fluff => filler}/.gpg-id         | 0
   2188         #| {fluff => filler}/one.age         | 0
   2189         #| {fluff => filler}/one.gpg         | 0
   2190         #| {fluff => filler}/three.age       | 0
   2191         #| {fluff => filler}/three.gpg       | 0
   2192         #| {fluff => filler}/two.age         | 0
   2193         #| {fluff => filler}/two.gpg         | 0
   2194         #| 8 files changed, 0 insertions(+), 0 deletions(-)
   2195         setup_log
   2196       }
   2197       The result of function git_log should be successful
   2198       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2199     End
   2200 
   2201     It 'renames a directory without recipients'
   2202       Skip if 'pass(age) needs bash' check_skip $2
   2203       When run script $1 mv subdir newdir
   2204       The status should be success
   2205       The error should be blank
   2206       The directory "${PREFIX}/subdir" should not be exist
   2207       file_contents() { %text:expand
   2208         #|${1}Recipient:myself
   2209         #|${1}:p4ssw0rd
   2210       }
   2211       The contents of file "${PREFIX}/newdir/file.$3" \
   2212         should equal "$(file_contents "$3")"
   2213       expected_log() {
   2214         if [ "$2" = pashage ]; then
   2215           %putsn 'Move subdir/ to newdir/'
   2216         else
   2217           %putsn 'Rename subdir to newdir.'
   2218         fi
   2219         %text:expand
   2220         #|
   2221         #| {subdir => newdir}/file.age | 0
   2222         #| {subdir => newdir}/file.gpg | 0
   2223         #| 2 files changed, 0 insertions(+), 0 deletions(-)
   2224         setup_log
   2225       }
   2226       The result of function git_log should be successful
   2227       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2228     End
   2229 
   2230     It 'overwrites an existing file when forced'
   2231       Skip if 'pass(age) needs bash' check_skip $2
   2232       When run script $1 mv -f fluff/two fluff/one
   2233       The status should be success
   2234       The file "${PREFIX}/fluff/two.$3" should not be exist
   2235       file_contents() { %text:expand
   2236         #|${1}Recipient:master
   2237         #|${1}Recipient:myself
   2238         #|${1}:2-password
   2239         #|${1}:URL: https://example.com/login
   2240       }
   2241       The contents of file "${PREFIX}/fluff/one.$3" \
   2242         should equal "$(file_contents "$3")"
   2243       expected_log() {
   2244         if [ "$2" = pashage ]; then
   2245           %putsn 'Move fluff/two.age to fluff/one.age'
   2246         else
   2247           %putsn 'Rename fluff/two to fluff/one.'
   2248         fi
   2249         %text:expand
   2250         #|
   2251         #| fluff/one.$1 | 3 ++-
   2252         #| fluff/two.$1 | 4 ----
   2253         #| 2 files changed, 2 insertions(+), 5 deletions(-)
   2254         setup_log
   2255       }
   2256       The result of function git_log should be successful
   2257       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2258     End
   2259 
   2260     It 'renames a file having an ambiguous name without suffix'
   2261       Skip if 'pass(age) needs bash' check_skip $2
   2262       When run script $1 mv extra new
   2263       The status should be success
   2264       The file "${PREFIX}/extra.$3" should not be exist
   2265       file_contents() { %text:expand
   2266         #|${1}Recipient:myself
   2267         #|${1}:ambiguous
   2268       }
   2269       The contents of file "${PREFIX}/new.$3" \
   2270         should equal "$(file_contents "$3")"
   2271       expected_log() {
   2272         if [ "$2" = pashage ]; then
   2273           %putsn 'Move extra.age to new.age'
   2274         else
   2275           %putsn 'Rename extra to new.'
   2276         fi
   2277         %text:expand
   2278         #|
   2279         #| extra.$1 => new.$1 | 0
   2280         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   2281         setup_log
   2282       }
   2283       The result of function git_log should be successful
   2284       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2285     End
   2286 
   2287     It 'renames a directory having an ambiguous name with `/` suffix'
   2288       Skip if 'pass(age) needs bash' check_skip $2
   2289       When run script $1 mv extra/ new
   2290       The status should be success
   2291       The directory "${PREFIX}/extra" should not be exist
   2292       The file "${PREFIX}/new/subdir/file.age" should be exist
   2293       The file "${PREFIX}/new/subdir/file.gpg" should be exist
   2294       expected_log() {
   2295         if [ "$2" = pashage ]; then
   2296           %putsn 'Move extra/ to new/'
   2297         else
   2298           %putsn 'Rename extra/ to new.'
   2299         fi
   2300         %text:expand
   2301         #|
   2302         #| {extra => new}/subdir/file.age | 0
   2303         #| {extra => new}/subdir/file.gpg | 0
   2304         #| 2 files changed, 0 insertions(+), 0 deletions(-)
   2305         setup_log
   2306       }
   2307       The result of function git_log should be successful
   2308       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2309     End
   2310 
   2311     It 'moves a file to a directory having an ambiguous name without suffix'
   2312       Skip if 'pass(age) needs bash' check_skip $2
   2313       When run script $1 mv subdir/file extra
   2314       The status should be success
   2315       The file "${PREFIX}/subdir/file.$3" should not be exist
   2316       file_contents() { %text:expand
   2317         #|${1}Recipient:myself
   2318         #|${1}:p4ssw0rd
   2319       }
   2320       The contents of file "${PREFIX}/extra/file.$3" \
   2321         should equal "$(file_contents "$3")"
   2322       expected_log() {
   2323         if [ "$2" = pashage ]; then
   2324           %putsn "Move subdir/file.$1 to extra/file.$1"
   2325         else
   2326           %putsn 'Rename subdir/file to extra.'
   2327         fi
   2328         %text:expand
   2329         #|
   2330         #| {subdir => extra}/file.$1 | 0
   2331         #| 1 file changed, 0 insertions(+), 0 deletions(-)
   2332         setup_log
   2333       }
   2334       The result of function git_log should be successful
   2335       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2336     End
   2337 
   2338     It 'does not merge directories recursively'
   2339       Skip if 'pass(age) needs bash' check_skip $2
   2340       When run script $1 mv -f subdir/ extra/
   2341       The status should equal 1
   2342       The error should include 'subdir'
   2343       The error should include 'extra/'
   2344       The directory "${PREFIX}/subdir" should be exist
   2345       The file "${PREFIX}/subdir/file.$3" should be exist
   2346       The result of function git_log should be successful
   2347       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2348     End
   2349 
   2350     It 'displays usage when called without argument'
   2351       Skip if 'pass(age) needs bash' check_skip $2
   2352       When run script $1 mv
   2353       The status should equal 1
   2354       The output should be blank
   2355       The error should include 'Usage:'
   2356       The result of function git_log should be successful
   2357       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2358     End
   2359 
   2360     It 'fails to rename a non-existent directory'
   2361       Skip if 'pass(age) considers "stale/" as the file "stale"' \
   2362         [ ! $2 = pashage ]
   2363       When run script $1 mv stale/ new-name
   2364       The status should equal 1
   2365       The error should equal 'Error: stale/ is not in the password store.'
   2366       The directory "${PREFIX}/new-name" should not be exist
   2367       The file "${PREFIX}/new-name.age" should not be exist
   2368       The file "${PREFIX}/new-name.gpg" should not be exist
   2369       The result of function git_log should be successful
   2370       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2371     End
   2372 
   2373     It 'fails to move a directory into a file masquerading as a directory'
   2374       Skip if 'pass(age) needs bash' check_skip $2
   2375       When run script $1 mv subdir y.txt/
   2376       The status should equal 1
   2377       The error should include 'y.txt'
   2378       The error should include 'ot a directory'
   2379       The result of function git_log should be successful
   2380       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2381     End
   2382 
   2383     It 'rejects a source path containing ..'
   2384       Skip if 'pass(age) needs bash' check_skip $2
   2385       When run script $1 mv fluff/../stale subdir/
   2386       The status should equal 1
   2387       The output should be blank
   2388       The error should include 'sneaky'
   2389       The file "${PREFIX}/stale.age" should be exist
   2390       The file "${PREFIX}/stale.gpg" should be exist
   2391       The file "${PREFIX}/subdir/stale.age" should not be exist
   2392       The file "${PREFIX}/subdir/stale.gpg" should not be exist
   2393       The result of function git_log should be successful
   2394       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2395     End
   2396 
   2397     It 'rejects a destination path containing ..'
   2398       Skip if 'pass(age) needs bash' check_skip $2
   2399       When run script $1 mv subdir/file extra/subdir/..
   2400       The status should equal 1
   2401       The output should be blank
   2402       The error should include 'sneaky'
   2403       The file "${PREFIX}/subdir/file.age" should be exist
   2404       The file "${PREFIX}/subdir/file.gpg" should be exist
   2405       The file "${PREFIX}/extra/file.age" should not be exist
   2406       The file "${PREFIX}/extra/file.gpg" should not be exist
   2407       The result of function git_log should be successful
   2408       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2409     End
   2410 
   2411     extra_setup() {
   2412       @mkdir "${PREFIX}/extra/stale.age" "${PREFIX}/extra/stale.gpg"
   2413     }
   2414     BeforeEach extra_setup
   2415 
   2416     It 'fails to move a file where a directory already exists'
   2417       Skip if 'pass(age) needs bash' check_skip $2
   2418       When run script $1 mv -f stale extra/
   2419       The status should equal 1
   2420       if [ "$2" = pashage ]; then
   2421         The error should equal 'Error: extra/ already contains stale.age/'
   2422       else
   2423         The error should match pattern 'mv: *directory*'
   2424       fi
   2425       The result of function git_log should be successful
   2426       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2427     End
   2428   End
   2429 
   2430   Describe 'cp'
   2431     It 'copies a file without reencrypting'
   2432       Skip if 'pass(age) needs bash' check_skip $2
   2433       When run script $1 cp subdir/file subdir/copy
   2434       The status should be success
   2435       The error should be blank
   2436       file_contents() { %text:expand
   2437         #|${1}Recipient:myself
   2438         #|${1}:p4ssw0rd
   2439       }
   2440       The contents of file "${PREFIX}/subdir/copy.$3" \
   2441         should equal "$(file_contents "$3")"
   2442       expected_log() {
   2443         if [ "$2" = pashage ]; then
   2444           %putsn 'Copy subdir/file.age to subdir/copy.age'
   2445         else
   2446           %putsn 'Copy subdir/file to subdir/copy.'
   2447         fi
   2448         %text:expand
   2449         #|
   2450         #| subdir/copy.$1 | 2 ++
   2451         #| 1 file changed, 2 insertions(+)
   2452         setup_log
   2453       }
   2454       The result of function git_log should be successful
   2455       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2456     End
   2457 
   2458     It 'fails to copy a file named like a flag without escape'
   2459       Skip if 'pass(age) needs bash' check_skip $2
   2460       When run script $1 cp -g safe-name
   2461       The status should equal 1
   2462       The output should be blank
   2463       The error should not be blank
   2464       The error should include 'Usage:'
   2465       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2466     End
   2467 
   2468     It 'copies a file named like a flag'
   2469       Skip if 'pass(age) needs bash' check_skip $2
   2470       When run script $1 cp -- -g -h
   2471       The status should be success
   2472       The error should be blank
   2473       file_contents() { %text:expand
   2474         #|${1}Recipient:myself
   2475         #|${1}:--
   2476       }
   2477       The contents of file "${PREFIX}/-h.$3" \
   2478         should equal "$(file_contents "$3")"
   2479       expected_log() {
   2480         if [ "$2" = pashage ]; then
   2481           %putsn 'Copy -g.age to -h.age'
   2482         else
   2483           %putsn 'Copy -g to -h.'
   2484         fi
   2485         %text:expand
   2486         #|
   2487         #| -h.$1 | 2 ++
   2488         #| 1 file changed, 2 insertions(+)
   2489         setup_log
   2490       }
   2491       The result of function git_log should be successful
   2492       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2493     End
   2494 
   2495     It 'reencrypts a copied file'
   2496       Skip if 'pass(age) needs bash' check_skip $2
   2497       When run script $1 cp subdir/file shared/copy
   2498       The status should be success
   2499       The error should be blank
   2500       file_contents() { %text:expand
   2501         #|${1}Recipient:myself
   2502         #|${1}Recipient:friend
   2503         #|${1}:p4ssw0rd
   2504       }
   2505       The contents of file "${PREFIX}/shared/copy.$3" \
   2506         should equal "$(file_contents "$3")"
   2507       expected_log() {
   2508         if [ "$2" = pashage ]; then
   2509           %putsn 'Copy subdir/file.age to shared/copy.age'
   2510         else
   2511           %putsn 'Copy subdir/file to shared/copy.'
   2512         fi
   2513         %text:expand
   2514         #|
   2515         #| shared/copy.$1 | 3 +++
   2516         #| 1 file changed, 3 insertions(+)
   2517         setup_log
   2518       }
   2519       The result of function git_log should be successful
   2520       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2521     End
   2522 
   2523     It 'copies an unencrypted file without reencrypting it'
   2524       Skip if 'pass(age) needs bash' check_skip $2
   2525       When run script $1 cp y.txt shared
   2526       The status should be success
   2527       The error should be blank
   2528       file_contents() { %text
   2529         #|Unencrypted line 1
   2530         #|Unencrypted line 2
   2531         #|Unencrypted line 3
   2532       }
   2533       The contents of file "${PREFIX}/shared/y.txt" \
   2534         should equal "$(file_contents)"
   2535       expected_log() {
   2536         if [ "$2" = pashage ]; then
   2537           %putsn 'Copy y.txt to shared/y.txt'
   2538         else
   2539           %putsn 'Copy y.txt to shared.'
   2540         fi
   2541         %text
   2542         #|
   2543         #| shared/y.txt | 3 +++
   2544         #| 1 file changed, 3 insertions(+)
   2545         setup_log
   2546       }
   2547       The result of function git_log should be successful
   2548       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2549     End
   2550 
   2551     It 'reencrypts relevant files in a copied directory'
   2552       Skip if 'pass(age) needs bash' check_skip $2
   2553       When run script $1 cp subdir shared/
   2554       The status should be success
   2555       The error should be blank
   2556       file_contents() { %text:expand
   2557         #|${1}Recipient:myself
   2558         #|${1}Recipient:friend
   2559         #|${1}:p4ssw0rd
   2560       }
   2561       The contents of file "${PREFIX}/shared/subdir/file.$3" \
   2562         should equal "$(file_contents "$3")"
   2563       expected_log() {
   2564         if [ "$2" = pashage ]; then
   2565           %putsn 'Copy subdir/ to shared/subdir/'
   2566         else
   2567           %putsn 'Copy subdir to shared/.'
   2568         fi
   2569         if [ "$1" = age ]; then
   2570           %text:expand
   2571           #|
   2572           #| shared/subdir/file.age | 3 +++
   2573           #| shared/subdir/file.gpg | 2 ++
   2574           #| 2 files changed, 5 insertions(+)
   2575         else
   2576           %text:expand
   2577           #|
   2578           #| shared/subdir/file.age | 2 ++
   2579           #| shared/subdir/file.gpg | 3 +++
   2580           #| 2 files changed, 5 insertions(+)
   2581         fi
   2582         setup_log
   2583       }
   2584       The result of function git_log should be successful
   2585       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2586     End
   2587 
   2588     It 'copies a file into a new directory'
   2589       Skip if 'pass(age) needs bash' check_skip $2
   2590       When run script $1 cp subdir/file new-subdir/
   2591       The status should be success
   2592       The error should be blank
   2593       file_contents() { %text:expand
   2594         #|${1}Recipient:myself
   2595         #|${1}:p4ssw0rd
   2596       }
   2597       The contents of file "${PREFIX}/new-subdir/file.$3" \
   2598         should equal "$(file_contents "$3")"
   2599       expected_log() {
   2600         if [ "$2" = pashage ]; then
   2601           %putsn 'Copy subdir/file.age to new-subdir/file.age'
   2602         else
   2603           %putsn 'Copy subdir/file to new-subdir/.'
   2604         fi
   2605         %text:expand
   2606         #|
   2607         #| new-subdir/file.$1 | 2 ++
   2608         #| 1 file changed, 2 insertions(+)
   2609         setup_log
   2610       }
   2611       The result of function git_log should be successful
   2612       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2613     End
   2614 
   2615     It 'fails to copy a non-existent file'
   2616       Skip if 'pass(age) needs bash' check_skip $2
   2617       When run script $1 cp non-existent-file new-name
   2618       The status should equal 1
   2619       The error should \
   2620         equal 'Error: non-existent-file is not in the password store.'
   2621       The result of function git_log should be successful
   2622       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2623     End
   2624 
   2625     It 'copies a directory with recipients'
   2626       Skip if 'pass(age) needs bash' check_skip $2
   2627       When run script $1 cp fluff filler
   2628       The status should be success
   2629       The error should be blank
   2630       The directory "${PREFIX}/filler" should be exist
   2631       expected_log() {
   2632         if [ "$2" = pashage ]; then
   2633           %putsn 'Copy fluff/ to filler/'
   2634         else
   2635           %putsn 'Copy fluff to filler.'
   2636         fi
   2637         %text:expand
   2638         #|
   2639         #| filler/.age-recipients | 2 ++
   2640         #| filler/.gpg-id         | 2 ++
   2641         #| filler/one.age         | 3 +++
   2642         #| filler/one.gpg         | 3 +++
   2643         #| filler/three.age       | 5 +++++
   2644         #| filler/three.gpg       | 5 +++++
   2645         #| filler/two.age         | 4 ++++
   2646         #| filler/two.gpg         | 4 ++++
   2647         #| 8 files changed, 28 insertions(+)
   2648         setup_log
   2649       }
   2650       The result of function git_log should be successful
   2651       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2652     End
   2653 
   2654     It 'copies a directory without recipients'
   2655       Skip if 'pass(age) needs bash' check_skip $2
   2656       When run script $1 cp subdir newdir
   2657       The status should be success
   2658       The error should be blank
   2659       file_contents() { %text:expand
   2660         #|${1}Recipient:myself
   2661         #|${1}:p4ssw0rd
   2662       }
   2663       The contents of file "${PREFIX}/newdir/file.$3" \
   2664         should equal "$(file_contents "$3")"
   2665       expected_log() {
   2666         if [ "$2" = pashage ]; then
   2667           %putsn 'Copy subdir/ to newdir/'
   2668         else
   2669           %putsn 'Copy subdir to newdir.'
   2670         fi
   2671         %text:expand
   2672         #|
   2673         #| newdir/file.age | 2 ++
   2674         #| newdir/file.gpg | 2 ++
   2675         #| 2 files changed, 4 insertions(+)
   2676         setup_log
   2677       }
   2678       The result of function git_log should be successful
   2679       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2680     End
   2681 
   2682     It 'overwrites an existing file when forced'
   2683       Skip if 'pass(age) needs bash' check_skip $2
   2684       When run script $1 cp -f fluff/two fluff/one
   2685       The status should be success
   2686       file_contents() { %text:expand
   2687         #|${1}Recipient:master
   2688         #|${1}Recipient:myself
   2689         #|${1}:2-password
   2690         #|${1}:URL: https://example.com/login
   2691       }
   2692       The contents of file "${PREFIX}/fluff/one.$3" \
   2693         should equal "$(file_contents "$3")"
   2694       expected_log() {
   2695         if [ "$2" = pashage ]; then
   2696           %putsn 'Copy fluff/two.age to fluff/one.age'
   2697         else
   2698           %putsn 'Copy fluff/two to fluff/one.'
   2699         fi
   2700         %text:expand
   2701         #|
   2702         #| fluff/one.$1 | 3 ++-
   2703         #| 1 file changed, 2 insertions(+), 1 deletion(-)
   2704         setup_log
   2705       }
   2706       The result of function git_log should be successful
   2707       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2708     End
   2709 
   2710     It 'overwrites collisions when copying recursively and forcefully'
   2711       Skip if 'pass(age) needs bash' check_skip $2
   2712       When run script $1 cp -f subdir/ extra/
   2713       The status should be success
   2714       expected_log() {
   2715         if [ "$2" = pashage ]; then
   2716           %putsn 'Copy subdir/ to extra/subdir/'
   2717         else
   2718           %putsn 'Copy subdir/ to extra/.'
   2719         fi
   2720         %text:expand
   2721         #|
   2722         #| extra/subdir/file.age | 2 +-
   2723         #| extra/subdir/file.gpg | 2 +-
   2724         #| 2 files changed, 2 insertions(+), 2 deletions(-)
   2725         setup_log
   2726       }
   2727       The result of function git_log should be successful
   2728       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2729     End
   2730 
   2731     It 'copies a file having an ambiguous name without suffix'
   2732       Skip if 'pass(age) needs bash' check_skip $2
   2733       When run script $1 cp extra new
   2734       The status should be success
   2735       file_contents() { %text:expand
   2736         #|${1}Recipient:myself
   2737         #|${1}:ambiguous
   2738       }
   2739       The contents of file "${PREFIX}/new.$3" \
   2740         should equal "$(file_contents "$3")"
   2741       expected_log() {
   2742         if [ "$2" = pashage ]; then
   2743           %putsn 'Copy extra.age to new.age'
   2744         else
   2745           %putsn 'Copy extra to new.'
   2746         fi
   2747         %text:expand
   2748         #|
   2749         #| new.$1 | 2 ++
   2750         #| 1 file changed, 2 insertions(+)
   2751         setup_log
   2752       }
   2753       The result of function git_log should be successful
   2754       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2755     End
   2756 
   2757     It 'copies a directory having an ambiguous name with `/` suffix'
   2758       Skip if 'pass(age) needs bash' check_skip $2
   2759       When run script $1 cp extra/ new
   2760       The status should be success
   2761       file_contents() { %text:expand
   2762         #|${1}Recipient:myself
   2763         #|${1}:Pa55worD
   2764       }
   2765       The contents of file "${PREFIX}/new/subdir/file.$3" \
   2766         should equal "$(file_contents $3)"
   2767       expected_log() {
   2768         if [ "$2" = pashage ]; then
   2769           %putsn 'Copy extra/ to new/'
   2770         else
   2771           %putsn 'Copy extra/ to new.'
   2772         fi
   2773         %text:expand
   2774         #|
   2775         #| new/subdir/file.age | 2 ++
   2776         #| new/subdir/file.gpg | 2 ++
   2777         #| 2 files changed, 4 insertions(+)
   2778         setup_log
   2779       }
   2780       The result of function git_log should be successful
   2781       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2782     End
   2783 
   2784     It 'copies a file to a directory having an ambiguous name without suffix'
   2785       Skip if 'pass(age) needs bash' check_skip $2
   2786       When run script $1 cp subdir/file extra
   2787       The status should be success
   2788       file_contents() { %text:expand
   2789         #|${1}Recipient:myself
   2790         #|${1}:p4ssw0rd
   2791       }
   2792       The contents of file "${PREFIX}/extra/file.$3" \
   2793         should equal "$(file_contents "$3")"
   2794       expected_log() {
   2795         if [ "$2" = pashage ]; then
   2796           %putsn "Copy subdir/file.$1 to extra/file.$1"
   2797         else
   2798           %putsn 'Copy subdir/file to extra.'
   2799         fi
   2800         %text:expand
   2801         #|
   2802         #| extra/file.$1 | 2 ++
   2803         #| 1 file changed, 2 insertions(+)
   2804         setup_log
   2805       }
   2806       The result of function git_log should be successful
   2807       The contents of file "${GITLOG}" should equal "$(expected_log $3 $2)"
   2808     End
   2809 
   2810     It 'displays usage when called without argument'
   2811       Skip if 'pass(age) needs bash' check_skip $2
   2812       When run script $1 copy
   2813       The status should equal 1
   2814       The output should be blank
   2815       The error should include 'Usage:'
   2816       The error should include ' copy '
   2817       The result of function git_log should be successful
   2818       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2819     End
   2820 
   2821     It 'fails to copy a non-existent directory'
   2822       Skip if 'pass(age) considers "stale/" as the file "stale"' \
   2823         [ ! $2 = pashage ]
   2824       When run script $1 cp stale/ new-name
   2825       The status should equal 1
   2826       The error should equal 'Error: stale/ is not in the password store.'
   2827       The directory "${PREFIX}/new-name" should not be exist
   2828       The file "${PREFIX}/new-name.age" should not be exist
   2829       The file "${PREFIX}/new-name.gpg" should not be exist
   2830       The result of function git_log should be successful
   2831       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2832     End
   2833 
   2834     It 'fails to copy a directory into a file masquerading as a directory'
   2835       Skip if 'pass(age) needs bash' check_skip $2
   2836       When run script $1 cp subdir y.txt/
   2837       The status should equal 1
   2838       The error should include 'y.txt is not a directory'
   2839       The result of function git_log should be successful
   2840       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2841     End
   2842 
   2843     It 'rejects a source path containing ..'
   2844       Skip if 'pass(age) needs bash' check_skip $2
   2845       When run script $1 cp fluff/../stale subdir/
   2846       The status should equal 1
   2847       The output should be blank
   2848       The error should include 'sneaky'
   2849       The file "${PREFIX}/subdir/stale.age" should not be exist
   2850       The file "${PREFIX}/subdir/stale.gpg" should not be exist
   2851       The result of function git_log should be successful
   2852       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2853     End
   2854 
   2855     It 'rejects a destination path containing ..'
   2856       Skip if 'pass(age) needs bash' check_skip $2
   2857       When run script $1 cp subdir/file extra/subdir/..
   2858       The status should equal 1
   2859       The output should be blank
   2860       The error should include 'sneaky'
   2861       The file "${PREFIX}/extra/file.age" should not be exist
   2862       The file "${PREFIX}/extra/file.gpg" should not be exist
   2863       The result of function git_log should be successful
   2864       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2865     End
   2866 
   2867     extra_setup() {
   2868       @mkdir "${PREFIX}/extra/stale.age" "${PREFIX}/extra/stale.gpg"
   2869     }
   2870     BeforeEach extra_setup
   2871 
   2872     It 'fails to copy a file where a directory already exists'
   2873       Skip if 'pass(age) needs bash' check_skip $2
   2874       When run script $1 cp -f stale extra/
   2875       The status should equal 1
   2876       if [ "$2" = pashage ]; then
   2877         The error should equal 'Error: extra/ already contains stale.age/'
   2878       else
   2879         The error should match pattern 'cp: *directory*'
   2880       fi
   2881       The result of function git_log should be successful
   2882       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2883     End
   2884   End
   2885 
   2886   Describe 'git'
   2887     It 'transmits arguments to git'
   2888       Skip if 'pass(age) needs bash' check_skip $2
   2889       When run script $1 git log --format='%s' --stat
   2890       The status should be success
   2891       The output should equal "$(setup_log)"
   2892     End
   2893 
   2894     It 'displays git usage when called without argument'
   2895       Skip if 'pass(age) needs bash' check_skip $2
   2896       When run script $1 git
   2897       The status should equal \
   2898         "$(if [ $2 = pashage ]; then echo 1; else echo 0; fi)"
   2899       The output should include 'usage: git'
   2900       The error should be blank
   2901       The result of function git_log should be successful
   2902       The contents of file "${GITLOG}" should equal "$(setup_log)"
   2903     End
   2904 
   2905     remove_git() { rm -rf "${PREFIX}/.git"; }
   2906     BeforeEach remove_git
   2907 
   2908     It 'fails without a git repository'
   2909       Skip if 'pass(age) needs bash' check_skip $2
   2910       When run script $1 git log
   2911       The status should equal 1
   2912       The output should be blank
   2913       The error should match pattern \
   2914         'Error: the password store is not a git repository. Try "* git init".'
   2915     End
   2916 
   2917     It 're-initializes the git repository'
   2918       Skip if 'pass(age) needs bash' check_skip $2
   2919       When run script $1 git init -b trunk
   2920       The status should be successful
   2921       The output should start with \
   2922         "Initialized empty Git repository in ${PREFIX}/.git"
   2923       The error should be blank
   2924       The directory "${PREFIX}/.git" should be exist
   2925       The file "${PREFIX}/.gitattributes" should be exist
   2926     End
   2927   End
   2928 
   2929   Describe 'help'
   2930     It 'displays a help text with supported commands'
   2931       Skip if 'pass(age) needs bash' check_skip $2
   2932       When run script $1 help
   2933       The status should be success
   2934       if ! [ $2 = passage ]; then
   2935         The output should include ' init '
   2936       fi
   2937       The output should include ' find '
   2938       The output should include ' [show] '
   2939       The output should include ' grep '
   2940       The output should include ' insert '
   2941       The output should include ' edit '
   2942       The output should include ' generate '
   2943       The output should include ' git '
   2944       The output should include ' help'
   2945       The output should include ' version'
   2946     End
   2947   End
   2948 
   2949   Describe 'version'
   2950     It 'displays a version box'
   2951       Skip if 'pass(age) needs bash' check_skip $2
   2952       When run script $1 version
   2953       The status should be success
   2954       The output should include "$2:"
   2955       The output should match pattern "*v[0-9].[0-9].[0-9]*"
   2956       The output should include 'password manager'
   2957       The output should start with '============='
   2958       The output should end with '============='
   2959     End
   2960   End
   2961 
   2962   Describe 'working outside of a repository:'
   2963     REF_PREFIX="${SHELLSPEC_WORKDIR}/ref"
   2964     CHANGE_FILE="${SHELLSPEC_WORKDIR}/changes.diff"
   2965     make_gitless() {
   2966       @rm -rf "${PREFIX}/.git"
   2967       @cp -r "${PREFIX}" "${REF_PREFIX}"
   2968     }
   2969     clean_gitless() {
   2970       @rm -rf "${REF_PREFIX}" "${CHANGE_FILE}"
   2971     }
   2972     BeforeEach make_gitless
   2973     AfterEach clean_gitless
   2974 
   2975     check_changes() {
   2976       (cd "${SHELLSPEC_WORKDIR}" && @diff -r ref store ||true)>"${CHANGE_FILE}"
   2977     }
   2978 
   2979     Example 'init'
   2980       Skip if 'pass(age) needs bash' check_skip $2
   2981       Skip if 'passage has no init' [ "$2" = passage ]
   2982       When run script $1 init 'new-id'
   2983       The error should be blank
   2984       The status should be success
   2985       The output should include 'Password store'
   2986       changes() {
   2987         %text:expand
   2988         #|diff -r ref/-g.$1 store/-g.$1
   2989         #|1c1
   2990         #|< $1Recipient:myself
   2991         #|---
   2992         #|> $1Recipient:new-id
   2993         if [ "$1" = age ]; then
   2994           %putsn 'Only in store: .age-recipients'
   2995         else
   2996           %text
   2997           #|diff -r ref/.gpg-id store/.gpg-id
   2998           #|1c1
   2999           #|< myself
   3000           #|---
   3001           #|> new-id
   3002         fi
   3003         %text:expand
   3004         #|diff -r ref/extra/subdir/file.$1 store/extra/subdir/file.$1
   3005         #|1c1
   3006         #|< $1Recipient:myself
   3007         #|---
   3008         #|> $1Recipient:new-id
   3009         #|diff -r ref/extra.$1 store/extra.$1
   3010         #|1c1
   3011         #|< $1Recipient:myself
   3012         #|---
   3013         #|> $1Recipient:new-id
   3014         #|diff -r ref/stale.$1 store/stale.$1
   3015         #|1,2c1
   3016         #|< $1Recipient:master
   3017         #|< $1Recipient:myself
   3018         #|---
   3019         #|> $1Recipient:new-id
   3020         #|diff -r ref/subdir/file.$1 store/subdir/file.$1
   3021         #|1c1
   3022         #|< $1Recipient:myself
   3023         #|---
   3024         #|> $1Recipient:new-id
   3025       }
   3026       The directory "${PREFIX}/.git" should not be exist
   3027       The result of function check_changes should be successful
   3028       The contents of file "${CHANGE_FILE}" should equal "$(changes $3)"
   3029     End
   3030 
   3031     Example 'ls'
   3032       Skip if 'pass(age) needs bash' check_skip $2
   3033       When run script $1 ls subdir
   3034       The status should be success
   3035       The error should be blank
   3036       The line 1 of output should include 'subdir'
   3037       The line 2 of output should include 'file'
   3038       The directory "${PREFIX}/.git" should not be exist
   3039       The result of function check_changes should be successful
   3040       The contents of file "${CHANGE_FILE}" should equal ''
   3041     End
   3042 
   3043     Example 'find'
   3044       Skip if 'pass(age) needs bash' check_skip $2
   3045       When run script $1 find o
   3046       The status should be success
   3047       The lines of output should equal 6
   3048       The line 1 of output should match pattern 'Search *: o'
   3049       The line 2 of output should include 'fluff'
   3050       The line 3 of output should include 'one'
   3051       The line 4 of output should include 'one'
   3052       The line 5 of output should include 'two'
   3053       The line 6 of output should include 'two'
   3054       The directory "${PREFIX}/.git" should not be exist
   3055       The result of function check_changes should be successful
   3056       The contents of file "${CHANGE_FILE}" should equal ''
   3057     End
   3058 
   3059     Example 'show'
   3060       Skip if 'pass(age) needs bash' check_skip $2
   3061       When run script $1 show subdir/file
   3062       The status should be success
   3063       The output should equal 'p4ssw0rd'
   3064       The error should be blank
   3065       The directory "${PREFIX}/.git" should not be exist
   3066       The result of function check_changes should be successful
   3067       The contents of file "${CHANGE_FILE}" should equal ''
   3068     End
   3069 
   3070     Example 'grep'
   3071       Skip if 'pass(age) needs bash' check_skip $2
   3072       When run script $1 grep -i Com
   3073       The status should be success
   3074       The lines of output should equal 4
   3075       The line 1 of output should include 'fluff'
   3076       The output should include 'three'
   3077       The line 2 of output should include 'https://example.'
   3078       The line 3 of output should include 'fluff'
   3079       The output should include 'two'
   3080       The line 4 of output should include 'https://example.'
   3081       The directory "${PREFIX}/.git" should not be exist
   3082       The result of function check_changes should be successful
   3083       The contents of file "${CHANGE_FILE}" should equal ''
   3084     End
   3085 
   3086     Example 'insert'
   3087       Skip if 'pass(age) needs bash' check_skip $2
   3088       Data
   3089         #|password
   3090         #|Username: tester
   3091         #|URL: https://example.com/login
   3092       End
   3093       When run script $1 insert -m rootpass
   3094       The status should be success
   3095       The output should include 'rootpass'
   3096       The error should be blank
   3097       expected_file() { %text:expand
   3098         #|$1Recipient:myself
   3099         #|$1:password
   3100         #|$1:Username: tester
   3101         #|$1:URL: https://example.com/login
   3102       }
   3103       The contents of file "${PREFIX}/rootpass.$3" should \
   3104         equal "$(expected_file $3)"
   3105       The directory "${PREFIX}/.git" should not be exist
   3106       The result of function check_changes should be successful
   3107       The contents of file "${CHANGE_FILE}" should \
   3108         equal "Only in store: rootpass.$3"
   3109     End
   3110 
   3111     Example 'edit'
   3112       EDITOR=ed
   3113       TERM=dumb
   3114       Skip if 'pass(age) needs bash' check_skip $2
   3115       Data
   3116         #|2i
   3117         #|New line
   3118         #|.
   3119         #|wq
   3120       End
   3121       When run script $1 edit fluff/two
   3122       The status should be success
   3123       The error should be blank
   3124       changes() { %text:expand
   3125         #|diff -r ref/fluff/two.$1 store/fluff/two.$1
   3126         #|3a4
   3127         #|> $1:New line
   3128       }
   3129       The directory "${PREFIX}/.git" should not be exist
   3130       The result of function check_changes should be successful
   3131       The contents of file "${CHANGE_FILE}" should equal "$(changes $3)"
   3132     End
   3133 
   3134     Example 'generate'
   3135       Skip if 'pass(age) needs bash' check_skip $2
   3136       When run script $1 generate newfile
   3137       The status should be success
   3138       The output should include 'The generated password for'
   3139       The file "${PREFIX}/newfile.$3" should be exist
   3140       The lines of contents of file "${PREFIX}/newfile.$3" should equal 2
   3141       The line 1 of contents of file "${PREFIX}/newfile.$3" should \
   3142         equal "$3Recipient:myself"
   3143       The output should include "$(@sed -n "2s/$3://p" "${PREFIX}/newfile.$3")"
   3144       The directory "${PREFIX}/.git" should not be exist
   3145       The result of function check_changes should be successful
   3146       The contents of file "${CHANGE_FILE}" should \
   3147         equal "Only in store: newfile.$3"
   3148     End
   3149 
   3150     Example 'rm'
   3151       Skip if 'pass(age) needs bash' check_skip $2
   3152       When run script $1 rm -f subdir/file
   3153       The status should be success
   3154       The output should include 'subdir/file'
   3155       The error should be blank
   3156       The file "${PREFIX}/subdir/file.$3" should not be exist
   3157       The directory "${PREFIX}/.git" should not be exist
   3158       The result of function check_changes should be successful
   3159       The contents of file "${CHANGE_FILE}" should \
   3160         equal "Only in ref/subdir: file.$3"
   3161     End
   3162 
   3163     Example 'mv'
   3164       Skip if 'pass(age) needs bash' check_skip $2
   3165       When run script $1 mv subdir/file subdir/renamed
   3166       The status should be success
   3167       The error should be blank
   3168       The file "${PREFIX}/subdir/file.$3" should not be exist
   3169       file_contents() { %text:expand
   3170         #|${1}Recipient:myself
   3171         #|${1}:p4ssw0rd
   3172       }
   3173       The contents of file "${PREFIX}/subdir/renamed.$3" \
   3174         should equal "$(file_contents "$3")"
   3175       changes() { %text:expand
   3176         #|Only in ref/subdir: file.$1
   3177         #|Only in store/subdir: renamed.$1
   3178       }
   3179       The directory "${PREFIX}/.git" should not be exist
   3180       The result of function check_changes should be successful
   3181       The contents of file "${CHANGE_FILE}" should equal "$(changes $3)"
   3182     End
   3183 
   3184     Example 'cp'
   3185       Skip if 'pass(age) needs bash' check_skip $2
   3186       When run script $1 cp subdir/file copy
   3187       The status should be success
   3188       The error should be blank
   3189       file_contents() { %text:expand
   3190         #|${1}Recipient:myself
   3191         #|${1}:p4ssw0rd
   3192       }
   3193       The contents of file "${PREFIX}/copy.$3" \
   3194         should equal "$(file_contents "$3")"
   3195       The directory "${PREFIX}/.git" should not be exist
   3196       The result of function check_changes should be successful
   3197       The contents of file "${CHANGE_FILE}" should \
   3198         equal "Only in store: copy.$3"
   3199     End
   3200 
   3201     Example 'help'
   3202       Skip if 'pass(age) needs bash' check_skip $2
   3203       When run script $1 help
   3204       The status should be success
   3205       if ! [ $2 = passage ]; then
   3206         The output should include ' init '
   3207       fi
   3208       The output should include ' find '
   3209       The output should include ' [show] '
   3210       The output should include ' grep '
   3211       The output should include ' insert '
   3212       The output should include ' edit '
   3213       The output should include ' generate '
   3214       The output should include ' git '
   3215       The output should include ' help'
   3216       The output should include ' version'
   3217       The directory "${PREFIX}/.git" should not be exist
   3218       The result of function check_changes should be successful
   3219       The contents of file "${CHANGE_FILE}" should equal ''
   3220     End
   3221 
   3222     Example 'version'
   3223       Skip if 'pass(age) needs bash' check_skip $2
   3224       When run script $1 version
   3225       The status should be success
   3226       The output should include "$2:"
   3227       The output should match pattern "*v[0-9].[0-9].[0-9]*"
   3228       The output should include 'password manager'
   3229       The output should start with '============='
   3230       The output should end with '============='
   3231       The directory "${PREFIX}/.git" should not be exist
   3232       The result of function check_changes should be successful
   3233       The contents of file "${CHANGE_FILE}" should equal ''
   3234     End
   3235   End
   3236 End