action_spec.sh (63357B)
1 # pashage - age-backed POSIX password manager 2 # Copyright (C) 2024-2025 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 fully covers all action functions in isolation, 19 # with all interactions fully mocked. 20 21 Describe 'Action Functions' 22 Include src/pashage.sh 23 if [ "${SHELLSPEC_SHELL_TYPE}" = sh ]; then 24 Set 'errexit:on' 'nounset:on' 25 else 26 Set 'errexit:on' 'nounset:on' 'pipefail:on' 27 fi 28 29 Describe 'do_copy_move' 30 DECISION=default 31 OVERWRITE=yes 32 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 33 34 ACTION=Move 35 SCM_ACTION=scm_mv 36 37 do_decrypt() { 38 mocklog do_decrypt "$@" 39 %putsn data 40 } 41 42 do_encrypt() { 43 @cat >/dev/null 44 mocklog do_encrypt "$@" 45 } 46 47 basename() { @basename "$@"; } 48 cat() { @cat "$@"; } 49 dirname() { @dirname "$@"; } 50 51 mkdir() { mocklog mkdir "$@"; } 52 scm_add() { mocklog scm_add "$@"; } 53 scm_begin() { mocklog scm_begin "$@"; } 54 scm_commit() { mocklog scm_commit "$@"; } 55 scm_cp() { mocklog scm_cp "$@"; } 56 scm_mv() { mocklog scm_mv "$@"; } 57 scm_rm() { mocklog scm_rm "$@"; } 58 59 setup() { 60 @mkdir -p "${PREFIX}/sub/bare/sub" "${PREFIX}/subdir/notes.txt" 61 %putsn 'identity 1' >"${PREFIX}/.age-recipients" 62 %putsn 'identity 2' >"${PREFIX}/sub/.age-recipients" 63 %putsn 'identity 2' >"${PREFIX}/subdir/.age-recipients" 64 %putsn data >"${PREFIX}/sub/secret.age" 65 %putsn data >"${PREFIX}/sub/bare/deep.age" 66 %putsn data >"${PREFIX}/sub/bare/sub/deepest.age" 67 %putsn data >"${PREFIX}/subdir/lower.age" 68 %putsn data >"${PREFIX}/root.age" 69 %putsn data >"${PREFIX}/notes.txt" 70 } 71 72 cleanup() { 73 @rm -rf "${PREFIX}" 74 } 75 76 BeforeEach setup 77 AfterEach cleanup 78 79 It 'renames a file without re-encrypting' 80 result() { 81 %text:expand 82 #|$ mkdir -p -- ${PREFIX}/sub 83 #|$ scm_begin 84 #|$ scm_mv sub/secret.age sub/renamed.age 85 #|$ scm_commit Move sub/secret.age to sub/renamed.age 86 } 87 When call do_copy_move sub/secret sub/renamed 88 The status should be success 89 The output should be blank 90 The error should equal "$(result)" 91 End 92 93 It 're-encrypts when copying to another identity' 94 ACTION=Copy 95 SCM_ACTION=scm_cp 96 result() { 97 %text:expand 98 #|$ scm_begin 99 #|$ do_decrypt ${PREFIX}/root.age 100 #|$ do_encrypt sub/root.age 101 #|$ scm_add sub/root.age 102 #|$ scm_commit Copy root.age to sub/root.age 103 } 104 When call do_copy_move root sub/ 105 The status should be success 106 The output should be blank 107 The error should equal "$(result)" 108 End 109 110 It 'accepts explicit .age extensions' 111 ACTION=Copy 112 SCM_ACTION=scm_cp 113 result() { 114 %text:expand 115 #|$ mkdir -p -- ${PREFIX}/sub 116 #|$ scm_begin 117 #|$ do_decrypt ${PREFIX}/root.age 118 #|$ do_encrypt sub/moved.age 119 #|$ scm_add sub/moved.age 120 #|$ scm_commit Copy root.age to sub/moved.age 121 } 122 When call do_copy_move root.age sub/moved.age 123 The status should be success 124 The output should be blank 125 The error should equal "$(result)" 126 End 127 128 It 'can be prevented from re-encrypting when copying to another identity' 129 DECISION=keep 130 ACTION=Copy 131 SCM_ACTION=scm_cp 132 result() { %text 133 #|$ scm_begin 134 #|$ scm_cp root.age sub/root.age 135 #|$ scm_commit Copy root.age to sub/root.age 136 } 137 When call do_copy_move root sub/ 138 The status should be success 139 The output should be blank 140 The error should equal "$(result)" 141 End 142 143 It 'does not re-encrypt a non-encrypted file' 144 result() { %text 145 #|$ scm_begin 146 #|$ scm_mv notes.txt sub/notes.txt 147 #|$ scm_commit Move notes.txt to sub/notes.txt 148 } 149 When call do_copy_move notes.txt sub/ 150 The status should be success 151 The output should be blank 152 The error should equal "$(result)" 153 End 154 155 It 'does not re-encrypt a non-encrypted file even when forced' 156 DECISION=force 157 result() { %text 158 #|$ scm_begin 159 #|$ scm_mv notes.txt sub/notes.txt 160 #|$ scm_commit Move notes.txt to sub/notes.txt 161 } 162 When call do_copy_move notes.txt sub/ 163 The status should be success 164 The output should be blank 165 The error should equal "$(result)" 166 End 167 168 It 'moves a file without re-encrypting to another directory' 169 result() { %text 170 #|$ scm_begin 171 #|$ scm_mv sub/secret.age subdir/secret.age 172 #|$ scm_commit Move sub/secret.age to subdir/secret.age 173 } 174 When call do_copy_move sub/secret subdir 175 The status should be success 176 The output should be blank 177 The error should equal "$(result)" 178 End 179 180 It 'asks confirmation before overwriting a file' 181 OVERWRITE=no 182 rm() { mocklog rm "$@"; } 183 yesno() { 184 mocklog yesno "$@" 185 ANSWER=y 186 } 187 result() { 188 %text:expand 189 #|$ mkdir -p -- ${PREFIX}/sub 190 #|$ scm_begin 191 #|$ yesno sub/secret.age already exists. Overwrite? 192 #|$ rm -f -- ${PREFIX}/sub/secret.age 193 #|$ do_decrypt ${PREFIX}/root.age 194 #|$ do_encrypt sub/secret.age 195 #|$ scm_rm root.age 196 #|$ scm_add sub/secret.age 197 #|$ scm_commit Move root.age to sub/secret.age 198 } 199 When call do_copy_move root sub/secret 200 The status should be success 201 The output should be blank 202 The error should equal "$(result)" 203 End 204 205 It 'moves a whole directory with identity' 206 result() { 207 %text:expand 208 #|$ scm_begin 209 #|$ mkdir -p -- ${PREFIX}/subdir/sub 210 #|$ scm_mv sub/.age-recipients subdir/sub/.age-recipients 211 #|$ mkdir -p -- ${PREFIX}/subdir/sub/bare 212 #|$ scm_mv sub/bare/deep.age subdir/sub/bare/deep.age 213 #|$ mkdir -p -- ${PREFIX}/subdir/sub/bare/sub 214 #|$ scm_mv sub/bare/sub/deepest.age subdir/sub/bare/sub/deepest.age 215 #|$ scm_mv sub/secret.age subdir/sub/secret.age 216 #|$ scm_commit Move sub/ to subdir/sub/ 217 } 218 When call do_copy_move sub subdir/ 219 The status should be success 220 The output should be blank 221 The error should equal "$(result)" 222 End 223 224 It 'recursively moves files to a directory with the same identity' 225 result() { 226 %text:expand 227 #|$ scm_begin 228 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare 229 #|$ scm_mv sub/bare/deep.age subdir/new-bare/deep.age 230 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub 231 #|$ scm_mv sub/bare/sub/deepest.age subdir/new-bare/sub/deepest.age 232 #|$ scm_commit Move sub/bare/ to subdir/new-bare/ 233 } 234 When call do_copy_move sub/bare subdir/new-bare 235 The status should be success 236 The output should be blank 237 The error should equal "$(result)" 238 End 239 240 It 'recursively re-encrypts a directory' 241 result() { 242 %text:expand 243 #|$ scm_begin 244 #|$ mkdir -p -- ${PREFIX}/new-bare 245 #|$ do_decrypt ${PREFIX}/sub/bare/deep.age 246 #|$ do_encrypt new-bare/deep.age 247 #|$ scm_rm sub/bare/deep.age 248 #|$ scm_add new-bare/deep.age 249 #|$ mkdir -p -- ${PREFIX}/new-bare/sub 250 #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age 251 #|$ do_encrypt new-bare/sub/deepest.age 252 #|$ scm_rm sub/bare/sub/deepest.age 253 #|$ scm_add new-bare/sub/deepest.age 254 #|$ scm_commit Move sub/bare/ to new-bare/ 255 } 256 When call do_copy_move sub/bare new-bare 257 The status should be success 258 The output should be blank 259 The error should equal "$(result)" 260 End 261 262 It 'recursively re-encrypts a directory with the same identity when forced' 263 DECISION=force 264 result() { 265 %text:expand 266 #|$ scm_begin 267 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare 268 #|$ do_decrypt ${PREFIX}/sub/bare/deep.age 269 #|$ do_encrypt subdir/new-bare/deep.age 270 #|$ scm_rm sub/bare/deep.age 271 #|$ scm_add subdir/new-bare/deep.age 272 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub 273 #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age 274 #|$ do_encrypt subdir/new-bare/sub/deepest.age 275 #|$ scm_rm sub/bare/sub/deepest.age 276 #|$ scm_add subdir/new-bare/sub/deepest.age 277 #|$ scm_commit Move sub/bare/ to subdir/new-bare/ 278 } 279 When call do_copy_move sub/bare subdir/new-bare 280 The status should be success 281 The output should be blank 282 The error should equal "$(result)" 283 End 284 285 It 'interactively re-enecrypts or copies files from a directory' 286 DECISION=interactive 287 ACTION=Copy 288 SCM_ACTION=scm_cp 289 YESNO_NEXT=n 290 yesno() { 291 mocklog yesno "$@" 292 ANSWER="${YESNO_NEXT}" 293 YESNO_NEXT=y 294 } 295 result() { 296 %text:expand 297 #|$ scm_begin 298 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare 299 #|$ yesno Reencrypt sub/bare/deep into subdir/new-bare/deep? 300 #|$ scm_cp sub/bare/deep.age subdir/new-bare/deep.age 301 #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub 302 #|$ yesno Reencrypt sub/bare/sub/deepest into subdir/new-bare/sub/deepest? 303 #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age 304 #|$ do_encrypt subdir/new-bare/sub/deepest.age 305 #|$ scm_add subdir/new-bare/sub/deepest.age 306 #|$ scm_commit Copy sub/bare/ to subdir/new-bare/ 307 } 308 When call do_copy_move sub/bare subdir/new-bare 309 The status should be success 310 The output should be blank 311 The error should equal "$(result)" 312 End 313 314 It 'reports a file masqueraded as a directory' 315 When run do_copy_move root.age/ subdir 316 The output should be blank 317 The error should equal 'Error: root.age/ is not in the password store.' 318 The status should equal 1 319 End 320 321 It 'reports non-existent source' 322 When run do_copy_move nonexistent subdir 323 The output should be blank 324 The error should equal 'Error: nonexistent is not in the password store.' 325 The status should equal 1 326 End 327 328 It 'cannot merge similarly-named directories' 329 When run do_copy_move sub/bare/sub / 330 The output should be blank 331 The error should equal 'Error: / already contains sub' 332 The status should equal 1 333 End 334 335 It 'cannot move a directory into a file' 336 When run do_copy_move sub/ root.age 337 The output should be blank 338 The error should equal 'Error: root.age is not a directory' 339 The status should equal 1 340 End 341 342 It 'cannot overwrite a directory with a file' 343 When run do_copy_move notes.txt subdir 344 The output should be blank 345 The error should equal 'Error: subdir already contains notes.txt/' 346 The status should equal 1 347 End 348 349 # Unreachable branches in do_copy_move_file, defensively implemented 350 It 'defensively avois re-encrypting' 351 DECISION=keep 352 result() { 353 %text 354 #|$ scm_mv root.age non-existent 355 } 356 When run do_copy_move_file root.age non-existent 357 The status should be success 358 The output should be blank 359 The error should equal "$(result)" 360 End 361 362 It 'defensively checks internal consistency of DECISION' 363 DECISION=garbage 364 When run do_copy_move_file root.age non-existent 365 The output should be blank 366 The error should equal 'Unexpected DECISION value "garbage"' 367 The status should equal 1 368 End 369 End 370 371 Specify 'do_decrypt' 372 AGE=age 373 age() { 374 mocklog age "$@" 375 %= 'cleartext' 376 } 377 378 IDENTITIES_FILE='/path/to/identity' 379 When call do_decrypt '/path/to/encrypted/file.age' 380 The status should be success 381 The output should equal 'cleartext' 382 The error should equal \ 383 '$ age -d -i /path/to/identity -- /path/to/encrypted/file.age' 384 End 385 386 Describe 'do_decrypt_gpg' 387 It 'uses gpg when agent is not available' 388 gpg() { mocklog gpg "$@"; } 389 unset GPG_AGENT_INFO 390 unset GPG 391 When call do_decrypt_gpg /path/to/encrypted/file.gpg 392 The status should be success 393 The error should equal \ 394 '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to -- /path/to/encrypted/file.gpg' 395 End 396 397 It 'uses gpg when agent is available' 398 gpg() { mocklog gpg "$@"; } 399 GPG_AGENT_INFO=agent-info 400 unset GPG 401 When call do_decrypt_gpg /path/to/encrypted/file.gpg 402 The status should be success 403 The error should equal \ 404 '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent -- /path/to/encrypted/file.gpg' 405 End 406 407 It 'uses gpg2' 408 gpg2() { mocklog gpg2 "$@"; } 409 unset GPG_AGENT_INFO 410 unset GPG 411 When call do_decrypt_gpg /path/to/encrypted/file.gpg 412 The status should be success 413 The error should equal \ 414 '$ gpg2 -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent -- /path/to/encrypted/file.gpg' 415 End 416 417 It 'uses user-provided command' 418 user_cmd() { mocklog user_cmd "$@"; } 419 unset GPG_AGENT_INFO 420 GPG=user_cmd 421 When call do_decrypt_gpg /path/to/encrypted/file.gpg 422 The status should be success 423 The error should equal \ 424 '$ user_cmd -d --quiet --yes --compress-algo=none --no-encrypt-to -- /path/to/encrypted/file.gpg' 425 End 426 427 It 'bails out when command cannot be guessed' 428 unset GPG 429 When run do_decrypt_gpg /path/to/encrypted/file.gpg 430 The error should equal 'GPG does not seem available' 431 The status should equal 1 432 End 433 End 434 435 Describe 'do_deinit' 436 DECISION=default 437 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 438 439 do_reencrypt_dir() { mocklog do_reencrypt_dir "$@"; } 440 scm_begin() { mocklog scm_begin "$@"; } 441 scm_commit() { mocklog scm_commit "$@"; } 442 scm_rm() { mocklog scm_rm "$@"; } 443 444 setup() { 445 @mkdir -p "${PREFIX}/empty" "${PREFIX}/sub" 446 %putsn data > "${PREFIX}/.age-recipients" 447 %putsn data > "${PREFIX}/sub/.age-recipients" 448 } 449 450 cleanup() { 451 @rm -rf "${PREFIX}" 452 } 453 454 BeforeEach setup 455 AfterEach cleanup 456 457 It 'de-initializes the whole store' 458 result() { 459 %text:expand 460 #|$ scm_begin 461 #|$ scm_rm .age-recipients 462 #|$ do_reencrypt_dir ${PREFIX}/ 463 #|$ scm_commit Deinitialize store root 464 } 465 When call do_deinit '' 466 The status should be success 467 The output should be blank 468 The error should equal "$(result)" 469 End 470 471 It 'de-initializes a subdirectory' 472 result() { 473 %text:expand 474 #|$ scm_begin 475 #|$ scm_rm sub/.age-recipients 476 #|$ do_reencrypt_dir ${PREFIX}/sub 477 #|$ scm_commit Deinitialize sub 478 } 479 When call do_deinit sub 480 The status should be success 481 The output should be blank 482 The error should equal "$(result)" 483 End 484 485 It 'can de-initialize without re-encryption' 486 DECISION=keep 487 result() { 488 %text:expand 489 #|$ scm_begin 490 #|$ scm_rm sub/.age-recipients 491 #|$ scm_commit Deinitialize sub 492 } 493 When call do_deinit sub 494 The status should be success 495 The output should be blank 496 The error should equal "$(result)" 497 End 498 499 It 'reports impossible de-initialization' 500 When run do_deinit non-existent 501 The output should be blank 502 The error should equal 'No existing recipient to remove at non-existent' 503 The status should equal 1 504 End 505 End 506 507 Describe 'do_delete' 508 DECISION=force 509 RECURSIVE=yes 510 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 511 512 dirname() { @dirname "$@"; } 513 scm_begin() { mocklog scm_begin "$@"; } 514 scm_commit() { mocklog scm_commit "$@"; } 515 scm_rm() { mocklog scm_rm "$@"; } 516 517 setup() { 518 @mkdir -p "${PREFIX}/empty" "${PREFIX}/sub" 519 %putsn data > "${PREFIX}/non-encrypted" 520 %putsn data > "${PREFIX}/sub.age" 521 %putsn data > "${PREFIX}/sub/entry.age" 522 } 523 524 cleanup() { 525 @rm -rf "${PREFIX}" 526 } 527 528 BeforeEach setup 529 AfterEach cleanup 530 531 It 'deletes a file after confirmation' 532 DECISION=default 533 yesno() { 534 mocklog yesno "$@" 535 ANSWER=y 536 } 537 result() { 538 %text:expand 539 #|$ yesno Are you sure you would like to delete sub/entry? 540 #|$ scm_begin 541 #|$ scm_rm sub/entry.age 542 #|$ scm_commit Remove sub/entry from store. 543 } 544 When call do_delete sub/entry 545 The status should be success 546 The output should be blank 547 The error should equal "$(result)" 548 End 549 550 It 'does not delete a file without confirmation' 551 DECISION=default 552 yesno() { 553 mocklog yesno "$@" 554 ANSWER=n 555 } 556 result() { 557 %text 558 #|$ yesno Are you sure you would like to delete sub/entry? 559 } 560 When call do_delete sub/entry 561 The status should be success 562 The output should be blank 563 The error should equal "$(result)" 564 End 565 566 It 'deletes a directory' 567 result() { 568 %text:expand 569 #|$ scm_begin 570 #|$ scm_rm empty/ 571 #|$ scm_commit Remove empty/ from store. 572 } 573 When call do_delete empty 574 The status should be success 575 The output should equal 'Removing empty/' 576 The error should equal "$(result)" 577 End 578 579 It 'deletes a file rather than a directory on ambiguity' 580 result() { 581 %text:expand 582 #|$ scm_begin 583 #|$ scm_rm sub.age 584 #|$ scm_commit Remove sub from store. 585 } 586 When call do_delete sub 587 The status should be success 588 The output should equal 'Removing sub' 589 The error should equal "$(result)" 590 End 591 592 It 'deletes a directory when explicitly asked' 593 result() { 594 %text:expand 595 #|$ scm_begin 596 #|$ scm_rm sub/ 597 #|$ scm_commit Remove sub/ from store. 598 } 599 When call do_delete sub/ 600 The status should be success 601 The output should equal 'Removing sub/' 602 The error should equal "$(result)" 603 End 604 605 It 'does not delete an explicit directory without RECURSIVE' 606 RECURSIVE=no 607 When run do_delete sub/ 608 The output should be blank 609 The error should equal 'Error: sub/ is a directory' 610 The status should equal 1 611 End 612 613 It 'does not delete an implicit directory without RECURSIVE' 614 RECURSIVE=no 615 When run do_delete empty 616 The output should be blank 617 The error should equal 'Error: empty/ is a directory' 618 The status should equal 1 619 End 620 621 It 'does not delete a non-encrypted file' 622 When run do_delete non-encrypted 623 The output should be blank 624 The error should equal \ 625 'Error: non-encrypted is not in the password store.' 626 The status should equal 1 627 End 628 629 It 'does not delete a file presented as a directory' 630 When run do_delete non-encrypted/ 631 The output should be blank 632 The error should equal \ 633 'Error: non-encrypted/ is not a directory.' 634 The status should equal 1 635 End 636 637 It 'reports a non-existent directory' 638 When run do_delete non-existent/ 639 The output should be blank 640 The error should equal \ 641 'Error: non-existent/ is not in the password store.' 642 The status should equal 1 643 End 644 End 645 646 Describe 'do_edit' 647 SECURE_TMPDIR="${SHELLSPEC_WORKDIR}/secure" 648 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 649 650 diff(){ @diff "$@"; } 651 652 do_decrypt() { 653 mocklog do_decrypt "$@" 654 %= foo 655 } 656 old_do_decrypt() { 657 mocklog do_decrypt "$@" 658 %text 659 #|old line 1 660 #|old line 2 661 } 662 663 do_encrypt() { 664 mocklog do_encrypt "$@" 665 @sed 's/^/> /' >&2 666 } 667 668 mktemp() { 669 mocklog mktemp "$@" 670 %putsn "$2" 671 } 672 673 rm(){ mocklog rm "$@"; @rm "$@"; } 674 675 setup() { 676 @mkdir -p "${PREFIX}" 677 %text > "${PREFIX}/existing.age" 678 #|encrypted data 679 @mkdir -p "${SECURE_TMPDIR}" 680 %text > "${SECURE_TMPDIR}/new-cleartext.txt" 681 #|new line 1 682 #|old line 2 683 #|new line 3 684 } 685 686 scm_add() { mocklog scm_add "$@"; } 687 scm_begin() { mocklog scm_begin "$@"; } 688 scm_commit() { mocklog scm_commit "$@"; } 689 690 cleanup() { 691 @rm -rf "${PREFIX}" "${SECURE_TMPDIR}" 692 } 693 694 BeforeEach setup 695 AfterEach cleanup 696 697 It 'creates a new file' 698 edit(){ @cat "${SECURE_TMPDIR}/new-cleartext.txt" >|"$1"; } 699 result() { 700 %text:expand 701 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 702 #|$ scm_begin 703 #|$ do_encrypt sub/new.age 704 #|> new line 1 705 #|> old line 2 706 #|> new line 3 707 #|$ scm_add sub/new.age 708 #|$ scm_commit Add password for sub/new using edit. 709 #|$ rm ${SECURE_TMPDIR}/XXXXXX-sub-new.txt 710 } 711 EDIT_CMD=edit 712 When call do_edit sub/new 713 The status should be success 714 The output should be blank 715 The error should equal "$(result)" 716 End 717 718 It 'handles NOT creating a new file' 719 result() { 720 %text:expand 721 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 722 #|$ scm_begin 723 } 724 EDIT_CMD=true 725 When call do_edit new 726 The status should be success 727 The output should equal 'New password for new not saved.' 728 The error should equal "$(result)" 729 End 730 731 It 'updates a file' 732 edit(){ @cat "${SECURE_TMPDIR}/new-cleartext.txt" >|"$1"; } 733 cat(){ mocklog cat "$@"; @cat "$@"; } 734 result() { 735 %text:expand 736 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 737 #|$ do_decrypt ${PREFIX}/existing.age 738 #|$ cat ${SECURE_TMPDIR}/XXXXXX-existing.txt 739 #|$ scm_begin 740 #|$ do_encrypt existing.age 741 #|> new line 1 742 #|> old line 2 743 #|> new line 3 744 #|$ scm_add existing.age 745 #|$ scm_commit Edit password for existing using edit. 746 #|$ rm ${SECURE_TMPDIR}/XXXXXX-existing.txt 747 } 748 EDIT_CMD=edit 749 When call do_edit existing 750 The status should be success 751 The output should be blank 752 The error should equal "$(result)" 753 End 754 755 It 'does not re-encrypt an unchanged file' 756 cat(){ mocklog cat "$@"; @cat "$@"; } 757 result() { 758 %text:expand 759 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 760 #|$ do_decrypt ${PREFIX}/existing.age 761 #|$ cat ${SECURE_TMPDIR}/XXXXXX-existing.txt 762 #|$ scm_begin 763 #|$ rm ${SECURE_TMPDIR}/XXXXXX-existing.txt 764 } 765 EDIT_CMD=true 766 When call do_edit existing 767 The status should be success 768 The output should equal 'Password for existing unchanged.' 769 The error should equal "$(result)" 770 End 771 772 It 'uses VISUAL on non-dumb terminal' 773 edit() { mocklog edit "$@"; } 774 VISUAL=edit 775 TERM=non-dumb 776 EDITOR=false 777 unset EDIT_CMD 778 result() { 779 %text:expand 780 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 781 #|$ scm_begin 782 #|$ edit ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 783 } 784 When call do_edit subdir/new 785 The status should be success 786 The output should equal 'New password for subdir/new not saved.' 787 The error should equal "$(result)" 788 End 789 790 It 'uses EDITOR on dumb terminal' 791 edit() { mocklog edit "$@"; } 792 VISUAL=false 793 TERM=dumb 794 EDITOR=edit 795 unset EDIT_CMD 796 result() { 797 %text:expand 798 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 799 #|$ scm_begin 800 #|$ edit ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 801 } 802 When call do_edit subdir/new 803 The status should be success 804 The output should equal 'New password for subdir/new not saved.' 805 The error should equal "$(result)" 806 End 807 808 It 'uses EDITOR without terminal' 809 edit() { mocklog edit "$@"; } 810 VISUAL=false 811 EDITOR=edit 812 unset EDIT_CMD 813 unset TERM 814 result() { 815 %text:expand 816 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 817 #|$ scm_begin 818 #|$ edit ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 819 } 820 When call do_edit subdir/new 821 The status should be success 822 The output should equal 'New password for subdir/new not saved.' 823 The error should equal "$(result)" 824 End 825 826 It 'uses EDITOR on non-dumb terminal without VISUAL' 827 edit() { mocklog edit "$@"; } 828 TERM=non-dumb 829 EDITOR=edit 830 unset VISUAL 831 unset EDIT_CMD 832 result() { 833 %text:expand 834 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 835 #|$ scm_begin 836 #|$ edit ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 837 } 838 When call do_edit subdir/new 839 The status should be success 840 The output should equal 'New password for subdir/new not saved.' 841 The error should equal "$(result)" 842 End 843 844 It 'falls back on vi without EDITOR nor VISUAL' 845 vi() { mocklog vi "$@"; } 846 unset EDITOR 847 unset VISUAL 848 unset EDIT_CMD 849 result() { 850 %text:expand 851 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 852 #|$ scm_begin 853 #|$ vi ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 854 } 855 When call do_edit subdir/new 856 The status should be success 857 The output should equal 'New password for subdir/new not saved.' 858 The error should equal "$(result)" 859 End 860 861 It 'reports EDIT_CMD exit code' 862 exit42() { mocklog editor "$@"; return 42; } 863 EDIT_CMD=exit42 864 result() { 865 %text:expand 866 #|$ mktemp -u ${SECURE_TMPDIR}/XXXXXX 867 #|$ scm_begin 868 #|$ editor ${SECURE_TMPDIR}/XXXXXX-subdir-new.txt 869 #|Editor "exit42" exited with code 42 870 } 871 When run do_edit subdir/new 872 The status should equal 42 873 The error should equal "$(result)" 874 End 875 End 876 877 Describe 'do_encrypt' 878 unset PASHAGE_RECIPIENTS_FILE 879 unset PASSAGE_RECIPIENTS_FILE 880 unset PASHAGE_RECIPIENTS 881 unset PASSAGE_RECIPIENTS 882 AGE=age 883 PREFIX=/prefix 884 dirname() { @dirname "$@"; } 885 886 age() { mocklog age "$@"; } 887 mkdir() { mocklog mkdir "$@"; } 888 889 setup() { 890 %= data >"${SHELLSPEC_WORKDIR}/existing-file" 891 } 892 BeforeAll 'setup' 893 894 It 'falls back on identity when there is no recipient' 895 OVERWRITE=yes 896 IDENTITIES_FILE='/path/to/identity' 897 set_LOCAL_RECIPIENT_FILE() { 898 LOCAL_RECIPIENT_FILE='' 899 } 900 result() { 901 %text 902 #|$ mkdir -p /prefix/encrypted 903 #|$ age -e -i /path/to/identity -o /prefix/encrypted/file.age 904 } 905 When run do_encrypt 'encrypted/file.age' 906 The status should be success 907 The error should equal "$(result)" 908 End 909 910 It 'overwrites existing file only once' 911 OVERWRITE=once 912 PREFIX="${SHELLSPEC_WORKDIR}" 913 set_LOCAL_RECIPIENT_FILE() { 914 LOCAL_RECIPIENT_FILE='/path/to/recipients' 915 } 916 preserve() { %preserve OVERWRITE; } 917 AfterRun 'preserve' 918 result() { 919 %text:expand 920 #|$ mkdir -p ${PREFIX} 921 #|$ age -e -R /path/to/recipients -o ${PREFIX}/existing-file 922 } 923 When run do_encrypt 'existing-file' 924 The status should be success 925 The error should equal "$(result)" 926 The variable OVERWRITE should equal no 927 End 928 929 It 'overwrites existing file when requested' 930 OVERWRITE=yes 931 PREFIX="${SHELLSPEC_WORKDIR}" 932 set_LOCAL_RECIPIENT_FILE() { 933 LOCAL_RECIPIENT_FILE='/path/to/recipients' 934 } 935 preserve() { %preserve OVERWRITE; } 936 AfterRun 'preserve' 937 result() { 938 %text:expand 939 #|$ mkdir -p ${PREFIX} 940 #|$ age -e -R /path/to/recipients -o ${PREFIX}/existing-file 941 } 942 When run do_encrypt 'existing-file' 943 The status should be success 944 The error should equal "$(result)" 945 The variable OVERWRITE should equal yes 946 End 947 948 It 'refuses to overwrite an existing file' 949 OVERWRITE=no 950 PREFIX="${SHELLSPEC_WORKDIR}" 951 set_LOCAL_RECIPIENT_FILE() { 952 LOCAL_RECIPIENT_FILE='/path/to/recipients' 953 } 954 When run do_encrypt 'existing-file' 955 The error should equal 'Refusing to overwite existing-file' 956 The status should equal 1 957 End 958 959 It 'uses PASSAGE_RECIPIENTS rather than LOCAL_RECIPIENT_FILE' 960 PASSAGE_RECIPIENTS='inline-recipient-1 inline-recipient-2' 961 set_LOCAL_RECIPIENT_FILE() { 962 LOCAL_RECIPIENT_FILE='shadowed' 963 } 964 OVERWRITE=yes 965 result() { 966 %text 967 #|$ mkdir -p /prefix/encrypted 968 #|$ age -e -r inline-recipient-1 -r inline-recipient-2 -o /prefix/encrypted/file.age 969 } 970 971 When call do_encrypt 'encrypted/file.age' 972 The status should be success 973 The error should equal "$(result)" 974 End 975 976 It 'uses PASHAGE_RECIPIENTS rather than PASSAGE_RECIPIENTS' 977 PASHAGE_RECIPIENTS='inline-recipient-1 inline-recipient-2' 978 PASSAGE_RECIPIENTS='shadowed' 979 set_LOCAL_RECIPIENT_FILE() { 980 LOCAL_RECIPIENT_FILE='shadowed' 981 } 982 OVERWRITE=yes 983 result() { 984 %text 985 #|$ mkdir -p /prefix/encrypted 986 #|$ age -e -r inline-recipient-1 -r inline-recipient-2 -o /prefix/encrypted/file.age 987 } 988 989 When call do_encrypt 'encrypted/file.age' 990 The status should be success 991 The error should equal "$(result)" 992 End 993 994 It 'uses PASSAGE_RECIPIENTS_FILE rather than PASHAGE_RECIPIENTS' 995 PASSAGE_RECIPIENTS_FILE='/path/to/recipients' 996 PASHAGE_RECIPIENTS='shadowed' 997 PASSAGE_RECIPIENTS='shadowed' 998 set_LOCAL_RECIPIENT_FILE() { 999 LOCAL_RECIPIENT_FILE='shadowed' 1000 } 1001 OVERWRITE=yes 1002 result() { 1003 %text 1004 #|$ mkdir -p /prefix/encrypted 1005 #|$ age -e -R /path/to/recipients -o /prefix/encrypted/file.age 1006 } 1007 1008 When call do_encrypt 'encrypted/file.age' 1009 The status should be success 1010 The error should equal "$(result)" 1011 End 1012 1013 It 'uses PASHAGE_RECIPIENTS_FILE rather than PASSAGE_RECIPIENTS_FILE' 1014 PASHAGE_RECIPIENTS_FILE='/path/to/recipients' 1015 PASSAGE_RECIPIENTS_FILE='shadowed' 1016 PASHAGE_RECIPIENTS='shadowed' 1017 PASSAGE_RECIPIENTS='shadowed' 1018 set_LOCAL_RECIPIENT_FILE() { 1019 LOCAL_RECIPIENT_FILE='shadowed' 1020 } 1021 OVERWRITE=yes 1022 result() { 1023 %text 1024 #|$ mkdir -p /prefix/encrypted 1025 #|$ age -e -R /path/to/recipients -o /prefix/encrypted/file.age 1026 } 1027 1028 When call do_encrypt 'encrypted/file.age' 1029 The status should be success 1030 The error should equal "$(result)" 1031 End 1032 End 1033 1034 Describe 'do_generate' 1035 DECISION=default 1036 MULTILINE=no 1037 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1038 SHOW=none 1039 1040 dd() { %- 0123456789 ; } 1041 dirname() { @dirname "$@"; } 1042 tr() { @tr "$@"; } 1043 1044 do_encrypt() { 1045 mocklog do_encrypt "$@" 1046 @sed 's/^/> /' >&2 1047 } 1048 1049 do_show() { 1050 mocklog do_show "$@" 1051 @sed 's/^/> /' >&2 1052 } 1053 1054 mkdir() { mocklog mkdir "$@"; } 1055 scm_add() { mocklog scm_add "$@"; } 1056 scm_begin() { mocklog scm_begin "$@"; } 1057 scm_commit() { mocklog scm_commit "$@"; } 1058 1059 setup() { 1060 @mkdir -p "${PREFIX}/suspicious.age" 1061 %putsn data >"${PREFIX}/existing.age" 1062 } 1063 1064 cleanup() { 1065 @rm -rf "${PREFIX}" 1066 } 1067 1068 BeforeEach setup 1069 AfterEach cleanup 1070 1071 It 'detects short reads' 1072 When run do_generate suspicious 12 '[:alnum:]' 1073 The output should be blank 1074 The error should equal \ 1075 'Error while generating password: 10/12 bytes read' 1076 The status should equal 1 1077 End 1078 1079 It 'aborts when a directory is in the way' 1080 result(){ 1081 %text:expand 1082 #|$ scm_begin 1083 #|$ mkdir -p -- ${PREFIX} 1084 #|Cannot replace directory suspicious.age 1085 } 1086 When run do_generate suspicious 10 '[:alnum:]' 1087 The output should be blank 1088 The error should equal "$(result)" 1089 The status should equal 1 1090 End 1091 1092 It 'generates a new file' 1093 result(){ 1094 %text:expand 1095 #|$ scm_begin 1096 #|$ mkdir -p -- ${PREFIX}/sub 1097 #|$ do_encrypt sub/new.age 1098 #|> 0123456789 1099 #|$ scm_add ${PREFIX}/sub/new.age 1100 #|$ scm_commit Add generated password for sub/new. 1101 #|$ do_show sub/new 1102 #|> 0123456789 1103 } 1104 When call do_generate sub/new 10 '[:alnum:]' 1105 The status should be success 1106 The output should be blank 1107 The error should equal "$(result)" 1108 End 1109 1110 It 'displays a title before text output' 1111 SHOW=text 1112 BOLD_TEXT='(B)' 1113 NORMAL_TEXT='(N)' 1114 UNDERLINE_TEXT='(U)' 1115 NO_UNDERLINE_TEXT='(!U)' 1116 result(){ 1117 %text:expand 1118 #|$ scm_begin 1119 #|$ mkdir -p -- ${PREFIX}/sub 1120 #|$ do_encrypt sub/new.age 1121 #|> 0123456789 1122 #|$ scm_add ${PREFIX}/sub/new.age 1123 #|$ scm_commit Add generated password for sub/new. 1124 #|$ do_show sub/new 1125 #|> 0123456789 1126 } 1127 When call do_generate sub/new 10 '[:alnum:]' 1128 The status should be success 1129 The output should equal \ 1130 '(B)The generated password for (U)sub/new(!U) is:(N)' 1131 The error should equal "$(result)" 1132 End 1133 1134 It 'overwrites an existing file when forced' 1135 MULTILINE=no 1136 OVERWRITE=yes 1137 result(){ 1138 %text:expand 1139 #|$ scm_begin 1140 #|$ mkdir -p -- ${PREFIX} 1141 #|$ do_encrypt existing.age 1142 #|> 0123456789 1143 #|$ scm_add ${PREFIX}/existing.age 1144 #|$ scm_commit Add generated password for existing. 1145 #|$ do_show existing 1146 #|> 0123456789 1147 } 1148 When call do_generate existing 10 '[:alnum:]' 1149 The status should be success 1150 The output should be blank 1151 The error should equal "$(result)" 1152 End 1153 1154 It 'overwrites an existing file after confirmation' 1155 MULTILINE=no 1156 OVERWRITE=no 1157 yesno() { 1158 mocklog yesno "$@"; 1159 ANSWER=y 1160 } 1161 result(){ 1162 %text:expand 1163 #|$ scm_begin 1164 #|$ mkdir -p -- ${PREFIX} 1165 #|$ yesno An entry already exists for existing. Overwrite it? 1166 #|$ do_encrypt existing.age 1167 #|> 0123456789 1168 #|$ scm_add ${PREFIX}/existing.age 1169 #|$ scm_commit Add generated password for existing. 1170 #|$ do_show existing 1171 #|> 0123456789 1172 } 1173 When call do_generate existing 10 '[:alnum:]' 1174 The status should be success 1175 The output should be blank 1176 The error should equal "$(result)" 1177 The variable OVERWRITE should equal 'once' 1178 End 1179 1180 It 'does not overwrite an existing file without confirmation' 1181 MULTILINE=no 1182 OVERWRITE=no 1183 yesno() { 1184 mocklog yesno "$@"; 1185 ANSWER=n 1186 } 1187 result(){ 1188 %text:expand 1189 #|$ scm_begin 1190 #|$ mkdir -p -- ${PREFIX} 1191 #|$ yesno An entry already exists for existing. Overwrite it? 1192 } 1193 When call do_generate existing 10 '[:alnum:]' 1194 The status should be success 1195 The output should be blank 1196 The error should equal "$(result)" 1197 End 1198 1199 It 'updates the first line of an existing file' 1200 MULTILINE=no 1201 OVERWRITE=reuse 1202 do_decrypt() { 1203 mocklog do_decrypt "$@" 1204 %text 1205 #|old password 1206 #|line 2 1207 #|line 3 1208 } 1209 mv() { mocklog mv "$@"; } 1210 result(){ 1211 %text:expand 1212 #|$ scm_begin 1213 #|$ mkdir -p -- ${PREFIX} 1214 #|$ do_decrypt ${PREFIX}/existing.age 1215 #|$ do_encrypt existing.age 1216 #|> 0123456789 1217 #|> line 2 1218 #|> line 3 1219 #|$ scm_add ${PREFIX}/existing.age 1220 #|$ scm_commit Replace generated password for existing. 1221 #|$ do_show existing 1222 #|> 0123456789 1223 } 1224 When call do_generate existing 10 '[:alnum:]' 1225 The status should be success 1226 The output should equal 'Decrypting previous secret for existing' 1227 The error should equal "$(result)" 1228 End 1229 1230 It 'updates the only line of an existing one-line file' 1231 MULTILINE=no 1232 OVERWRITE=reuse 1233 do_decrypt() { 1234 mocklog do_decrypt "$@" 1235 %text 1236 #|old password 1237 } 1238 mv() { mocklog mv "$@"; } 1239 result(){ 1240 %text:expand 1241 #|$ scm_begin 1242 #|$ mkdir -p -- ${PREFIX} 1243 #|$ do_decrypt ${PREFIX}/existing.age 1244 #|$ do_encrypt existing.age 1245 #|> 0123456789 1246 #|$ scm_add ${PREFIX}/existing.age 1247 #|$ scm_commit Replace generated password for existing. 1248 #|$ do_show existing 1249 #|> 0123456789 1250 } 1251 When call do_generate existing 10 '[:alnum:]' 1252 The status should be success 1253 The output should equal 'Decrypting previous secret for existing' 1254 The error should equal "$(result)" 1255 End 1256 1257 It 'saves the password after showing it and getting confirmation' 1258 DECISION=interactive 1259 yesno() { 1260 mocklog yesno "$@" 1261 ANSWER=y 1262 } 1263 result(){ 1264 %text:expand 1265 #|$ do_show sub/new 1266 #|> 0123456789 1267 #|$ yesno Save generated password for sub/new? 1268 #|$ scm_begin 1269 #|$ mkdir -p -- ${PREFIX}/sub 1270 #|$ do_encrypt sub/new.age 1271 #|> 0123456789 1272 #|$ scm_add ${PREFIX}/sub/new.age 1273 #|$ scm_commit Add generated password for sub/new. 1274 } 1275 When call do_generate sub/new 10 '[:alnum:]' 1276 The status should be success 1277 The output should be blank 1278 The error should equal "$(result)" 1279 End 1280 1281 It 'does not save the password after showing it and getting cancellation' 1282 DECISION=interactive 1283 yesno() { 1284 mocklog yesno "$@" 1285 ANSWER=n 1286 } 1287 result(){ 1288 %text:expand 1289 #|$ do_show sub/new 1290 #|> 0123456789 1291 #|$ yesno Save generated password for sub/new? 1292 } 1293 When call do_generate sub/new 10 '[:alnum:]' 1294 The status should be success 1295 The output should be blank 1296 The error should equal "$(result)" 1297 End 1298 1299 It 'accepts an extra line after the generated secret' 1300 MULTILINE=yes 1301 Data 'comment line' 1302 When call do_generate sub/new 10 '[:alnum:]' 1303 result(){ 1304 %text:expand 1305 #|$ scm_begin 1306 #|$ mkdir -p -- ${PREFIX}/sub 1307 #|$ do_encrypt sub/new.age 1308 #|> 0123456789 1309 #|> comment line 1310 #|$ scm_add ${PREFIX}/sub/new.age 1311 #|$ scm_commit Add generated password for sub/new. 1312 #|$ do_show sub/new 1313 #|> 0123456789 1314 } 1315 The status should be success 1316 The output should equal 'Enter extra secrets then Ctrl+D when finished:' 1317 The error should equal "$(result)" 1318 End 1319 1320 It 'accepts several lines after the generated secret' 1321 MULTILINE=yes 1322 OVERWRITE=no 1323 Data 1324 #|comment line 1325 #|end of secret 1326 End 1327 yesno() { 1328 mocklog yesno "$@" 1329 ANSWER=y 1330 } 1331 When call do_generate existing 10 '[:alnum:]' 1332 result(){ 1333 %text:expand 1334 #|$ scm_begin 1335 #|$ mkdir -p -- ${PREFIX} 1336 #|$ yesno An entry already exists for existing. Overwrite it? 1337 #|$ do_encrypt existing.age 1338 #|> 0123456789 1339 #|> comment line 1340 #|> end of secret 1341 #|$ scm_add ${PREFIX}/existing.age 1342 #|$ scm_commit Add generated password for existing. 1343 #|$ do_show existing 1344 #|> 0123456789 1345 } 1346 The status should be success 1347 The output should equal 'Enter extra secrets then Ctrl+D when finished:' 1348 The error should equal "$(result)" 1349 End 1350 1351 It 'does not asks for extra lines after refusing to overwrite' 1352 MULTILINE=yes 1353 OVERWRITE=no 1354 Data 'n' 1355 yesno() { 1356 mocklog yesno "$@" 1357 ANSWER=n 1358 } 1359 When call do_generate existing 10 '[:alnum:]' 1360 result(){ 1361 %text:expand 1362 #|$ scm_begin 1363 #|$ mkdir -p -- ${PREFIX} 1364 #|$ yesno An entry already exists for existing. Overwrite it? 1365 } 1366 The status should be success 1367 The output should be blank 1368 The error should equal "$(result)" 1369 End 1370 1371 It 'inserts extra lines after the in-place secrets' 1372 MULTILINE=yes 1373 OVERWRITE=reuse 1374 do_decrypt() { 1375 mocklog do_decrypt "$@" 1376 %text:expand 1377 #|old password 1378 #|old annotation 1379 #|end of $* 1380 } 1381 Data 1382 #|comment line 1383 #|end of secret 1384 End 1385 yesno() { 1386 mocklog yesno "$@" 1387 ANSWER=y 1388 } 1389 When call do_generate existing 10 '[:alnum:]' 1390 o_result(){ 1391 %text 1392 #|Decrypting previous secret for existing 1393 #|Enter extra secrets then Ctrl+D when finished: 1394 } 1395 result(){ 1396 %text:expand 1397 #|$ scm_begin 1398 #|$ mkdir -p -- ${PREFIX} 1399 #|$ do_decrypt ${PREFIX}/existing.age 1400 #|$ do_encrypt existing.age 1401 #|> 0123456789 1402 #|> old annotation 1403 #|> end of ${PREFIX}/existing.age 1404 #|> comment line 1405 #|> end of secret 1406 #|$ scm_add ${PREFIX}/existing.age 1407 #|$ scm_commit Replace generated password for existing. 1408 #|$ do_show existing 1409 #|> 0123456789 1410 } 1411 The status should be success 1412 The output should equal "$(o_result)" 1413 The error should equal "$(result)" 1414 End 1415 End 1416 1417 Describe 'do_grep' 1418 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1419 BLUE_TEXT='(B)' 1420 BOLD_TEXT='(G)' 1421 NORMAL_TEXT='(N)' 1422 1423 do_decrypt() { @cat "$1"; } 1424 grep() { @grep "$1"; } 1425 1426 setup() { 1427 @mkdir -p "${PREFIX}/subdir" 1428 %putsn data >"${PREFIX}/non-match.age" 1429 %text >"${PREFIX}/subdir/match.age" 1430 #|non-match 1431 #|other 1432 #|suffix 1433 } 1434 1435 cleanup() { 1436 @rm -rf "${PREFIX}" 1437 } 1438 1439 BeforeEach setup 1440 AfterEach cleanup 1441 1442 It 'outputs matching files' 1443 result(){ 1444 %text 1445 #|(B)subdir/(G)match(N): 1446 #|other 1447 } 1448 start_do_grep(){ 1449 ( cd "${PREFIX}" && do_grep '' "$@" ) 1450 } 1451 When call start_do_grep ot 1452 The status should be success 1453 The output should equal "$(result)" 1454 End 1455 1456 It 'outputs all the matching lines' 1457 result(){ 1458 %text 1459 #|(B)subdir/(G)match(N): 1460 #|other 1461 #|suffix 1462 } 1463 start_do_grep(){ 1464 ( cd "${PREFIX}" && do_grep '' "$@" ) 1465 } 1466 When call start_do_grep -vea 1467 The status should be success 1468 The output should equal "$(result)" 1469 End 1470 End 1471 1472 Describe 'do_init' 1473 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1474 DECISION=default 1475 OVERWRITE=no 1476 1477 do_reencrypt_dir() { mocklog do_reencrypt_dir "$@"; } 1478 mkdir() { mocklog mkdir "$@"; @mkdir "$@"; } 1479 scm_add() { mocklog scm_add "$@"; } 1480 scm_begin() { mocklog scm_begin "$@"; } 1481 scm_commit() { mocklog scm_commit "$@"; } 1482 1483 cleanup() { 1484 @rm -rf "${PREFIX}" 1485 } 1486 1487 AfterEach cleanup 1488 1489 It 'initializes the store' 1490 result() { 1491 %text:expand 1492 #|$ mkdir -p -- ${PREFIX} 1493 #|$ scm_begin 1494 #|$ scm_add .age-recipients 1495 #|$ do_reencrypt_dir ${PREFIX} 1496 #|$ scm_commit Set age recipients at store root 1497 } 1498 When call do_init '' identity 1499 The status should be success 1500 The output should equal 'Password store recipients set at store root' 1501 The error should equal "$(result)" 1502 The file "${PREFIX}/.age-recipients" should be exist 1503 The contents of the file "${PREFIX}/.age-recipients" should equal \ 1504 'identity' 1505 End 1506 1507 It 'initializes a subdirectory' 1508 result() { 1509 %text:expand 1510 #|$ mkdir -p -- ${PREFIX}/sub 1511 #|$ scm_begin 1512 #|$ scm_add sub/.age-recipients 1513 #|$ do_reencrypt_dir ${PREFIX}/sub 1514 #|$ scm_commit Set age recipients at sub 1515 } 1516 two_id() { 1517 %text 1518 #|identity 1 1519 #|identity 2 1520 } 1521 When call do_init sub 'identity 1' 'identity 2' 1522 The status should be success 1523 The output should equal 'Password store recipients set at sub' 1524 The error should equal "$(result)" 1525 The file "${PREFIX}/sub/.age-recipients" should be exist 1526 The contents of the file "${PREFIX}/sub/.age-recipients" should equal \ 1527 "$(two_id)" 1528 End 1529 1530 It 'can initialize without re-encryption' 1531 DECISION=keep 1532 result() { 1533 %text:expand 1534 #|$ mkdir -p -- ${PREFIX} 1535 #|$ scm_begin 1536 #|$ scm_add .age-recipients 1537 #|$ scm_commit Set age recipients at store root 1538 } 1539 When call do_init '' identity 1540 The status should be success 1541 The output should equal 'Password store recipients set at store root' 1542 The error should equal "$(result)" 1543 The file "${PREFIX}/.age-recipients" should be exist 1544 The contents of the file "${PREFIX}/.age-recipients" should equal \ 1545 'identity' 1546 End 1547 End 1548 1549 Describe 'do_insert' 1550 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1551 1552 do_encrypt() { 1553 mocklog do_encrypt "$@" 1554 @sed 's/^/> /' >&2 1555 } 1556 1557 dirname() { @dirname "$@"; } 1558 head() { @head "$@"; } 1559 1560 mkdir() { mocklog mkdir "$@"; } 1561 scm_add() { mocklog scm_add "$@"; } 1562 scm_begin() { mocklog scm_begin "$@"; } 1563 scm_commit() { mocklog scm_commit "$@"; } 1564 1565 setup() { 1566 @mkdir -p "${PREFIX}" 1567 %putsn data >"${PREFIX}/existing.age" 1568 } 1569 1570 cleanup() { 1571 @rm -rf "${PREFIX}" 1572 } 1573 1574 BeforeEach setup 1575 AfterEach cleanup 1576 1577 It 'inserts a single line from standard input' 1578 ECHO=yes 1579 MULTILINE=no 1580 OVERWRITE=yes 1581 result() { 1582 %text:expand 1583 #|$ scm_begin 1584 #|$ mkdir -p -- ${PREFIX}/subdir 1585 #|$ do_encrypt subdir/new.age 1586 #|> line 1 1587 #|$ scm_add subdir/new.age 1588 #|$ scm_commit Add given password for subdir/new to store. 1589 } 1590 Data 1591 #|line 1 1592 #|line 2 1593 #|line 3 1594 End 1595 1596 When call do_insert 'subdir/new' 1597 The status should be success 1598 The output should equal 'Enter password for subdir/new: ' 1599 The error should equal "$(result)" 1600 End 1601 1602 It 'inserts the standard input until the first blank line' 1603 MULTILINE=yes 1604 OVERWRITE=yes 1605 o_result() { %text 1606 #|Enter contents of subdir/new and 1607 #|press Ctrl+D or enter an empty line when finished: 1608 } 1609 result() { 1610 %text:expand 1611 #|$ scm_begin 1612 #|$ mkdir -p -- ${PREFIX}/subdir 1613 #|$ do_encrypt subdir/new.age 1614 #|> line 1 1615 #|> line 2 1616 #|$ scm_add subdir/new.age 1617 #|$ scm_commit Add given password for subdir/new to store. 1618 } 1619 Data 1620 #|line 1 1621 #|line 2 1622 #| 1623 #|line 3 1624 #|line 4 1625 End 1626 1627 When call do_insert 'subdir/new' 1628 The status should be success 1629 The output should equal "$(o_result)" 1630 The error should equal "$(result)" 1631 End 1632 1633 It 'inserts the whole standard input without blank line' 1634 MULTILINE=yes 1635 OVERWRITE=yes 1636 o_result() { %text 1637 #|Enter contents of subdir/new and 1638 #|press Ctrl+D or enter an empty line when finished: 1639 } 1640 result() { 1641 %text:expand 1642 #|$ scm_begin 1643 #|$ mkdir -p -- ${PREFIX}/subdir 1644 #|$ do_encrypt subdir/new.age 1645 #|> line 1 1646 #|> line 2 1647 #|> line 3 1648 #|$ scm_add subdir/new.age 1649 #|$ scm_commit Add given password for subdir/new to store. 1650 } 1651 Data 1652 #|line 1 1653 #|line 2 1654 #|line 3 1655 End 1656 1657 When call do_insert 'subdir/new' 1658 The status should be success 1659 The output should equal "$(o_result)" 1660 The error should equal "$(result)" 1661 End 1662 1663 It 'checks password confirmation before inserting it' 1664 ECHO=no 1665 MULTILINE=no 1666 OVERWRITE=yes 1667 stty() { true; } 1668 o_result() { 1669 %text | @sed 's/\$$//' 1670 #|Enter password for subdir/new: $ 1671 #|Retype password for subdir/new: $ 1672 #|Passwords don't match$ 1673 #|Enter password for subdir/new: $ 1674 #|Retype password for subdir/new: $ 1675 } 1676 e_result() { 1677 %text:expand 1678 #|$ scm_begin 1679 #|$ mkdir -p -- ${PREFIX}/subdir 1680 #|$ do_encrypt subdir/new.age 1681 #|> line 3 1682 #|$ scm_add subdir/new.age 1683 #|$ scm_commit Add given password for subdir/new to store. 1684 } 1685 Data 1686 #|line 1 1687 #|line 2 1688 #|line 3 1689 #|line 3 1690 #|line 5 1691 End 1692 1693 When call do_insert 'subdir/new' 1694 The status should be success 1695 The output should equal "$(o_result)" 1696 The error should equal "$(e_result)" 1697 End 1698 1699 It 'asks confirmation before overwriting' 1700 MULTILINE=yes 1701 OVERWRITE=no 1702 yesno() { 1703 mocklog yesno "$@" 1704 ANSWER=y 1705 } 1706 o_result() { %text 1707 #|Enter contents of existing and 1708 #|press Ctrl+D or enter an empty line when finished: 1709 } 1710 result() { 1711 %text:expand 1712 #|$ yesno An entry already exists for existing. Overwrite it? 1713 #|$ scm_begin 1714 #|$ mkdir -p -- ${PREFIX} 1715 #|$ do_encrypt existing.age 1716 #|> password 1717 #|$ scm_add existing.age 1718 #|$ scm_commit Add given password for existing to store. 1719 } 1720 Data 'password' 1721 1722 When call do_insert 'existing' 1723 The status should be success 1724 The output should equal "$(o_result)" 1725 The error should equal "$(result)" 1726 The variable OVERWRITE should equal once 1727 End 1728 1729 It 'does not overwrite without confirmation' 1730 MULTILINE=yes 1731 OVERWRITE=no 1732 yesno() { 1733 mocklog yesno "$@" 1734 ANSWER=n 1735 } 1736 result() { 1737 %text:expand 1738 #|$ yesno An entry already exists for existing. Overwrite it? 1739 } 1740 Data 'password' 1741 1742 When call do_insert 'existing' 1743 The status should be success 1744 The output should be blank 1745 The error should equal "$(result)" 1746 End 1747 1748 It 'does not ask confirmation before overwriting when forced' 1749 MULTILINE=yes 1750 OVERWRITE=yes 1751 yesno() { 1752 mocklog yesno "$@" 1753 ANSWER=y 1754 } 1755 o_result() { %text 1756 #|Enter contents of existing and 1757 #|press Ctrl+D or enter an empty line when finished: 1758 } 1759 result() { 1760 %text:expand 1761 #|$ scm_begin 1762 #|$ mkdir -p -- ${PREFIX} 1763 #|$ do_encrypt existing.age 1764 #|> password 1765 #|$ scm_add existing.age 1766 #|$ scm_commit Add given password for existing to store. 1767 } 1768 Data 'password' 1769 1770 When call do_insert 'existing' 1771 The status should be success 1772 The output should equal "$(o_result)" 1773 The error should equal "$(result)" 1774 End 1775 End 1776 1777 Describe 'do_list' 1778 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1779 1780 grep() { @grep "$@"; } 1781 1782 setup() { 1783 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 1784 %putsn data >"${PREFIX}/root.age" 1785 %putsn data >"${PREFIX}/subdir/hidden" 1786 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 1787 %putsn data >"${PREFIX}/other/lower.age" 1788 } 1789 1790 cleanup() { 1791 @rm -rf "${PREFIX}" 1792 } 1793 1794 BeforeEach setup 1795 AfterEach cleanup 1796 1797 It 'displays everything without a pattern' 1798 result() { 1799 %text 1800 #|other/lower 1801 #|root 1802 #|subdir/subsub/old 1803 } 1804 When call do_list '' 1805 The status should be success 1806 The output should equal "$(result)" 1807 End 1808 1809 It 'displays only matching files' 1810 result() { 1811 %text 1812 #|other/lower 1813 #|subdir/subsub/old 1814 } 1815 When call do_list '' -i L 1816 The status should be success 1817 The output should equal "$(result)" 1818 End 1819 1820 It 'does not display matching directories' 1821 When call do_list '' t 1822 The status should be success 1823 The output should equal 'root' 1824 End 1825 1826 It 'might not display anything' 1827 When call do_list '' z 1828 The status should be success 1829 The output should equal '' 1830 End 1831 End 1832 1833 Describe 'do_list_or_show' 1834 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1835 1836 do_decrypt() { 1837 mocklog do_decrypt "$@" 1838 %putsn data 1839 } 1840 1841 do_decrypt_gpg() { 1842 mocklog do_decrypt_gpg "$@" 1843 %putsn data 1844 } 1845 1846 do_list() { mocklog do_list "$@"; } 1847 1848 do_show() { 1849 @cat >/dev/null 1850 mocklog do_show "$@" 1851 } 1852 1853 do_tree() { mocklog do_tree "$@"; } 1854 1855 setup() { 1856 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 1857 %putsn data >"${PREFIX}/root.age" 1858 %putsn data >"${PREFIX}/subdir/hidden" 1859 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 1860 %putsn data >"${PREFIX}/other/lower.age" 1861 } 1862 1863 cleanup() { 1864 @rm -rf "${PREFIX}" 1865 } 1866 1867 BeforeEach setup 1868 AfterEach cleanup 1869 1870 It 'lists the whole store as a list' 1871 LIST_VIEW=yes 1872 When call do_list_or_show '' 1873 The status should be success 1874 The output should be blank 1875 The error should equal "$ do_list " 1876 End 1877 1878 It 'lists the whole store as a tree' 1879 LIST_VIEW=no 1880 When call do_list_or_show '' 1881 The status should be success 1882 The output should be blank 1883 The error should equal "$ do_tree ${PREFIX} Password Store" 1884 End 1885 1886 It 'shows a decrypted age file' 1887 result() { 1888 %text:expand 1889 #|$ do_decrypt ${PREFIX}/other/lower.age 1890 #|$ do_show other/lower 1891 } 1892 When call do_list_or_show 'other/lower' 1893 The status should be success 1894 The error should equal "$(result)" 1895 End 1896 1897 It 'shows a decrypted gpg file' 1898 result() { 1899 %text:expand 1900 #|$ do_decrypt_gpg ${PREFIX}/subdir/subsub/old.gpg 1901 #|$ do_show subdir/subsub/old 1902 } 1903 When call do_list_or_show 'subdir/subsub/old' 1904 The status should be success 1905 The error should equal "$(result)" 1906 End 1907 1908 It 'lists a subdirectory as a list' 1909 LIST_VIEW=yes 1910 When call do_list_or_show 'subdir' 1911 The status should be success 1912 The output should be blank 1913 The error should equal "$ do_list subdir" 1914 End 1915 1916 It 'lists a subdirectory as a tree' 1917 LIST_VIEW=no 1918 When call do_list_or_show 'subdir' 1919 The status should be success 1920 The output should be blank 1921 The error should equal "$ do_tree ${PREFIX}/subdir subdir" 1922 End 1923 1924 It 'does not show a non-encrypted file' 1925 When run do_list_or_show 'subdir/hidden' 1926 The output should be blank 1927 The error should equal \ 1928 'Error: subdir/hidden is not in the password store.' 1929 The status should equal 1 1930 End 1931 End 1932 1933 Describe 'do_reencrypt' 1934 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1935 DECISION=default 1936 1937 do_decrypt() { 1938 mocklog do_decrypt "$@" 1939 %putsn 'secret data' 1940 } 1941 1942 do_encrypt() { 1943 @cat >/dev/null 1944 mocklog do_encrypt "$@" 1945 } 1946 1947 mktemp() { %putsn "${2-$1}"; } 1948 mv() { mocklog mv "$@"; } 1949 scm_add() { mocklog scm_add "$@"; } 1950 scm_begin() { mocklog scm_begin "$@"; } 1951 scm_commit() { mocklog scm_commit "$@"; } 1952 1953 setup() { 1954 @mkdir -p "${PREFIX}/subdir/subsub" 1955 %putsn data >"${PREFIX}/root.age" 1956 %putsn data >"${PREFIX}/subdir/middle.age" 1957 %putsn data >"${PREFIX}/subdir/subsub/deep.age" 1958 } 1959 1960 cleanup() { 1961 @rm -rf "${PREFIX}" 1962 } 1963 1964 BeforeEach setup 1965 AfterEach cleanup 1966 1967 It 're-encrypts a single file' 1968 result() { 1969 %text:expand 1970 #|$ scm_begin 1971 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 1972 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 1973 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 1974 #|$ scm_add subdir/subsub/deep.age 1975 #|$ scm_commit Re-encrypt subdir/subsub/deep 1976 } 1977 When call do_reencrypt subdir/subsub/deep 1978 The status should be success 1979 The output should be blank 1980 The error should equal "$(result)" 1981 End 1982 1983 It 'recursively re-encrypts a directory' 1984 result() { 1985 %text:expand 1986 #|$ scm_begin 1987 #|$ do_decrypt ${PREFIX}/subdir/middle.age 1988 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 1989 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 1990 #|$ scm_add subdir/middle.age 1991 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 1992 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 1993 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 1994 #|$ scm_add subdir/subsub/deep.age 1995 #|$ scm_commit Re-encrypt subdir/ 1996 } 1997 When call do_reencrypt subdir/ 1998 The status should be success 1999 The output should be blank 2000 The error should equal "$(result)" 2001 End 2002 2003 It 'recursively re-encrypts the whole store as /' 2004 result() { 2005 %text:expand 2006 #|$ scm_begin 2007 #|$ do_decrypt ${PREFIX}/root.age 2008 #|$ do_encrypt root-XXXXXXXXX.age 2009 #|$ mv -f -- ${PREFIX}/root-XXXXXXXXX.age ${PREFIX}/root.age 2010 #|$ scm_add root.age 2011 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2012 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2013 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2014 #|$ scm_add subdir/middle.age 2015 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2016 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2017 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2018 #|$ scm_add subdir/subsub/deep.age 2019 #|$ scm_commit Re-encrypt / 2020 } 2021 When call do_reencrypt / 2022 The status should be success 2023 The output should be blank 2024 The error should equal "$(result)" 2025 End 2026 2027 It 'recursively re-encrypts the whole store as the empty string' 2028 result() { 2029 %text:expand 2030 #|$ scm_begin 2031 #|$ do_decrypt ${PREFIX}/root.age 2032 #|$ do_encrypt root-XXXXXXXXX.age 2033 #|$ mv -f -- ${PREFIX}/root-XXXXXXXXX.age ${PREFIX}/root.age 2034 #|$ scm_add root.age 2035 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2036 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2037 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2038 #|$ scm_add subdir/middle.age 2039 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2040 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2041 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2042 #|$ scm_add subdir/subsub/deep.age 2043 #|$ scm_commit Re-encrypt / 2044 } 2045 When call do_reencrypt '' 2046 The status should be success 2047 The output should be blank 2048 The error should equal "$(result)" 2049 End 2050 2051 It 'asks for confirmation before each file' 2052 DECISION=interactive 2053 YESNO_NEXT=n 2054 yesno() { 2055 mocklog yesno "$@" 2056 ANSWER="${YESNO_NEXT}" 2057 YESNO_NEXT=y 2058 } 2059 result() { 2060 %text:expand 2061 #|$ scm_begin 2062 #|$ yesno Re-encrypt subdir/middle? 2063 #|$ yesno Re-encrypt subdir/subsub/deep? 2064 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2065 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2066 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2067 #|$ scm_add subdir/subsub/deep.age 2068 #|$ scm_commit Re-encrypt subdir/ 2069 } 2070 When call do_reencrypt subdir 2071 The status should be success 2072 The output should be blank 2073 The error should equal "$(result)" 2074 End 2075 2076 It 'reports a non-existent directory' 2077 result() { 2078 %text 2079 #|$ scm_begin 2080 #|Error: non-existent/ is not in the password store. 2081 } 2082 When run do_reencrypt non-existent/ 2083 The output should be blank 2084 The error should equal "$(result)" 2085 The status should equal 1 2086 End 2087 2088 It 'reports a non-existent file' 2089 result() { 2090 %text 2091 #|$ scm_begin 2092 #|Error: non-existent is not in the password store. 2093 } 2094 When run do_reencrypt non-existent 2095 The output should be blank 2096 The error should equal "$(result)" 2097 The status should equal 1 2098 End 2099 End 2100 2101 Describe 'do_show' 2102 cleartext(){ 2103 %text 2104 #|password line 2105 #|extra line 1 2106 #|extra line 2 2107 } 2108 2109 It 'shows a secret on standard output' 2110 cat() { @cat; } 2111 Data cleartext 2112 SHOW=text 2113 When call do_show 2114 The status should be success 2115 The output should equal "$(cleartext)" 2116 End 2117 2118 It 'pastes a secret into the clipboard' 2119 head() { @head "$@"; } 2120 tail() { @tail "$@"; } 2121 tr() { @tr "$@"; } 2122 platform_clip() { @cat >&2; } 2123 Data cleartext 2124 SELECTED_LINE=1 2125 SHOW=clip 2126 When call do_show title 2127 The status should be success 2128 The output should be blank 2129 The error should equal 'password line' 2130 End 2131 2132 It 'shows a secret as a QR-code' 2133 head() { @head "$@"; } 2134 tail() { @tail "$@"; } 2135 tr() { @tr "$@"; } 2136 platform_qrcode() { @cat >&2; } 2137 Data cleartext 2138 SELECTED_LINE=1 2139 SHOW=qrcode 2140 When call do_show title 2141 The status should be success 2142 The output should be blank 2143 The error should equal 'password line' 2144 End 2145 2146 It 'aborts on unexpected SHOW' 2147 SHOW=bad 2148 Data cleartext 2149 When run do_show title 2150 The output should be blank 2151 The error should equal 'Unexpected SHOW value "bad"' 2152 The status should equal 1 2153 End 2154 End 2155 2156 Describe 'do_tree' 2157 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 2158 BLUE_TEXT='(B)' 2159 NORMAL_TEXT='(N)' 2160 RED_TEXT='(R)' 2161 TREE_T='T_' 2162 TREE_L='L_' 2163 TREE_I='I_' 2164 TREE__='__' 2165 2166 grep() { @grep "$@"; } 2167 2168 setup() { 2169 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 2170 %putsn data >"${PREFIX}/root.age" 2171 %putsn data >"${PREFIX}/subdir/hidden" 2172 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 2173 %putsn data >"${PREFIX}/other/lower.age" 2174 } 2175 2176 cleanup() { 2177 @rm -rf "${PREFIX}" 2178 } 2179 2180 BeforeEach setup 2181 AfterEach cleanup 2182 2183 It 'displays everything without a pattern' 2184 result() { 2185 %text 2186 #|Title 2187 #|T_(B)empty(N) 2188 #|T_(B)other(N) 2189 #|I_L_lower 2190 #|T_root 2191 #|L_(B)subdir(N) 2192 #|__L_(B)subsub(N) 2193 #|____L_(R)old(N) 2194 } 2195 When call do_tree "${PREFIX}" 'Title' 2196 The status should be success 2197 The output should equal "$(result)" 2198 End 2199 2200 It 'displays matching files and their non-matching parents' 2201 result() { 2202 %text 2203 #|Title 2204 #|T_(B)other(N) 2205 #|I_L_lower 2206 #|L_(B)subdir(N) 2207 #|__L_(B)subsub(N) 2208 #|____L_(R)old(N) 2209 } 2210 When call do_tree "${PREFIX}" 'Title' -i L 2211 The status should be success 2212 The output should equal "$(result)" 2213 End 2214 2215 It 'does not display matching directories' 2216 result() { 2217 %text 2218 #|Title 2219 #|L_root 2220 } 2221 When call do_tree "${PREFIX}" 'Title' t 2222 The status should be success 2223 The output should equal "$(result)" 2224 End 2225 2226 It 'might not display anything' 2227 When call do_tree "${PREFIX}" 'Title' z 2228 The status should be success 2229 The output should equal '' 2230 End 2231 2232 It 'does not display an empty title' 2233 When call do_tree "${PREFIX}" '' t 2234 The status should be success 2235 The output should equal 'L_root' 2236 End 2237 End 2238 End