commit 248a64472233980b810d9ce76c89459495958459
parent ad7c45a63946ff1c31b32562bc16b0c5404666b4
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date: Sat, 14 Sep 2024 09:01:31 +0000
SCM functions are tested
Diffstat:
5 files changed, 383 insertions(+), 4 deletions(-)
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
@@ -36,7 +36,8 @@ The following test sets can be found in `spec/` directory:
- `internal_spec.sh` tests internal helper functions in isolation;
- `action_spec.sh` tests action functions in isolation, mocking everything;
- `usage_spec.sh` tests command functions in isolation, mocking everything;
-- TODO tests SCM functions in isolation;
+- `scm_spec.sh` tests SCM functions in isolation, using the real git
+ and filesystem;
- TODO tests integration, calling command functions with minimal mocks;
- TODO tests `pass`-like behavior of the whole script;
- TODO tests `passage`-like behavior of the whole script.
diff --git a/spec/scm_spec.sh b/spec/scm_spec.sh
@@ -0,0 +1,372 @@
+# pashage - age-backed POSIX password manager
+# Copyright (C) 2024 Natasha Kerensikova
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# This test file fully covers all SCM functions,
+# both integrated with git and without a repository.
+
+Describe 'Integrated SCM Functions'
+ Include src/pashage.sh
+ Set 'errexit:on' 'nounset:on' 'pipefail:on'
+ PREFIX="${SHELLSPEC_WORKDIR}/repo"
+
+ git() { @git "$@"; }
+
+ git_log() {
+ @git -C "${PREFIX}" log --format='%s' >|"${SHELLSPEC_WORKDIR}/git-log.txt"
+ }
+
+ git_status() {
+ @git -C "${PREFIX}" status --porcelain \
+ >|"${SHELLSPEC_WORKDIR}/git-status.txt"
+ }
+
+ setup() {
+ @git init -q -b main "${PREFIX}"
+ @git -C "${PREFIX}" config --local user.name 'Test User'
+ @git -C "${PREFIX}" config --local user.email 'test@example.com'
+ @mkdir "${PREFIX}/subdir"
+ %putsn data >"${PREFIX}/subdir/file.txt"
+ @git -C "${PREFIX}" add subdir/file.txt
+ @git -C "${PREFIX}" commit -m 'Setup a file' >/dev/null
+ }
+
+ cleanup() {
+ @rm -rf "${PREFIX}"
+ }
+
+ BeforeEach setup
+ AfterEach cleanup
+
+ Describe 'scm_add'
+ It 'adds an untracked file'
+ testcase() {
+ %putsn other-data >"${PREFIX}/untracked.txt"
+ scm_add untracked.txt
+ }
+ When call testcase
+ The output should be blank
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'A untracked.txt'
+ End
+
+ It 'adds changes to a tracked file'
+ testcase() {
+ %putsn other-data >|"${PREFIX}/subdir/file.txt"
+ scm_add subdir/file.txt
+ }
+ When call testcase
+ The output should be blank
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'M subdir/file.txt'
+ End
+ End
+
+ Describe 'scm_begin'
+ It 'is successful on a clean repository'
+ When call scm_begin
+ The output should be blank
+ The status should be successful
+ End
+
+ It 'aborts when an untracked file exists'
+ testcase() {
+ %putsn other-data >"${PREFIX}/untracked.txt"
+ scm_begin
+ }
+ When run testcase
+ The status should equal 1
+ The error should equal 'There are already pending changes.'
+ End
+
+ It 'aborts when a tracked file is modified'
+ testcase() {
+ %putsn other-data >|"${PREFIX}/subdir/file.txt"
+ scm_begin
+ }
+ When run testcase
+ The status should equal 1
+ The error should equal 'There are already pending changes.'
+ End
+
+ It 'aborts when there are uncommitted changes'
+ testcase() {
+ %putsn other-data >|"${PREFIX}/subdir/file.txt"
+ scm_add subdir/file.txt
+ scm_begin
+ }
+ When run testcase
+ The status should equal 1
+ The error should equal 'There are already pending changes.'
+ End
+ End
+
+ Describe 'scm_commit'
+ It 'commits a new file'
+ testcase() {
+ scm_begin
+ %putsn other-data >"${PREFIX}/new.txt"
+ scm_add new.txt
+ scm_commit 'New file'
+ }
+ expected_log() { %text
+ #|New file
+ #|Setup a file
+ }
+ When call testcase
+ The output should be blank
+ The status should be successful
+ The result of function git_log should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-log.txt" \
+ should equal "$(expected_log)"
+ End
+
+ It 'does nothing without scm_add'
+ testcase() {
+ scm_begin
+ %putsn other-data >"${PREFIX}/new.txt"
+ scm_commit 'Nothing'
+ }
+ When call testcase
+ The output should be blank
+ The status should be successful
+ The result of function git_log should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-log.txt" \
+ should equal "Setup a file"
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal "?? new.txt"
+ End
+ End
+
+ Describe 'scm_cp'
+ cp() { @cp "$@"; }
+
+ It 'creates and adds a file'
+ When call scm_cp subdir/file.txt file-copy.txt
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ The contents of file "${PREFIX}/file-copy.txt" should equal 'data'
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal "A file-copy.txt"
+ End
+
+ It 'copies and adds a directory recursively'
+ When call scm_cp subdir newdir
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ The contents of file "${PREFIX}/newdir/file.txt" should equal 'data'
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal "A newdir/file.txt"
+ End
+ End
+
+ Describe 'scm_del'
+ rm() { @rm "$@"; }
+
+ It 'deletes a file'
+ When call scm_del subdir/file.txt
+ The output should be blank
+ The file "${PREFIX}/subdir/file.txt" should not be exist
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal "D subdir/file.txt"
+ End
+
+ It 'deletes a directory recursively'
+ When call scm_del subdir
+ The output should be blank
+ The directory "${PREFIX}/subdir" should not be exist
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal "D subdir/file.txt"
+ End
+ End
+
+ Describe 'scm_mv'
+ mv() { @mv "$@"; }
+
+ It 'moves a file and records the move'
+ When call scm_mv subdir/file.txt file.txt
+ The output should be blank
+ The file "${PREFIX}/subdir/file.txt" should not be exist
+ The contents of file "${PREFIX}/file.txt" should equal 'data'
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'R subdir/file.txt -> file.txt'
+ End
+
+ It 'moves a directory recursively and records the move'
+ When call scm_mv subdir newdir
+ The output should be blank
+ The directory "${PREFIX}/subdir" should not be exist
+ The contents of file "${PREFIX}/newdir/file.txt" should equal 'data'
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'R subdir/file.txt -> newdir/file.txt'
+ End
+ End
+
+ Describe 'scm_rm'
+ rm() { @rm "$@"; }
+
+ It 'removes a file'
+ When call scm_rm subdir/file.txt
+ The output should be blank
+ The file "${PREFIX}/subdir/file.txt" should not be exist
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'D subdir/file.txt'
+ End
+
+ It 'removes a directory recursively'
+ When call scm_rm subdir
+ The output should be blank
+ The directory "${PREFIX}/subdir" should not be exist
+ The result of function git_status should be successful
+ The contents of file "${SHELLSPEC_WORKDIR}/git-status.txt" \
+ should equal 'D subdir/file.txt'
+ End
+ End
+End
+
+Describe 'Integrated SCM Functions without SCM'
+ Include src/pashage.sh
+ Set 'errexit:on' 'nounset:on' 'pipefail:on'
+ PREFIX="${SHELLSPEC_WORKDIR}/repo"
+
+ setup() {
+ @mkdir -p "${PREFIX}/subdir"
+ %putsn data >"${PREFIX}/subdir/file.txt"
+ }
+
+ cleanup() {
+ @rm -rf "${PREFIX}"
+ }
+
+ BeforeEach setup
+ AfterEach cleanup
+
+ Describe 'scm_add'
+ It 'does nothing'
+ When call scm_add untracked.txt
+ The output should be blank
+ The status should be successful
+ End
+ End
+
+ Describe 'scm_begin'
+ It 'does nothing'
+ When call scm_begin
+ The output should be blank
+ The status should be successful
+ End
+
+ It 'does nothing even when an untracked file exists'
+ testcase() {
+ %putsn other-data >"${PREFIX}/untracked.txt"
+ scm_begin
+ }
+ When run testcase
+ The output should be blank
+ The status should be successful
+ End
+ End
+
+ Describe 'scm_commit'
+ It 'does nothing even with a new file added'
+ testcase() {
+ scm_begin
+ %putsn other-data >"${PREFIX}/new.txt"
+ scm_add new.txt
+ scm_commit 'New file'
+ }
+ When call testcase
+ The output should be blank
+ The status should be successful
+ End
+ End
+
+ Describe 'scm_cp'
+ cp() { @cp "$@"; }
+
+ It 'creates a file'
+ When call scm_cp subdir/file.txt file-copy.txt
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ The contents of file "${PREFIX}/file-copy.txt" should equal 'data'
+ End
+
+ It 'copies a directory recursively'
+ When call scm_cp subdir newdir
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ The contents of file "${PREFIX}/newdir/file.txt" should equal 'data'
+ End
+ End
+
+ Describe 'scm_del'
+ It 'does nothing with a file'
+ When call scm_del subdir/file.txt
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ End
+
+ It 'does nothing with a directory'
+ When call scm_del subdir
+ The output should be blank
+ The contents of file "${PREFIX}/subdir/file.txt" should equal 'data'
+ End
+ End
+
+ Describe 'scm_mv'
+ mv() { @mv "$@"; }
+
+ It 'moves a file'
+ When call scm_mv subdir/file.txt file.txt
+ The output should be blank
+ The file "${PREFIX}/subdir/file.txt" should not be exist
+ The contents of file "${PREFIX}/file.txt" should equal 'data'
+ End
+
+ It 'moves a directory recursively'
+ When call scm_mv subdir newdir
+ The output should be blank
+ The directory "${PREFIX}/subdir" should not be exist
+ The contents of file "${PREFIX}/newdir/file.txt" should equal 'data'
+ End
+ End
+
+ Describe 'scm_rm'
+ rm() { @rm "$@"; }
+
+ It 'removes a file'
+ When call scm_rm subdir/file.txt
+ The output should be blank
+ The file "${PREFIX}/subdir/file.txt" should not be exist
+ End
+
+ It 'removes a directory recursively'
+ When call scm_rm subdir
+ The output should be blank
+ The directory "${PREFIX}/subdir" should not be exist
+ End
+ End
+End
diff --git a/spec/support/bin/@cp b/spec/support/bin/@cp
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+. "$SHELLSPEC_SUPPORT_BIN"
+invoke cp "$@"
diff --git a/spec/support/bin/@git b/spec/support/bin/@git
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+. "$SHELLSPEC_SUPPORT_BIN"
+invoke git "$@"
diff --git a/src/pashage.sh b/src/pashage.sh
@@ -162,7 +162,7 @@ scm_begin() {
scm_commit() {
[ -d "${PREFIX}/.git" ] || return 0
if [ -n "$(git -C "${PREFIX}" status --porcelain || true)" ]; then
- git -C "${PREFIX}" commit -m "$1"
+ git -C "${PREFIX}" commit -m "$1" >/dev/null
fi
}
@@ -170,7 +170,7 @@ scm_commit() {
# $1: source
# $2: destination
scm_cp() {
- cp -r "${PREFIX}/$1" "${PREFIX}/$2"
+ cp -r -- "${PREFIX}/$1" "${PREFIX}/$2"
scm_add "$2"
}
@@ -192,7 +192,7 @@ scm_mv() {
fi
}
-# Delete a file or directory from filesystem and put it in pending chnages
+# Delete a file or directory from filesystem and put it in pending changes
scm_rm() {
rm -rf -- "${PREFIX:?}/$1"
scm_del "$1"