action_spec.sh (67911B)
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}/empty" "${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 When call do_grep "${PREFIX}" ot 1449 The status should be success 1450 The output should equal "$(result)" 1451 The error should be blank 1452 End 1453 1454 It 'outputs all the matching lines' 1455 result(){ 1456 %text 1457 #|(B)subdir/(G)match(N): 1458 #|other 1459 #|suffix 1460 } 1461 When call do_grep "${PREFIX}" -vea 1462 The status should be success 1463 The output should equal "$(result)" 1464 The error should be blank 1465 End 1466 1467 It 'outputs nothing without matches' 1468 When call do_grep "${PREFIX}" z 1469 The status should be success 1470 The output should be blank 1471 The error should be blank 1472 End 1473 1474 It 'correctly displays matches in the root' 1475 result(){ 1476 %text 1477 #|(B)(G)non-match(N): 1478 #|data 1479 #|(B)subdir/(G)match(N): 1480 #|non-match 1481 } 1482 When call do_grep "${PREFIX}" a 1483 The status should be success 1484 The output should equal "$(result)" 1485 The error should be blank 1486 End 1487 End 1488 1489 Describe 'do_init' 1490 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1491 DECISION=default 1492 OVERWRITE=no 1493 1494 do_reencrypt_dir() { mocklog do_reencrypt_dir "$@"; } 1495 mkdir() { mocklog mkdir "$@"; @mkdir "$@"; } 1496 scm_add() { mocklog scm_add "$@"; } 1497 scm_begin() { mocklog scm_begin "$@"; } 1498 scm_commit() { mocklog scm_commit "$@"; } 1499 1500 cleanup() { 1501 @rm -rf "${PREFIX}" 1502 } 1503 1504 AfterEach cleanup 1505 1506 It 'initializes the store' 1507 result() { 1508 %text:expand 1509 #|$ mkdir -p -- ${PREFIX} 1510 #|$ scm_begin 1511 #|$ scm_add .age-recipients 1512 #|$ do_reencrypt_dir ${PREFIX} 1513 #|$ scm_commit Set age recipients at store root 1514 } 1515 When call do_init '' identity 1516 The status should be success 1517 The output should equal 'Password store recipients set at store root' 1518 The error should equal "$(result)" 1519 The file "${PREFIX}/.age-recipients" should be exist 1520 The contents of the file "${PREFIX}/.age-recipients" should equal \ 1521 'identity' 1522 End 1523 1524 It 'initializes a subdirectory' 1525 result() { 1526 %text:expand 1527 #|$ mkdir -p -- ${PREFIX}/sub 1528 #|$ scm_begin 1529 #|$ scm_add sub/.age-recipients 1530 #|$ do_reencrypt_dir ${PREFIX}/sub 1531 #|$ scm_commit Set age recipients at sub 1532 } 1533 two_id() { 1534 %text 1535 #|identity 1 1536 #|identity 2 1537 } 1538 When call do_init sub 'identity 1' 'identity 2' 1539 The status should be success 1540 The output should equal 'Password store recipients set at sub' 1541 The error should equal "$(result)" 1542 The file "${PREFIX}/sub/.age-recipients" should be exist 1543 The contents of the file "${PREFIX}/sub/.age-recipients" should equal \ 1544 "$(two_id)" 1545 End 1546 1547 It 'can initialize without re-encryption' 1548 DECISION=keep 1549 result() { 1550 %text:expand 1551 #|$ mkdir -p -- ${PREFIX} 1552 #|$ scm_begin 1553 #|$ scm_add .age-recipients 1554 #|$ scm_commit Set age recipients at store root 1555 } 1556 When call do_init '' identity 1557 The status should be success 1558 The output should equal 'Password store recipients set at store root' 1559 The error should equal "$(result)" 1560 The file "${PREFIX}/.age-recipients" should be exist 1561 The contents of the file "${PREFIX}/.age-recipients" should equal \ 1562 'identity' 1563 End 1564 End 1565 1566 Describe 'do_insert' 1567 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1568 1569 do_encrypt() { 1570 mocklog do_encrypt "$@" 1571 @sed 's/^/> /' >&2 1572 } 1573 1574 dirname() { @dirname "$@"; } 1575 head() { @head "$@"; } 1576 1577 mkdir() { mocklog mkdir "$@"; } 1578 scm_add() { mocklog scm_add "$@"; } 1579 scm_begin() { mocklog scm_begin "$@"; } 1580 scm_commit() { mocklog scm_commit "$@"; } 1581 1582 setup() { 1583 @mkdir -p "${PREFIX}" 1584 %putsn data >"${PREFIX}/existing.age" 1585 } 1586 1587 cleanup() { 1588 @rm -rf "${PREFIX}" 1589 } 1590 1591 BeforeEach setup 1592 AfterEach cleanup 1593 1594 It 'inserts a single line from standard input' 1595 ECHO=yes 1596 MULTILINE=no 1597 OVERWRITE=yes 1598 result() { 1599 %text:expand 1600 #|$ scm_begin 1601 #|$ mkdir -p -- ${PREFIX}/subdir 1602 #|$ do_encrypt subdir/new.age 1603 #|> line 1 1604 #|$ scm_add subdir/new.age 1605 #|$ scm_commit Add given password for subdir/new to store. 1606 } 1607 Data 1608 #|line 1 1609 #|line 2 1610 #|line 3 1611 End 1612 1613 When call do_insert 'subdir/new' 1614 The status should be success 1615 The output should equal 'Enter password for subdir/new: ' 1616 The error should equal "$(result)" 1617 End 1618 1619 It 'inserts the standard input until the first blank line' 1620 MULTILINE=yes 1621 OVERWRITE=yes 1622 o_result() { %text 1623 #|Enter contents of subdir/new and 1624 #|press Ctrl+D or enter an empty line when finished: 1625 } 1626 result() { 1627 %text:expand 1628 #|$ scm_begin 1629 #|$ mkdir -p -- ${PREFIX}/subdir 1630 #|$ do_encrypt subdir/new.age 1631 #|> line 1 1632 #|> line 2 1633 #|$ scm_add subdir/new.age 1634 #|$ scm_commit Add given password for subdir/new to store. 1635 } 1636 Data 1637 #|line 1 1638 #|line 2 1639 #| 1640 #|line 3 1641 #|line 4 1642 End 1643 1644 When call do_insert 'subdir/new' 1645 The status should be success 1646 The output should equal "$(o_result)" 1647 The error should equal "$(result)" 1648 End 1649 1650 It 'inserts the whole standard input without blank line' 1651 MULTILINE=yes 1652 OVERWRITE=yes 1653 o_result() { %text 1654 #|Enter contents of subdir/new and 1655 #|press Ctrl+D or enter an empty line when finished: 1656 } 1657 result() { 1658 %text:expand 1659 #|$ scm_begin 1660 #|$ mkdir -p -- ${PREFIX}/subdir 1661 #|$ do_encrypt subdir/new.age 1662 #|> line 1 1663 #|> line 2 1664 #|> line 3 1665 #|$ scm_add subdir/new.age 1666 #|$ scm_commit Add given password for subdir/new to store. 1667 } 1668 Data 1669 #|line 1 1670 #|line 2 1671 #|line 3 1672 End 1673 1674 When call do_insert 'subdir/new' 1675 The status should be success 1676 The output should equal "$(o_result)" 1677 The error should equal "$(result)" 1678 End 1679 1680 It 'checks password confirmation before inserting it' 1681 ECHO=no 1682 MULTILINE=no 1683 OVERWRITE=yes 1684 stty() { true; } 1685 o_result() { 1686 %text | @sed 's/\$$//' 1687 #|Enter password for subdir/new: $ 1688 #|Retype password for subdir/new: $ 1689 #|Passwords don't match$ 1690 #|Enter password for subdir/new: $ 1691 #|Retype password for subdir/new: $ 1692 } 1693 e_result() { 1694 %text:expand 1695 #|$ scm_begin 1696 #|$ mkdir -p -- ${PREFIX}/subdir 1697 #|$ do_encrypt subdir/new.age 1698 #|> line 3 1699 #|$ scm_add subdir/new.age 1700 #|$ scm_commit Add given password for subdir/new to store. 1701 } 1702 Data 1703 #|line 1 1704 #|line 2 1705 #|line 3 1706 #|line 3 1707 #|line 5 1708 End 1709 1710 When call do_insert 'subdir/new' 1711 The status should be success 1712 The output should equal "$(o_result)" 1713 The error should equal "$(e_result)" 1714 End 1715 1716 It 'asks confirmation before overwriting' 1717 MULTILINE=yes 1718 OVERWRITE=no 1719 yesno() { 1720 mocklog yesno "$@" 1721 ANSWER=y 1722 } 1723 o_result() { %text 1724 #|Enter contents of existing and 1725 #|press Ctrl+D or enter an empty line when finished: 1726 } 1727 result() { 1728 %text:expand 1729 #|$ yesno An entry already exists for existing. Overwrite it? 1730 #|$ scm_begin 1731 #|$ mkdir -p -- ${PREFIX} 1732 #|$ do_encrypt existing.age 1733 #|> password 1734 #|$ scm_add existing.age 1735 #|$ scm_commit Add given password for existing to store. 1736 } 1737 Data 'password' 1738 1739 When call do_insert 'existing' 1740 The status should be success 1741 The output should equal "$(o_result)" 1742 The error should equal "$(result)" 1743 The variable OVERWRITE should equal once 1744 End 1745 1746 It 'does not overwrite without confirmation' 1747 MULTILINE=yes 1748 OVERWRITE=no 1749 yesno() { 1750 mocklog yesno "$@" 1751 ANSWER=n 1752 } 1753 result() { 1754 %text:expand 1755 #|$ yesno An entry already exists for existing. Overwrite it? 1756 } 1757 Data 'password' 1758 1759 When call do_insert 'existing' 1760 The status should be success 1761 The output should be blank 1762 The error should equal "$(result)" 1763 End 1764 1765 It 'does not ask confirmation before overwriting when forced' 1766 MULTILINE=yes 1767 OVERWRITE=yes 1768 yesno() { 1769 mocklog yesno "$@" 1770 ANSWER=y 1771 } 1772 o_result() { %text 1773 #|Enter contents of existing and 1774 #|press Ctrl+D or enter an empty line when finished: 1775 } 1776 result() { 1777 %text:expand 1778 #|$ scm_begin 1779 #|$ mkdir -p -- ${PREFIX} 1780 #|$ do_encrypt existing.age 1781 #|> password 1782 #|$ scm_add existing.age 1783 #|$ scm_commit Add given password for existing to store. 1784 } 1785 Data 'password' 1786 1787 When call do_insert 'existing' 1788 The status should be success 1789 The output should equal "$(o_result)" 1790 The error should equal "$(result)" 1791 End 1792 End 1793 1794 Describe 'do_list' 1795 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1796 1797 grep() { @grep "$@"; } 1798 1799 setup() { 1800 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 1801 %putsn data >"${PREFIX}/root.age" 1802 %putsn data >"${PREFIX}/subdir/hidden" 1803 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 1804 %putsn data >"${PREFIX}/other/lower.age" 1805 %putsn data >"${PREFIX}/other/lower.gpg" 1806 %putsn data >"${PREFIX}/subdir.gpg" 1807 } 1808 1809 cleanup() { 1810 @rm -rf "${PREFIX}" 1811 } 1812 1813 BeforeEach setup 1814 AfterEach cleanup 1815 1816 It 'displays everything without a pattern' 1817 result() { 1818 %text 1819 #|other/lower 1820 #|other/lower.gpg 1821 #|root 1822 #|subdir/subsub/old 1823 #|subdir.gpg 1824 } 1825 BEGIN_GPG_NAME='' 1826 END_GPG_NAME='' 1827 LIST_EMPTY='no' 1828 When call do_list '' 1829 The status should be success 1830 The output should equal "$(result)" 1831 End 1832 1833 It 'displays everything, including empty directories' 1834 result() { 1835 %text 1836 #|empty/ 1837 #|other/lower 1838 #|other/lower.gpg 1839 #|root 1840 #|subdir/subsub/old 1841 #|subdir.gpg 1842 } 1843 BEGIN_GPG_NAME='' 1844 END_GPG_NAME='' 1845 LIST_EMPTY=yes 1846 When call do_list '' 1847 The status should be success 1848 The output should equal "$(result)" 1849 End 1850 1851 It 'displays only matching files' 1852 result() { 1853 %text 1854 #|other/lower 1855 #|other/lower.gpg 1856 #|subdir/subsub/old 1857 } 1858 BEGIN_GPG_NAME='' 1859 END_GPG_NAME='' 1860 LIST_EMPTY='no' 1861 When call do_list '' -i L 1862 The status should be success 1863 The output should equal "$(result)" 1864 End 1865 1866 It 'does not display matching directories' 1867 BEGIN_GPG_NAME='' 1868 END_GPG_NAME='' 1869 LIST_EMPTY='no' 1870 When call do_list '' t 1871 The status should be success 1872 The output should equal 'root' 1873 End 1874 1875 It 'might not display anything' 1876 BEGIN_GPG_NAME='' 1877 END_GPG_NAME='' 1878 LIST_EMPTY='no' 1879 When call do_list '' z 1880 The status should be success 1881 The output should equal '' 1882 End 1883 1884 It 'marks GPG names' 1885 result() { 1886 %text 1887 #|other/lower 1888 #|other/[lower.gpg] 1889 #|root 1890 #|subdir/subsub/[old] 1891 #|[subdir.gpg] 1892 } 1893 BEGIN_GPG_NAME='[' 1894 END_GPG_NAME=']' 1895 LIST_EMPTY='no' 1896 When call do_list '' 1897 The status should be success 1898 The output should equal "$(result)" 1899 End 1900 End 1901 1902 Describe 'do_list_or_show' 1903 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 1904 1905 do_decrypt() { 1906 mocklog do_decrypt "$@" 1907 %putsn data 1908 } 1909 1910 do_decrypt_gpg() { 1911 mocklog do_decrypt_gpg "$@" 1912 %putsn data 1913 } 1914 1915 do_list() { mocklog do_list "$@"; } 1916 1917 do_show() { 1918 @cat >/dev/null 1919 mocklog do_show "$@" 1920 } 1921 1922 do_tree() { mocklog do_tree "$@"; } 1923 1924 setup() { 1925 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 1926 %putsn data >"${PREFIX}/root.age" 1927 %putsn data >"${PREFIX}/subdir/hidden" 1928 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 1929 %putsn data >"${PREFIX}/other/lower.age" 1930 } 1931 1932 cleanup() { 1933 @rm -rf "${PREFIX}" 1934 } 1935 1936 BeforeEach setup 1937 AfterEach cleanup 1938 1939 It 'lists the whole store as a list' 1940 LIST_VIEW=yes 1941 When call do_list_or_show '' 1942 The status should be success 1943 The output should be blank 1944 The error should equal "$ do_list " 1945 End 1946 1947 It 'lists the whole store as a tree' 1948 LIST_VIEW=no 1949 When call do_list_or_show '' 1950 The status should be success 1951 The output should equal 'Password Store' 1952 The error should equal '$ do_tree ' 1953 End 1954 1955 It 'shows a decrypted age file' 1956 result() { 1957 %text:expand 1958 #|$ do_decrypt ${PREFIX}/other/lower.age 1959 #|$ do_show other/lower 1960 } 1961 When call do_list_or_show 'other/lower' 1962 The status should be success 1963 The error should equal "$(result)" 1964 End 1965 1966 It 'shows a decrypted gpg file' 1967 result() { 1968 %text:expand 1969 #|$ do_decrypt_gpg ${PREFIX}/subdir/subsub/old.gpg 1970 #|$ do_show subdir/subsub/old 1971 } 1972 When call do_list_or_show 'subdir/subsub/old' 1973 The status should be success 1974 The error should equal "$(result)" 1975 End 1976 1977 It 'lists a subdirectory as a list' 1978 LIST_VIEW=yes 1979 When call do_list_or_show 'subdir' 1980 The status should be success 1981 The output should be blank 1982 The error should equal "$ do_list subdir" 1983 End 1984 1985 It 'lists a subdirectory as a tree' 1986 LIST_VIEW=no 1987 When call do_list_or_show 'subdir' 1988 The status should be success 1989 The output should equal 'subdir' 1990 The error should equal '$ do_tree subdir' 1991 End 1992 1993 It 'does not show a non-encrypted file' 1994 When run do_list_or_show 'subdir/hidden' 1995 The output should be blank 1996 The error should equal \ 1997 'Error: subdir/hidden is not in the password store.' 1998 The status should equal 1 1999 End 2000 End 2001 2002 Describe 'do_reencrypt' 2003 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 2004 2005 do_decrypt() { 2006 mocklog do_decrypt "$@" 2007 %putsn 'secret data' 2008 } 2009 2010 do_encrypt() { 2011 @cat >/dev/null 2012 mocklog do_encrypt "$@" 2013 } 2014 2015 mktemp() { %putsn "${2-$1}"; } 2016 mv() { mocklog mv "$@"; } 2017 scm_add() { mocklog scm_add "$@"; } 2018 scm_begin() { mocklog scm_begin "$@"; } 2019 scm_commit() { mocklog scm_commit "$@"; } 2020 2021 setup() { 2022 @mkdir -p "${PREFIX}/subdir/other" "${PREFIX}/subdir/subsub" 2023 %putsn id >"${PREFIX}/subdir/other/.age-recipients" 2024 %putsn data >"${PREFIX}/root.age" 2025 %putsn data >"${PREFIX}/subdir/middle.age" 2026 %putsn data >"${PREFIX}/subdir/other/unrelated.age" 2027 %putsn data >"${PREFIX}/subdir/subsub/deep.age" 2028 } 2029 2030 cleanup() { 2031 @rm -rf "${PREFIX}" 2032 } 2033 2034 BeforeEach setup 2035 AfterEach cleanup 2036 2037 It 're-encrypts a single file' 2038 DECISION=default 2039 result() { 2040 %text:expand 2041 #|$ scm_begin 2042 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2043 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2044 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2045 #|$ scm_add subdir/subsub/deep.age 2046 #|$ scm_commit Re-encrypt subdir/subsub/deep 2047 } 2048 When call do_reencrypt subdir/subsub/deep 2049 The status should be success 2050 The output should be blank 2051 The error should equal "$(result)" 2052 End 2053 2054 It 'recursively re-encrypts a directory' 2055 DECISION=default 2056 RECURSIVE=no 2057 result() { 2058 %text:expand 2059 #|$ scm_begin 2060 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2061 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2062 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2063 #|$ scm_add subdir/middle.age 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 'recursively and deeply re-encrypts a directory' 2077 DECISION=default 2078 RECURSIVE=yes 2079 result() { 2080 %text:expand 2081 #|$ scm_begin 2082 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2083 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2084 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2085 #|$ scm_add subdir/middle.age 2086 #|$ do_decrypt ${PREFIX}/subdir/other/unrelated.age 2087 #|$ do_encrypt subdir/other/unrelated-XXXXXXXXX.age 2088 #|$ mv -f -- ${PREFIX}/subdir/other/unrelated-XXXXXXXXX.age ${PREFIX}/subdir/other/unrelated.age 2089 #|$ scm_add subdir/other/unrelated.age 2090 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2091 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2092 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2093 #|$ scm_add subdir/subsub/deep.age 2094 #|$ scm_commit Re-encrypt subdir/ 2095 } 2096 When call do_reencrypt subdir/ 2097 The status should be success 2098 The output should be blank 2099 The error should equal "$(result)" 2100 End 2101 2102 It 'recursively re-encrypts the whole store as /' 2103 DECISION=default 2104 RECURSIVE=no 2105 result() { 2106 %text:expand 2107 #|$ scm_begin 2108 #|$ do_decrypt ${PREFIX}/root.age 2109 #|$ do_encrypt root-XXXXXXXXX.age 2110 #|$ mv -f -- ${PREFIX}/root-XXXXXXXXX.age ${PREFIX}/root.age 2111 #|$ scm_add root.age 2112 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2113 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2114 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2115 #|$ scm_add subdir/middle.age 2116 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2117 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2118 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2119 #|$ scm_add subdir/subsub/deep.age 2120 #|$ scm_commit Re-encrypt / 2121 } 2122 When call do_reencrypt / 2123 The status should be success 2124 The output should be blank 2125 The error should equal "$(result)" 2126 End 2127 2128 It 'recursively re-encrypts the whole store as the empty string' 2129 DECISION=default 2130 RECURSIVE=no 2131 result() { 2132 %text:expand 2133 #|$ scm_begin 2134 #|$ do_decrypt ${PREFIX}/root.age 2135 #|$ do_encrypt root-XXXXXXXXX.age 2136 #|$ mv -f -- ${PREFIX}/root-XXXXXXXXX.age ${PREFIX}/root.age 2137 #|$ scm_add root.age 2138 #|$ do_decrypt ${PREFIX}/subdir/middle.age 2139 #|$ do_encrypt subdir/middle-XXXXXXXXX.age 2140 #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age 2141 #|$ scm_add subdir/middle.age 2142 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2143 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2144 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2145 #|$ scm_add subdir/subsub/deep.age 2146 #|$ scm_commit Re-encrypt / 2147 } 2148 When call do_reencrypt '' 2149 The status should be success 2150 The output should be blank 2151 The error should equal "$(result)" 2152 End 2153 2154 It 'asks for confirmation before each file' 2155 DECISION=interactive 2156 RECURSIVE=no 2157 YESNO_NEXT=n 2158 yesno() { 2159 mocklog yesno "$@" 2160 ANSWER="${YESNO_NEXT}" 2161 YESNO_NEXT=y 2162 } 2163 result() { 2164 %text:expand 2165 #|$ scm_begin 2166 #|$ yesno Re-encrypt subdir/middle? 2167 #|$ yesno Re-encrypt subdir/subsub/deep? 2168 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2169 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2170 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2171 #|$ scm_add subdir/subsub/deep.age 2172 #|$ scm_commit Re-encrypt subdir/ 2173 } 2174 When call do_reencrypt subdir 2175 The status should be success 2176 The output should be blank 2177 The error should equal "$(result)" 2178 End 2179 2180 It 'asks for confirmation before each file in deep re-encryption' 2181 DECISION=interactive 2182 RECURSIVE=yes 2183 YESNO_NEXT=n 2184 yesno() { 2185 mocklog yesno "$@" 2186 ANSWER="${YESNO_NEXT}" 2187 YESNO_NEXT=y 2188 } 2189 result() { 2190 %text:expand 2191 #|$ scm_begin 2192 #|$ yesno Re-encrypt subdir/middle? 2193 #|$ yesno Re-encrypt subdir/other/unrelated? 2194 #|$ do_decrypt ${PREFIX}/subdir/other/unrelated.age 2195 #|$ do_encrypt subdir/other/unrelated-XXXXXXXXX.age 2196 #|$ mv -f -- ${PREFIX}/subdir/other/unrelated-XXXXXXXXX.age ${PREFIX}/subdir/other/unrelated.age 2197 #|$ scm_add subdir/other/unrelated.age 2198 #|$ yesno Re-encrypt subdir/subsub/deep? 2199 #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age 2200 #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age 2201 #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age 2202 #|$ scm_add subdir/subsub/deep.age 2203 #|$ scm_commit Re-encrypt subdir/ 2204 } 2205 When call do_reencrypt subdir 2206 The status should be success 2207 The output should be blank 2208 The error should equal "$(result)" 2209 End 2210 2211 It 'reports a non-existent directory' 2212 result() { 2213 %text 2214 #|$ scm_begin 2215 #|Error: non-existent/ is not in the password store. 2216 } 2217 When run do_reencrypt non-existent/ 2218 The output should be blank 2219 The error should equal "$(result)" 2220 The status should equal 1 2221 End 2222 2223 It 'reports a non-existent file' 2224 result() { 2225 %text 2226 #|$ scm_begin 2227 #|Error: non-existent is not in the password store. 2228 } 2229 When run do_reencrypt non-existent 2230 The output should be blank 2231 The error should equal "$(result)" 2232 The status should equal 1 2233 End 2234 End 2235 2236 Describe 'do_show' 2237 cleartext(){ 2238 %text 2239 #|password line 2240 #|extra line 1 2241 #|extra line 2 2242 } 2243 2244 It 'shows a secret on standard output' 2245 cat() { @cat; } 2246 Data cleartext 2247 SHOW=text 2248 When call do_show 2249 The status should be success 2250 The output should equal "$(cleartext)" 2251 End 2252 2253 It 'pastes a secret into the clipboard' 2254 head() { @head "$@"; } 2255 tail() { @tail "$@"; } 2256 tr() { @tr "$@"; } 2257 platform_clip() { @cat >&2; } 2258 Data cleartext 2259 SELECTED_LINE=1 2260 SHOW=clip 2261 When call do_show title 2262 The status should be success 2263 The output should be blank 2264 The error should equal 'password line' 2265 End 2266 2267 It 'shows a secret as a QR-code' 2268 head() { @head "$@"; } 2269 tail() { @tail "$@"; } 2270 tr() { @tr "$@"; } 2271 platform_qrcode() { @cat >&2; } 2272 Data cleartext 2273 SELECTED_LINE=1 2274 SHOW=qrcode 2275 When call do_show title 2276 The status should be success 2277 The output should be blank 2278 The error should equal 'password line' 2279 End 2280 2281 It 'aborts on unexpected SHOW' 2282 SHOW=bad 2283 Data cleartext 2284 When run do_show title 2285 The output should be blank 2286 The error should equal 'Unexpected SHOW value "bad"' 2287 The status should equal 1 2288 End 2289 End 2290 2291 Describe 'do_tree' 2292 PREFIX="${SHELLSPEC_WORKDIR}/prefix" 2293 BLUE_TEXT='(B)' 2294 NORMAL_TEXT='(N)' 2295 RED_TEXT='(R)' 2296 TREE_T='T_' 2297 TREE_L='L_' 2298 TREE_I='I_' 2299 TREE__='__' 2300 2301 grep() { @grep "$@"; } 2302 2303 setup() { 2304 @mkdir -p "${PREFIX}/subdir/subsub" "${PREFIX}/empty" "${PREFIX}/other" 2305 %putsn data >"${PREFIX}/root.age" 2306 %putsn data >"${PREFIX}/subdir/hidden" 2307 %putsn data >"${PREFIX}/subdir/subsub/old.gpg" 2308 %putsn data >"${PREFIX}/other/upper.age" 2309 %putsn data >"${PREFIX}/other/lower.age" 2310 } 2311 2312 cleanup() { 2313 @rm -rf "${PREFIX}" 2314 } 2315 2316 BeforeEach setup 2317 AfterEach cleanup 2318 2319 It 'displays everything without a pattern' 2320 result() { 2321 %text 2322 #|T_(B)empty(N) 2323 #|T_(B)other(N) 2324 #|I_T_lower 2325 #|I_L_upper 2326 #|T_root 2327 #|L_(B)subdir(N) 2328 #|__L_(B)subsub(N) 2329 #|____L_(R)old(N) 2330 } 2331 When call do_tree '' 2332 The status should be success 2333 The output should equal "$(result)" 2334 End 2335 2336 It 'displays matching files and their non-matching parents' 2337 result() { 2338 %text 2339 #|T_(B)other(N) 2340 #|I_L_lower 2341 #|L_(B)subdir(N) 2342 #|__L_(B)subsub(N) 2343 #|____L_(R)old(N) 2344 } 2345 When call do_tree '' -i L 2346 The status should be success 2347 The output should equal "$(result)" 2348 End 2349 2350 It 'does not display matching directories' 2351 When call do_tree '' t 2352 The status should be success 2353 The output should equal 'L_root' 2354 End 2355 2356 It 'does not consider file extension when matching' 2357 When call do_tree '' g 2358 The status should be success 2359 The output should equal '' 2360 End 2361 2362 It 'might not display anything' 2363 When call do_tree '' z 2364 The status should be success 2365 The output should equal '' 2366 End 2367 2368 It 'defensively aborts on invalid prefix start' 2369 When run do_tree_prefix '_XI_' 2370 The output should be blank 2371 The error should equal 'Invalid tree prefix: "XI_"' 2372 The status should equal 1 2373 End 2374 2375 It 'defensively aborts on invalid prefix end' 2376 When run do_tree_prefix '_IX' 2377 The output should be blank 2378 The error should equal 'Invalid tree prefix: "X"' 2379 The status should equal 1 2380 End 2381 End 2382 End