|
|
|
@@ -11,7 +11,7 @@ source "${PROJECT_DIR}/wg-manager.sh"
|
|
|
|
|
|
|
|
|
|
case $# in
|
|
|
|
|
0) ;;
|
|
|
|
|
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes|add-client|address-allocation|add-collisions|add-rollback|no-secret-logs|add-golden-config|add-invalid-names|show-client|config-export|qr|path-safety|no-secret-default-output|remove-client|remove-cancel|remove-exact-block|remove-rollback|archive-client|archive-path-safety|remove-preserves-neighbors|diagnostics|diagnostics-systemd|diagnostics-firewall|diagnostics-forwarding|diagnostics-runtime-diff|diagnostics-read-only|diagnostics-exit-codes|audit|audit-no-secrets|backup-list|restore|restore-invalid|restore-rollback|backup-path-safety|runtime-file-modes) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
|
|
|
|
|
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes|add-client|address-allocation|add-collisions|add-rollback|no-secret-logs|add-golden-config|add-invalid-names|show-client|config-export|qr|path-safety|no-secret-default-output|remove-client|remove-cancel|remove-exact-block|remove-rollback|archive-client|archive-path-safety|remove-preserves-neighbors|diagnostics|diagnostics-systemd|diagnostics-firewall|diagnostics-forwarding|diagnostics-runtime-diff|diagnostics-read-only|diagnostics-exit-codes|audit|audit-no-secrets|backup-list|restore|restore-invalid|restore-rollback|backup-path-safety|runtime-file-modes|gum-resolution|tui-routing|tui-cancel|tui-read-only|tui-no-gum|tui-non-tty|tui-add|tui-remove|tui-client-actions|tui-backup|tui-restore|tui-backend-failure|tui-no-secret-args) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
|
|
|
|
|
*) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;;
|
|
|
|
|
esac
|
|
|
|
|
test_group=${1-}
|
|
|
|
@@ -385,6 +385,772 @@ run_gum_tests() {
|
|
|
|
|
set -e
|
|
|
|
|
assert_equal "$EXIT_DEPENDENCY" "$status" 'ошибка WG_MANAGER_GUM'
|
|
|
|
|
assert_contains 'WG_MANAGER_GUM' "$output" 'сообщение WG_MANAGER_GUM'
|
|
|
|
|
rmdir -- "${TEST_ROOT}/bin/gum"
|
|
|
|
|
rmdir -- "${TEST_ROOT}/directory-gum"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prepare_m9_fixture() {
|
|
|
|
|
local config_file="${TEST_ROOT}/m9.conf" fixture_error
|
|
|
|
|
# Each focused/full-suite invocation shares TEST_ROOT; own every fixture
|
|
|
|
|
# directory before seeding it so backup counts and captures are deterministic.
|
|
|
|
|
rm -rf -- "${TEST_ROOT}/clients" "${TEST_ROOT}/metadata" "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/archive"
|
|
|
|
|
rm -f -- "${TEST_ROOT}/manager.lock" "${TEST_ROOT}/lock" "${TEST_ROOT}/gum-script" "${TEST_ROOT}/gum-style.txt" "${TEST_ROOT}/gum-pager.txt" "${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
mkdir -p -- "${TEST_ROOT}/clients" "${TEST_ROOT}/metadata" "${TEST_ROOT}/backups" "${TEST_ROOT}/logs"
|
|
|
|
|
cp -- "${PROJECT_DIR}/tests/fixtures/wg0.conf" "${TEST_ROOT}/wg0.conf"
|
|
|
|
|
write_valid_config "$config_file"
|
|
|
|
|
export WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="${TEST_ROOT}/backups" WG_MANAGER_METADATA_DIR="${TEST_ROOT}/metadata" WG_MANAGER_LOG_DIR="${TEST_ROOT}/logs" WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive" WG_MANAGER_LOCK_FILE="${TEST_ROOT}/manager.lock" WG_MANAGER_GUM="${PROJECT_DIR}/tests/mocks/bin/gum" WG_MANAGER_TEST_MODE=1 WG_MANAGER_TEST_TTY=both GUM_STYLE_CAPTURE="${TEST_ROOT}/gum-style.txt" GUM_PAGER_CAPTURE="${TEST_ROOT}/gum-pager.txt" GUM_CHOICES_CAPTURE="${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
mkdir -p -- "${TEST_ROOT}/archive"
|
|
|
|
|
printf '1\n' >"${TEST_ROOT}/ipv4-forward"
|
|
|
|
|
printf '1\n' >"${TEST_ROOT}/ipv6-forward"
|
|
|
|
|
export WG_MANAGER_IPV4_FORWARD_FILE="${TEST_ROOT}/ipv4-forward" WG_MANAGER_IPV6_FORWARD_FILE="${TEST_ROOT}/ipv6-forward"
|
|
|
|
|
printf '[Interface]\nPrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\nAddress = 10.0.0.2/32\n' >"${TEST_ROOT}/clients/alice.conf"
|
|
|
|
|
chmod 600 -- "${TEST_ROOT}/clients/alice.conf"
|
|
|
|
|
printf 'NAME=alice\nPUBLIC_KEY=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\nIPV4=192.0.2.2\nCREATED_AT=1700000000\nCONFIG_PATH=%s\nCLIENT_MODE=full-tunnel\nMANAGED=1\n' "${TEST_ROOT}/clients/alice.conf" >"${TEST_ROOT}/metadata/alice.meta"
|
|
|
|
|
chmod 600 -- "${TEST_ROOT}/metadata/alice.meta"
|
|
|
|
|
load_config >/dev/null
|
|
|
|
|
printf 'timestamp=2023-11-14T22:13:20Z actor_uid=%s operation=tui target=fixture client=fixture key_suffix= outcome=success error=read_only\n' "$EUID" >"${TEST_ROOT}/logs/wg-manager.log"
|
|
|
|
|
chmod 600 -- "${TEST_ROOT}/logs/wg-manager.log"
|
|
|
|
|
if ! fixture_error=$(create_backup "$WG_CONF" tui fixture 2>&1); then
|
|
|
|
|
printf 'Ошибка тестовой фикстуры: не удалось создать резервную копию: %s\n' "$fixture_error" >&2
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prepare_m10_add_fixture() {
|
|
|
|
|
local config_file="${TEST_ROOT}/m10-add.conf" fixture="${TEST_ROOT}/wg0.conf" runtime_file="${TEST_ROOT}/runtime.dump"
|
|
|
|
|
rm -rf -- "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/metadata" "${TEST_ROOT}/archive" "${TEST_ROOT}/clients" "${TEST_ROOT}/client-files"
|
|
|
|
|
rm -f -- "${TEST_ROOT}/lock" "${TEST_ROOT}/manager.lock" "${TEST_ROOT}/gum-script" "${TEST_ROOT}/gum-style.txt" "${TEST_ROOT}/gum-pager.txt" "${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
|
|
|
|
write_m2_config "$config_file" "$fixture"
|
|
|
|
|
export WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="${TEST_ROOT}/backups" WG_MANAGER_LOG_DIR="${TEST_ROOT}/logs" WG_MANAGER_METADATA_DIR="${TEST_ROOT}/metadata" WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive" WG_MANAGER_LOCK_FILE="${TEST_ROOT}/lock" WG_MANAGER_GUM="${PROJECT_DIR}/tests/mocks/bin/gum" WG_MANAGER_NOW=1700000100 WG_MANAGER_TEST_MODE=1 WG_MANAGER_TEST_TTY=both WG_MOCK_INTERFACE=wg0 GUM_STYLE_CAPTURE="${TEST_ROOT}/gum-style.txt" GUM_PAGER_CAPTURE="${TEST_ROOT}/gum-pager.txt" GUM_CHOICES_CAPTURE="${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\n' >"$runtime_file"
|
|
|
|
|
rm -rf -- "${TEST_ROOT}/dump-sequence"
|
|
|
|
|
mkdir -p -- "${TEST_ROOT}/dump-sequence"
|
|
|
|
|
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/2"
|
|
|
|
|
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/3"
|
|
|
|
|
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/4"
|
|
|
|
|
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/5"
|
|
|
|
|
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/6"
|
|
|
|
|
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\nFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=\t(none)\tvpn.example.invalid:51820\t192.0.2.3/32\t0\t0\t0\t0\n' >"${TEST_ROOT}/dump-sequence/1"
|
|
|
|
|
export WG_MOCK_DUMP_FILE="$runtime_file" WG_MOCK_DUMP_SEQUENCE_DIR="${TEST_ROOT}/dump-sequence"
|
|
|
|
|
unset WG_SYNC_FAIL WG_SYNC_FAIL_ONCE_FILE WG_SYNC_FAIL_ON_CALL WG_MOCK_DUMP_FAIL_ON_CALL WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK WG_MOCK_EXPECTED_PRIVATE_KEY
|
|
|
|
|
set_runtime_paths
|
|
|
|
|
load_config
|
|
|
|
|
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"
|
|
|
|
|
mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR" "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/archive"
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prepare_m10_remove_fixture() {
|
|
|
|
|
local config_file="${TEST_ROOT}/m10-remove.conf" fixture="${TEST_ROOT}/wg0.conf" runtime_dir="${TEST_ROOT}/remove-dumps"
|
|
|
|
|
rm -rf -- "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/metadata" "${TEST_ROOT}/archive" "${TEST_ROOT}/clients" "${TEST_ROOT}/client-files" "${runtime_dir}"
|
|
|
|
|
rm -f -- "${TEST_ROOT}/lock" "${TEST_ROOT}/manager.lock" "${TEST_ROOT}/gum-script" "${TEST_ROOT}/gum-style.txt" "${TEST_ROOT}/gum-pager.txt" "${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
|
|
|
|
chmod 600 -- "$fixture"
|
|
|
|
|
sed -i 's/wg-manager-client: fixture/wg-manager-client: alice/' "$fixture"
|
|
|
|
|
sed -i '/\[Peer\]/{N; s/\[Peer\]\n# wg-manager-client: alice/# wg-manager-client: alice\n[Peer]/;}' "$fixture"
|
|
|
|
|
write_m2_config "$config_file" "$fixture"
|
|
|
|
|
replace_config_line "$config_file" 'CLIENT_DIR=' "CLIENT_DIR=\"${TEST_ROOT}/client-files\""
|
|
|
|
|
export WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="${TEST_ROOT}/backups" WG_MANAGER_LOG_DIR="${TEST_ROOT}/logs" WG_MANAGER_METADATA_DIR="${TEST_ROOT}/metadata" WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive" WG_MANAGER_LOCK_FILE="${TEST_ROOT}/lock" WG_MANAGER_GUM="${PROJECT_DIR}/tests/mocks/bin/gum" WG_MANAGER_NOW=1700000100 WG_MANAGER_TEST_MODE=1 WG_MANAGER_TEST_TTY=both WG_MOCK_INTERFACE=wg0 GUM_STYLE_CAPTURE="${TEST_ROOT}/gum-style.txt" GUM_PAGER_CAPTURE="${TEST_ROOT}/gum-pager.txt" GUM_CHOICES_CAPTURE="${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
set_runtime_paths
|
|
|
|
|
load_config
|
|
|
|
|
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR" "$LOG_DIR" "$runtime_dir" "$BACKUP_DIR"
|
|
|
|
|
mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR" "$LOG_DIR" "$runtime_dir"
|
|
|
|
|
chmod 700 -- "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR" "$LOG_DIR" "$runtime_dir"
|
|
|
|
|
write_alice_metadata
|
|
|
|
|
printf '[Interface]\nPrivateKey = EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=\nAddress = 192.0.2.2/32\n' >"$CLIENT_DIR/alice.conf"
|
|
|
|
|
printf 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=\n' >"$CLIENT_DIR/alice.key"
|
|
|
|
|
printf 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=\n' >"$CLIENT_DIR/alice.pub"
|
|
|
|
|
printf 'GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG=\n' >"$CLIENT_DIR/alice.psk"
|
|
|
|
|
chmod 600 -- "$METADATA_DIR/alice.meta" "$CLIENT_DIR"/alice.*
|
|
|
|
|
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\n' >"$runtime_dir/1"
|
|
|
|
|
cp -- "$runtime_dir/1" "$runtime_dir/2"
|
|
|
|
|
cp -- "$runtime_dir/1" "$runtime_dir/3"
|
|
|
|
|
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\n' >"$runtime_dir/4"
|
|
|
|
|
cp -- "$runtime_dir/1" "$runtime_dir/5"
|
|
|
|
|
export WG_MOCK_DUMP_SEQUENCE_DIR="$runtime_dir" WG_MOCK_DUMP_FILE="$runtime_dir/1" TEST_MOCK_LOG="${TEST_ROOT}/mocks.log"
|
|
|
|
|
unset WG_SYNC_FAIL WG_SYNC_FAIL_ONCE_FILE WG_SYNC_FAIL_ON_CALL M6_ARCHIVE_COPY_FAIL_INDEX M6_ARCHIVE_REMOVE_FAIL_INDEX M6_ARCHIVE_RECOVERY_FAIL_INDEX
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prepare_m10_restore_fixture() {
|
|
|
|
|
local config_file="${TEST_ROOT}/m10-restore.conf" fixture="${TEST_ROOT}/wg0.conf" runtime_file="${TEST_ROOT}/restore-runtime.dump"
|
|
|
|
|
rm -rf -- "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/metadata" "${TEST_ROOT}/archive" "${TEST_ROOT}/clients" "${TEST_ROOT}/client-files"
|
|
|
|
|
rm -f -- "${TEST_ROOT}/lock" "${TEST_ROOT}/manager.lock" "${TEST_ROOT}/gum-script" "${TEST_ROOT}/gum-style.txt" "${TEST_ROOT}/gum-pager.txt" "${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
|
|
|
|
chmod 600 -- "$fixture"
|
|
|
|
|
write_m2_config "$config_file" "$fixture"
|
|
|
|
|
export WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="${TEST_ROOT}/backups" WG_MANAGER_LOG_DIR="${TEST_ROOT}/logs" WG_MANAGER_METADATA_DIR="${TEST_ROOT}/metadata" WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive" WG_MANAGER_LOCK_FILE="${TEST_ROOT}/lock" WG_MANAGER_GUM="${PROJECT_DIR}/tests/mocks/bin/gum" WG_MANAGER_NOW=1700000100 WG_MANAGER_TEST_MODE=1 WG_MANAGER_TEST_TTY=both WG_MOCK_INTERFACE=wg0 GUM_STYLE_CAPTURE="${TEST_ROOT}/gum-style.txt" GUM_PAGER_CAPTURE="${TEST_ROOT}/gum-pager.txt" GUM_CHOICES_CAPTURE="${TEST_ROOT}/gum-choices.txt"
|
|
|
|
|
set_runtime_paths
|
|
|
|
|
load_config
|
|
|
|
|
rm -rf -- "$BACKUP_DIR" "$LOG_DIR" "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR"
|
|
|
|
|
mkdir -p -- "$BACKUP_DIR" "$LOG_DIR" "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR"
|
|
|
|
|
chmod 700 -- "$BACKUP_DIR" "$LOG_DIR" "$METADATA_DIR" "$CLIENT_DIR" "$ARCHIVE_DIR"
|
|
|
|
|
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\n' >"$runtime_file"
|
|
|
|
|
export WG_MOCK_DUMP_FILE="$runtime_file"
|
|
|
|
|
unset WG_MOCK_DUMP_SEQUENCE_DIR WG_SYNC_FAIL WG_SYNC_FAIL_ONCE_FILE WG_SYNC_FAIL_ON_CALL TRANSACTION_ROLLBACK_REPLACE_FAIL
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_m9_tests() {
|
|
|
|
|
local output status script_file tty_mode pager_count snapshot_before lock_status long_name before_backup secret_stream add_output captured_prompt formatted_prompt long_endpoint
|
|
|
|
|
case $test_group in
|
|
|
|
|
gum-resolution)
|
|
|
|
|
run_gum_tests
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
begin_test 'gum capability probes accept the scripted mock'
|
|
|
|
|
output=$(resolve_gum)
|
|
|
|
|
assert_contains 'tests/mocks/bin/gum' "$output" 'gum mock resolved'
|
|
|
|
|
validate_gum_capabilities "$output" >/dev/null
|
|
|
|
|
assert_success "$?" 'gum capabilities'
|
|
|
|
|
export GUM_FAIL_CAPABILITY=filter
|
|
|
|
|
set +e; output=$(validate_gum_capabilities "$output" 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_DEPENDENCY" "$status" 'missing gum capability status'
|
|
|
|
|
assert_contains 'filter' "$output" 'missing capability diagnostic names subcommand'
|
|
|
|
|
unset GUM_FAIL_CAPABILITY
|
|
|
|
|
printf 'Выбор\nКлиент\ny\ny\n' >"${TEST_ROOT}/gum-wrapper-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-wrapper-script"
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
gum_header 'Заголовок' >/dev/null
|
|
|
|
|
gum_choose 'Клиент' 'Выход' >/dev/null
|
|
|
|
|
gum_filter 'Клиент' >/dev/null
|
|
|
|
|
gum_input --placeholder 'Имя' >/dev/null
|
|
|
|
|
gum_confirm 'Продолжить' >/dev/null
|
|
|
|
|
printf 'таблица\n' | gum_table >/dev/null
|
|
|
|
|
printf 'страница\n' | gum_pager >/dev/null
|
|
|
|
|
gum_spin --title 'Ожидание' >/dev/null
|
|
|
|
|
begin_test 'all gum wrappers keep their raw invocation details isolated'
|
|
|
|
|
for capability in style choose filter input confirm table pager spin; do
|
|
|
|
|
assert_contains "gum $capability" "$(<"$TEST_MOCK_LOG")" "обёртка gum $capability"
|
|
|
|
|
done
|
|
|
|
|
;;
|
|
|
|
|
tui-routing)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
script_file="${TEST_ROOT}/gum-script"; printf 'Состояние\nВыход\n' >"$script_file"; export GUM_SCRIPT_FILE="$script_file"
|
|
|
|
|
begin_test 'TUI routes status through the dashboard loop'
|
|
|
|
|
set +e; output=$(tui_screen 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'TUI status route exits cleanly'
|
|
|
|
|
assert_contains 'gum choose' "$(<"$TEST_MOCK_LOG")" 'menu wrapper invocation recorded'
|
|
|
|
|
assert_contains 'gum pager' "$(<"$TEST_MOCK_LOG")" 'pager wrapper invocation recorded'
|
|
|
|
|
assert_contains 'WireGuard Manager — wg0' "$(<"$GUM_STYLE_CAPTURE")" 'dashboard interface'
|
|
|
|
|
assert_contains 'Состояние:' "$(<"$GUM_STYLE_CAPTURE")" 'dashboard runtime state'
|
|
|
|
|
assert_contains 'Управляемых:' "$(<"$GUM_STYLE_CAPTURE")" 'dashboard managed count'
|
|
|
|
|
assert_contains 'Активны недавно:' "$(<"$GUM_STYLE_CAPTURE")" 'dashboard active count'
|
|
|
|
|
assert_contains 'Предупреждения:' "$(<"$GUM_STYLE_CAPTURE")" 'dashboard warning count'
|
|
|
|
|
assert_equal 0 "$(grep -c '\\\\n' "$GUM_STYLE_CAPTURE" || true)" 'dashboard has real line breaks'
|
|
|
|
|
assert_equal 0 "$(awk 'length($0) > 80 { count += 1 } END { print count + 0 }' "$GUM_STYLE_CAPTURE")" 'dashboard lines fit 80 columns'
|
|
|
|
|
printf 'Неизвестно\nНеизвестно\nНеизвестно\n' >"$script_file"
|
|
|
|
|
set +e; tui_screen >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'three route failures are bounded'
|
|
|
|
|
printf 'Диагностика\nЖурнал\nРезервные копии\nОтмена\nДобавить\n\nУдалить\n\nВыход\n' >"$script_file"
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
set +e; output=$(tui_screen 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'all read-only and placeholder routes return to menu'
|
|
|
|
|
assert_contains 'gum input' "$(<"$TEST_MOCK_LOG")" 'add route reaches input wrapper'
|
|
|
|
|
assert_contains 'gum filter' "$(<"$TEST_MOCK_LOG")" 'remove route reaches filter wrapper'
|
|
|
|
|
assert_equal 0 "$(grep -c '^gum pager$' "$TEST_MOCK_LOG" | awk '$1 >= 2 { print 0; next } { print 1 }')" 'diagnose and log reach pager'
|
|
|
|
|
printf 'Выход\n' >"$script_file"
|
|
|
|
|
set +e; main >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'zero-argument main enters TUI'
|
|
|
|
|
printf 'Выход\n' >"$script_file"
|
|
|
|
|
set +e; main tui >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'explicit tui command exits cleanly'
|
|
|
|
|
assert_equal '' "${TUI_GUM_PATH:-}" 'explicit tui clears resolved gum path'
|
|
|
|
|
;;
|
|
|
|
|
tui-cancel)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
script_file="${TEST_ROOT}/gum-script"; : >"$script_file"; export GUM_SCRIPT_FILE="$script_file"
|
|
|
|
|
begin_test 'TUI cancellation returns normally without mutation'
|
|
|
|
|
set +e; tui_screen >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'menu cancellation'
|
|
|
|
|
assert_equal '' "$(grep -E 'wg (syncconf|quick)|wg-quick' "$TEST_MOCK_LOG" || true)" 'cancellation has no mutation command'
|
|
|
|
|
printf 'Клиенты\n\nВыход\n' >"$script_file"
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
set +e; tui_screen >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'client filter cancellation returns to menu'
|
|
|
|
|
assert_equal 2 "$(grep '^gum choose' "$TEST_MOCK_LOG" | grep -vc -- '--help' || true)" 'filter cancellation returns to main menu'
|
|
|
|
|
assert_equal 1 "$(grep '^gum filter' "$TEST_MOCK_LOG" | grep -vc -- '--help' || true)" 'filter cancellation invokes one filter'
|
|
|
|
|
assert_equal 1 "$(grep '^gum pager' "$TEST_MOCK_LOG" | grep -vc -- '--help' || true)" 'filter cancellation only pages the client list'
|
|
|
|
|
assert_equal 0 "$(grep -c 'TUI завершён\|Неизвестный пункт' "$TEST_MOCK_LOG" || true)" 'filter cancellation is not a route failure'
|
|
|
|
|
assert_equal '' "${TUI_GUM_PATH:-}" 'TUI gum path is cleared after cancellation'
|
|
|
|
|
unset WG_MANAGER_TEST_TTY
|
|
|
|
|
set +e; tui_is_terminal >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_failure "$status" 'unset test TTY is not accepted'
|
|
|
|
|
;;
|
|
|
|
|
tui-read-only)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
script_file="${TEST_ROOT}/gum-script"; printf 'Клиенты\nalice — BBBBBBBB\nДетали\nВыход\n' >"$script_file"; export GUM_SCRIPT_FILE="$script_file"
|
|
|
|
|
snapshot_before=$(find "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/archive" "${TEST_ROOT}/clients" "${TEST_ROOT}/metadata" -mindepth 1 -maxdepth 2 -printf '%P %y %m %s\n' | LC_ALL=C sort; sha256sum "$WG_CONF" "$WG_MANAGER_CONFIG" "${TEST_ROOT}/clients/alice.conf" "${TEST_ROOT}/metadata/alice.meta"; stat -c '%a:%u:%g:%h' "$WG_CONF" "$WG_MANAGER_CONFIG" "${TEST_ROOT}/clients/alice.conf" "${TEST_ROOT}/metadata/alice.meta")
|
|
|
|
|
begin_test 'TUI client selection uses stable identity and read-only backend'
|
|
|
|
|
set +e; tui_screen >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'client route exits cleanly'
|
|
|
|
|
assert_contains 'gum filter' "$(<"$TEST_MOCK_LOG")" 'filter wrapper invocation recorded'
|
|
|
|
|
pager_count=$(grep -c '^gum pager$' "$TEST_MOCK_LOG" || true)
|
|
|
|
|
((pager_count >= 1)) && ((TEST_ASSERTIONS += 1)) || record_failure 'client list is paged before selection'
|
|
|
|
|
assert_equal '' "$(grep -E 'syncconf|wg-quick (up|down|save)|ip (add|del|replace)' "$TEST_MOCK_LOG" || true)" 'read-only screen has no mutation calls'
|
|
|
|
|
CLIENT_NAMES=(same same) CLIENT_KEYS=(ABCDEFGH111111111111111111111111111111111= ABCDEFGH222222222222222222222222222222222=)
|
|
|
|
|
CLIENT_ADDRESSES=(192.0.2.2/32 192.0.2.2/32) CLIENT_ENDPOINTS=(- -) CLIENT_HANDSHAKES=(0 0) CLIENT_RX=(0 0) CLIENT_TX=(0 0) CLIENT_STATES=(unmanaged unmanaged) CLIENT_ORIGINS=(unmanaged unmanaged)
|
|
|
|
|
printf 'same — ABCDEFGH #2\nДетали\n' >"$script_file"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'duplicate display names select the offered stable index'
|
|
|
|
|
assert_contains 'ABCDEFGH222222222222222222222222222222222=' "$(<"$GUM_PAGER_CAPTURE")" 'selected duplicate identity renders the second full key'
|
|
|
|
|
assert_equal 0 "$(grep -c 'ABCDEFGH111111111111111111111111111111111=' "$GUM_PAGER_CAPTURE" || true)" 'selected detail does not render the first key'
|
|
|
|
|
long_name=$(printf 'n%.0s' {1..64})
|
|
|
|
|
CLIENT_NAMES=("$long_name" "$long_name") CLIENT_KEYS=(ABCDEFGH111111111111111111111111111111111= ABCDEFGH222222222222222222222222222222222=)
|
|
|
|
|
CLIENT_ADDRESSES=(192.0.2.2/32 192.0.2.2/32)
|
|
|
|
|
printf '%s\nДетали\n' "${long_name:0:60} — ABCDEFGH #2" >"$script_file"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'long client names remain selectable'
|
|
|
|
|
assert_equal 0 "$(awk 'length($0) > 80 { bad += 1 } END { print bad + 0 }' "$GUM_CHOICES_CAPTURE")" 'offered client labels fit 80 columns'
|
|
|
|
|
assert_contains 'ABCDEFGH222222222222222222222222222222222=' "$(<"$GUM_PAGER_CAPTURE")" 'long-name collision keeps the selected identity'
|
|
|
|
|
assert_equal "$snapshot_before" "$(find "${TEST_ROOT}/backups" "${TEST_ROOT}/logs" "${TEST_ROOT}/archive" "${TEST_ROOT}/clients" "${TEST_ROOT}/metadata" -mindepth 1 -maxdepth 2 -printf '%P %y %m %s\n' | LC_ALL=C sort; sha256sum "$WG_CONF" "$WG_MANAGER_CONFIG" "${TEST_ROOT}/clients/alice.conf" "${TEST_ROOT}/metadata/alice.meta"; stat -c '%a:%u:%g:%h' "$WG_CONF" "$WG_MANAGER_CONFIG" "${TEST_ROOT}/clients/alice.conf" "${TEST_ROOT}/metadata/alice.meta")" 'read-only files, modes, and directory entries unchanged'
|
|
|
|
|
assert_equal '' "$(grep -E 'syncconf|wg-quick (up|down|save)|ip (add|del|replace)|systemctl (start|stop|restart)|nft (add|delete)|iptables -.*-(A|D|I)' "$TEST_MOCK_LOG" || true)" 'read-only command log has no mutation'
|
|
|
|
|
assert_equal 0 "$(find "${WG_CONF%/*}" -maxdepth 1 -name '.wg-manager-tui.*' -print | wc -l)" 'read-only pages create no config-directory temporary files'
|
|
|
|
|
if [[ -e $WG_MANAGER_LOCK_FILE ]]; then lock_status=1; else lock_status=0; fi
|
|
|
|
|
assert_equal 0 "$lock_status" 'read-only screens do not create a mutation lock'
|
|
|
|
|
assert_equal 0 "$(grep -hE 'AAAAAAAAAAAAAAAA|PresharedKey' "$GUM_PAGER_CAPTURE" "$GUM_STYLE_CAPTURE" "$TEST_MOCK_LOG" | wc -l)" 'read-only TUI output has no secrets'
|
|
|
|
|
;;
|
|
|
|
|
tui-add)
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
begin_test 'TUI add real full-tunnel success uses the shared backend'
|
|
|
|
|
printf 'alice\nПолный туннель\ny\ny\nn\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_add_flow 2>"${TEST_ROOT}/add.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'real TUI add succeeds'
|
|
|
|
|
assert_file_exists "${CLIENT_DIR}/alice.conf" 'real client config exists'
|
|
|
|
|
assert_file_exists "${METADATA_DIR}/alice.meta" 'real client metadata exists'
|
|
|
|
|
assert_file_mode 600 "${CLIENT_DIR}/alice.conf" 'real client config mode'
|
|
|
|
|
assert_file_mode 600 "${METADATA_DIR}/alice.meta" 'real metadata mode'
|
|
|
|
|
assert_contains 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=' "$(<"$WG_CONF")" 'generated peer is in server config'
|
|
|
|
|
assert_contains '192.0.2.3/32' "$(<"$WG_CONF")" 'generated address is in server config'
|
|
|
|
|
assert_contains 'AllowedIPs = 0.0.0.0/0,::/0' "$(<"${CLIENT_DIR}/alice.conf")" 'full-tunnel AllowedIPs are generated'
|
|
|
|
|
assert_contains 'Endpoint = vpn.example.invalid:51820' "$(<"${CLIENT_DIR}/alice.conf")" 'configured endpoint is generated'
|
|
|
|
|
assert_contains 'wg syncconf' "$(<"$TEST_MOCK_LOG")" 'runtime syncconf is invoked'
|
|
|
|
|
assert_contains 'operation=add' "$(<"${LOG_DIR}/wg-manager.log")" 'add audit is recorded'
|
|
|
|
|
assert_contains "${CLIENT_DIR}/alice.conf" "$output" 'final config path is shown'
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey' "$TEST_MOCK_LOG" "${LOG_DIR}/wg-manager.log" "${TEST_ROOT}/add.err" "$GUM_PAGER_CAPTURE" || true)" 'add outputs and logs are secret-free'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add preserves QR operation failure status'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
export QR_MOCK_EXPECTED_FILE="${CLIENT_DIR}/qr-fail.conf" QR_MOCK_FAIL=1
|
|
|
|
|
printf 'qr-fail\nПолный туннель\ny\ny\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_add_flow >/dev/null 2>"${TEST_ROOT}/qr-fail-add.err"; status=$?; set -e
|
|
|
|
|
unset QR_MOCK_EXPECTED_FILE QR_MOCK_FAIL
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'TUI add returns QR operation failure'
|
|
|
|
|
assert_contains 'QR-код недоступен' "$(<"${TEST_ROOT}/qr-fail-add.err")" 'TUI add preserves QR diagnostic'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add treats missing QR dependency as a normal fallback'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
export QR_MOCK_EXPECTED_FILE="${CLIENT_DIR}/qr-missing.conf"
|
|
|
|
|
saved_path=$PATH
|
|
|
|
|
mkdir -- "${TEST_ROOT}/qr-add-no-command"
|
|
|
|
|
for utility in bash env wg wg-quick cat stat sort paste date tail mv cp chmod mkdir mktemp flock rm ln; do
|
|
|
|
|
ln -s -- "$(command -v "$utility")" "${TEST_ROOT}/qr-add-no-command/$utility"
|
|
|
|
|
done
|
|
|
|
|
PATH="${TEST_ROOT}/qr-add-no-command"
|
|
|
|
|
printf 'qr-missing\nПолный туннель\ny\ny\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_add_flow 2>"${TEST_ROOT}/qr-missing-add.err"); status=$?; set -e
|
|
|
|
|
PATH=$saved_path
|
|
|
|
|
unset QR_MOCK_EXPECTED_FILE
|
|
|
|
|
assert_equal 0 "$status" 'missing QR dependency is a normal TUI add fallback'
|
|
|
|
|
assert_contains 'qr-missing.conf' "$output" 'missing QR fallback retains config path'
|
|
|
|
|
assert_contains 'QR-код недоступен' "$(<"${TEST_ROOT}/qr-missing-add.err")" 'missing QR fallback is actionable'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add split tunnel accepts normalized networks and endpoint override'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
printf 'bob\nВыборочные сети\n10.40.0.0/16, 2001:db8:40::/48\nn\nalt.example.invalid\ny\nn\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_add_flow 2>"${TEST_ROOT}/split.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'split TUI add succeeds'
|
|
|
|
|
assert_file_exists "${CLIENT_DIR}/bob.conf" 'split client config exists'
|
|
|
|
|
assert_contains 'AllowedIPs = 10.40.0.0/16,2001:db8:40::/48' "$(<"${CLIENT_DIR}/bob.conf")" 'split networks are normalized'
|
|
|
|
|
assert_contains 'Endpoint = alt.example.invalid:51820' "$(<"${CLIENT_DIR}/bob.conf")" 'endpoint override is applied'
|
|
|
|
|
assert_contains 'MODE=split-tunnel' "$(<"${METADATA_DIR}/bob.meta")" 'split mode is recorded'
|
|
|
|
|
assert_contains 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=' "$(<"$WG_CONF")" 'split peer reaches server config'
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey' "$TEST_MOCK_LOG" "${LOG_DIR}/wg-manager.log" "${TEST_ROOT}/split.err" "$GUM_PAGER_CAPTURE" || true)" 'split output and logs are secret-free'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add cancellation before create preserves isolated state'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
printf 'alice\nПолный туннель\ny\nn\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_add_flow >/dev/null 2>"${TEST_ROOT}/cancel.err"; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'create cancellation is normal'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'create cancellation preserves server bytes'
|
|
|
|
|
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -mindepth 1 -maxdepth 1 -type f | wc -l)" 'create cancellation leaves no artifacts'
|
|
|
|
|
assert_equal '' "$(grep -E 'syncconf|wg-quick' "$TEST_MOCK_LOG" || true)" 'create cancellation does not mutate runtime'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add cancellation before name is harmless'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
: >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_add_flow >/dev/null 2>"${TEST_ROOT}/empty-name.err"; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'initial name cancellation is normal'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'initial cancellation preserves server bytes'
|
|
|
|
|
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -mindepth 1 -maxdepth 1 -type f | wc -l)" 'initial cancellation leaves no artifacts'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add corrects invalid name and endpoint'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
printf 'bad name\ncarol\nПолный туннель\nn\nbad endpoint\ncarol.example.invalid\ny\nn\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_add_flow 2>"${TEST_ROOT}/correction.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'invalid name and endpoint can be corrected'
|
|
|
|
|
assert_file_exists "${CLIENT_DIR}/carol.conf" 'corrected client artifact exists'
|
|
|
|
|
assert_contains 'carol.example.invalid:51820' "$(<"${CLIENT_DIR}/carol.conf")" 'corrected endpoint is used'
|
|
|
|
|
assert_contains 'Некорректное имя клиента' "$(<"${TEST_ROOT}/correction.err")" 'invalid name gives actionable feedback'
|
|
|
|
|
assert_contains 'Некорректный endpoint' "$(<"${TEST_ROOT}/correction.err")" 'invalid endpoint gives actionable feedback'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add rejects invalid split networks before mutation'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
printf 'badnet\nВыборочные сети\nnot-a-cidr\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_add_flow >/dev/null 2>"${TEST_ROOT}/bad-net.err"; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_VALIDATION" "$status" 'invalid split network status'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'invalid split network preserves server bytes'
|
|
|
|
|
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -mindepth 1 -maxdepth 1 -type f | wc -l)" 'invalid split network leaves no artifacts'
|
|
|
|
|
assert_equal '' "$(grep -E 'syncconf|wg-quick' "$TEST_MOCK_LOG" || true)" 'invalid split network does not mutate runtime'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI add apply failure rolls back and removes staged artifacts'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
export WG_SYNC_FAIL_ONCE_FILE="${TEST_ROOT}/sync-fail"
|
|
|
|
|
unset WG_MOCK_DUMP_SEQUENCE_DIR
|
|
|
|
|
printf 'dana\nПолный туннель\ny\ny\nn\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_add_flow >/dev/null 2>"${TEST_ROOT}/apply-fail.err"; status=$?; set -e
|
|
|
|
|
unset WG_SYNC_FAIL_ONCE_FILE
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'apply failure returns operation status after rollback'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'apply failure restores server bytes'
|
|
|
|
|
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 -type f -name 'dana.*' | wc -l)" 'apply failure leaves no client artifacts'
|
|
|
|
|
assert_contains 'syncconf' "$(<"$TEST_MOCK_LOG")" 'apply failure attempted runtime update and rollback'
|
|
|
|
|
assert_contains 'operation=add' "$(<"${LOG_DIR}/wg-manager.log")" 'apply failure is audited'
|
|
|
|
|
|
|
|
|
|
begin_test 'CLI add split-networks contract is validated'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
set +e; add_client cli --mode split-tunnel --split-networks '10.40.0.0/16,2001:db8:40::/48' >/dev/null 2>"${TEST_ROOT}/cli-split.err"; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'CLI split add succeeds'
|
|
|
|
|
assert_contains 'AllowedIPs = 10.40.0.0/16,2001:db8:40::/48' "$(<"${CLIENT_DIR}/cli.conf")" 'CLI split networks reach config'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
set +e; add_client missing --mode split-tunnel --split-networks >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'missing split-networks value is usage error'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
set +e; add_client repeat --mode split-tunnel --split-networks '10.40.0.0/16' --split-networks '10.41.0.0/16' >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'repeated split-networks is usage error'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
set +e; add_client invalid --mode split-tunnel --split-networks 'not-a-cidr' >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_VALIDATION" "$status" 'invalid split CIDR is validation error'
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
set +e; add_client full --mode full-tunnel --split-networks '10.40.0.0/16' >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_VALIDATION" "$status" 'split-networks is rejected for full tunnel'
|
|
|
|
|
;;
|
|
|
|
|
tui-remove)
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
begin_test 'TUI remove real success archives exact client and peer'
|
|
|
|
|
printf 'alice — 192.0.2.2/32 — BBBBBBBB\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_remove_flow 2>"${TEST_ROOT}/remove.err"); status=$?; set -e
|
|
|
|
|
output+=$'\n'"$(<"${TEST_ROOT}/remove.err")"
|
|
|
|
|
assert_equal 0 "$status" 'real TUI remove succeeds'
|
|
|
|
|
assert_equal 0 "$(grep -c 'PublicKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' "$WG_CONF" || true)" 'removed peer is absent'
|
|
|
|
|
assert_file_absent "$CLIENT_DIR/alice.conf" 'client config is archived'
|
|
|
|
|
assert_file_absent "$METADATA_DIR/alice.meta" 'client metadata is archived'
|
|
|
|
|
archive_path=$(find "$ARCHIVE_DIR" -mindepth 1 -maxdepth 1 -type d | head -n 1)
|
|
|
|
|
if [[ -n $archive_path ]]; then
|
|
|
|
|
assert_file_exists "$archive_path/alice.conf" 'archive contains config'
|
|
|
|
|
assert_file_exists "$archive_path/alice.meta" 'archive contains metadata'
|
|
|
|
|
assert_file_mode 700 "$archive_path" 'archive mode is restrictive'
|
|
|
|
|
assert_file_mode 600 "$archive_path/alice.conf" 'archived config mode'
|
|
|
|
|
else
|
|
|
|
|
assert_equal 1 0 'archive path is returned after successful removal'
|
|
|
|
|
fi
|
|
|
|
|
assert_contains 'Архив:' "$output" 'TUI reports actual archive path'
|
|
|
|
|
assert_contains 'syncconf' "$(<"$TEST_MOCK_LOG")" 'remove applies runtime update'
|
|
|
|
|
assert_contains 'operation=remove' "$(<"$LOG_DIR/wg-manager.log")" 'remove audit is recorded'
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey' "$TEST_MOCK_LOG" "$LOG_DIR/wg-manager.log" "$TEST_ROOT/remove.err" || true)" 'remove output and logs are secret-free'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove cancellation at filter preserves state'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF"); before_files=$(find "$CLIENT_DIR" "$METADATA_DIR" "$ARCHIVE_DIR" -type f -printf '%P %s\n' | LC_ALL=C sort)
|
|
|
|
|
: >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_remove_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'filter cancellation is normal'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'filter cancellation preserves server'
|
|
|
|
|
assert_equal "$before_files" "$(find "$CLIENT_DIR" "$METADATA_DIR" "$ARCHIVE_DIR" -type f -printf '%P %s\n' | LC_ALL=C sort)" 'filter cancellation preserves artifacts'
|
|
|
|
|
assert_equal '' "$(grep -E 'syncconf|wg-quick' "$TEST_MOCK_LOG" || true)" 'filter cancellation does not mutate runtime'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove cancellation at confirmation preserves state'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
printf 'alice — 192.0.2.2/32 — BBBBBBBB\nn\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_remove_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'confirmation cancellation is normal'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'confirmation cancellation preserves server'
|
|
|
|
|
assert_equal 0 "$(find "$ARCHIVE_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'confirmation cancellation creates no archive'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove excludes unmanaged clients and keeps labels bounded'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
printf '%s\t%s\t-\t192.0.2.9/32\t0\t0\t0\t0\n' EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE= '(none)' >>"${TEST_ROOT}/remove-dumps/1"
|
|
|
|
|
printf '\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_remove_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'unmanaged-only selection cancellation is normal'
|
|
|
|
|
assert_equal 0 "$(grep -c EEEEEEEE "$GUM_CHOICES_CAPTURE" || true)" 'unmanaged peer is not offered'
|
|
|
|
|
long_name=$(printf 'n%.0s' {1..64})
|
|
|
|
|
CLIENT_NAMES=("$long_name") CLIENT_KEYS=(BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=) CLIENT_ADDRESSES=(192.0.2.2/32) CLIENT_ORIGINS=(managed)
|
|
|
|
|
printf '\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_remove_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$(awk 'length($0) > 80 { bad += 1 } END { print bad + 0 }' "$GUM_CHOICES_CAPTURE")" 'remove labels stay within 80 columns'
|
|
|
|
|
|
|
|
|
|
begin_test 'destructive TUI previews retain identity while fitting 80 columns'
|
|
|
|
|
captured_prompt=''
|
|
|
|
|
gum_confirm() { captured_prompt=$1; return 1; }
|
|
|
|
|
set +e; tui_remove_flow_for_index "$long_name" 0 >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
unset -f gum_confirm
|
|
|
|
|
gum_confirm() { gum_command confirm "$@"; }
|
|
|
|
|
assert_equal 0 "$status" 'long destructive preview cancellation is normal'
|
|
|
|
|
assert_contains "$long_name" "$captured_prompt" 'long name remains in destructive preview'
|
|
|
|
|
assert_contains '192.0.2.2/32' "$captured_prompt" 'address remains in destructive preview'
|
|
|
|
|
assert_equal 0 "$(awk 'length($0) > 80 { bad += 1 } END { print bad + 0 }' <<<"$captured_prompt")" 'destructive preview lines fit 80 columns'
|
|
|
|
|
long_endpoint=$(printf 'e%.0s' {1..253})
|
|
|
|
|
formatted_prompt=$(tui_wrap_value 'Использовать endpoint:' "$long_endpoint")
|
|
|
|
|
assert_equal 0 "$(awk 'length($0) > 80 { bad += 1 } END { print bad + 0 }' <<<"$formatted_prompt")" 'endpoint preview lines fit 80 columns'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove exact-key mismatch is rejected before mutation'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
set +e; remove_client_exact alice CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC= >/dev/null 2>"${TEST_ROOT}/wrong-key.err"; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_VALIDATION" "$status" 'wrong selected key is rejected'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'wrong selected key preserves server'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove apply failure rolls back disk and artifacts'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
cp -- "$TEST_ROOT/remove-dumps/1" "$TEST_ROOT/remove-dumps/2"
|
|
|
|
|
cp -- "$TEST_ROOT/remove-dumps/1" "$TEST_ROOT/remove-dumps/3"
|
|
|
|
|
cp -- "$TEST_ROOT/remove-dumps/1" "$TEST_ROOT/remove-dumps/4"
|
|
|
|
|
cp -- "$TEST_ROOT/remove-dumps/1" "$TEST_ROOT/remove-dumps/5"
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
export WG_SYNC_FAIL_ONCE_FILE="${TEST_ROOT}/remove-sync-fail"
|
|
|
|
|
printf 'alice — 192.0.2.2/32 — BBBBBBBB\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_remove_flow >/dev/null 2>"${TEST_ROOT}/remove-fail.err"; status=$?; set -e
|
|
|
|
|
unset WG_SYNC_FAIL_ONCE_FILE
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'remove apply failure returns operation'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'remove apply failure restores server'
|
|
|
|
|
assert_file_exists "$CLIENT_DIR/alice.conf" 'remove apply failure preserves config'
|
|
|
|
|
assert_file_exists "$METADATA_DIR/alice.meta" 'remove apply failure preserves metadata'
|
|
|
|
|
assert_equal 0 "$(find "$ARCHIVE_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'remove apply failure leaves no completed archive'
|
|
|
|
|
assert_contains 'operation=rollback' "$(<"$LOG_DIR/wg-manager.log")" 'remove rollback is audited'
|
|
|
|
|
assert_contains 'operation=remove target=alice client=alice key_suffix=BBBBBBB= outcome=failed error=transaction-failed' "$(<"$LOG_DIR/wg-manager.log")" 'remove failure is audited'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI remove artifact recovery failure is critical'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
export M6_ARCHIVE_REMOVE_FAIL_INDEX=1 M6_ARCHIVE_RECOVERY_FAIL_INDEX=1
|
|
|
|
|
printf 'alice — 192.0.2.2/32 — BBBBBBBB\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_remove_flow 2>&1); status=$?; set -e
|
|
|
|
|
unset M6_ARCHIVE_REMOVE_FAIL_INDEX M6_ARCHIVE_RECOVERY_FAIL_INDEX
|
|
|
|
|
assert_equal "$EXIT_ROLLBACK" "$status" 'artifact recovery returns critical status'
|
|
|
|
|
assert_contains 'КРИТИЧНО' "$output" 'artifact recovery reports critical error'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
export M6_ARCHIVE_REMOVE_FAIL_INDEX=1 M6_ARCHIVE_RECOVERY_FAIL_INDEX=1
|
|
|
|
|
printf 'Удалить\nalice — 192.0.2.2/32 — BBBBBBBB\ny\nВыход\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_screen >/dev/null 2>"${TEST_ROOT}/screen-critical.err"; status=$?; set -e
|
|
|
|
|
unset M6_ARCHIVE_REMOVE_FAIL_INDEX M6_ARCHIVE_RECOVERY_FAIL_INDEX
|
|
|
|
|
assert_equal "$EXIT_ROLLBACK" "$status" 'TUI screen propagates critical remove failure'
|
|
|
|
|
assert_contains 'КРИТИЧНО' "$(<"${TEST_ROOT}/screen-critical.err")" 'screen critical diagnostic'
|
|
|
|
|
;;
|
|
|
|
|
tui-client-actions)
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
begin_test 'managed client details use the selected exact identity'
|
|
|
|
|
printf 'alice — BBBBBBBB\nДетали\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'managed details succeed'
|
|
|
|
|
assert_contains 'Публичный ключ: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' "$(<"$GUM_PAGER_CAPTURE")" 'details show selected key'
|
|
|
|
|
|
|
|
|
|
begin_test 'config denial does not read or page client config'
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать конфигурацию\nn\n' >"${TEST_ROOT}/gum-script"; : >"$GUM_PAGER_CAPTURE"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'config denial is normal'
|
|
|
|
|
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG|\[Interface\]|\[Peer\]' "$GUM_PAGER_CAPTURE" || true)" 'denied config is not paged'
|
|
|
|
|
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG|\[Interface\]' "$TEST_MOCK_LOG" || true)" 'denied config stays out of gum log'
|
|
|
|
|
|
|
|
|
|
begin_test 'confirmed config reaches pager only after confirmation'
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать конфигурацию\ny\n' >"${TEST_ROOT}/gum-script"; : >"$GUM_PAGER_CAPTURE"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'confirmed config succeeds'
|
|
|
|
|
assert_contains 'PrivateKey = EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=' "$(<"$GUM_PAGER_CAPTURE")" 'confirmed config reaches pager stdin'
|
|
|
|
|
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG' "$TEST_MOCK_LOG" || true)" 'confirmed config absent from gum argv log'
|
|
|
|
|
|
|
|
|
|
begin_test 'QR denial never invokes qrencode'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать QR\nn\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"; : >"$TEST_MOCK_LOG"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'QR denial is normal'
|
|
|
|
|
assert_equal 0 "$(grep -c '^qrencode ' "$TEST_MOCK_LOG" || true)" 'QR denial does not invoke qrencode'
|
|
|
|
|
|
|
|
|
|
begin_test 'confirmed QR uses exact argv and config stdin'
|
|
|
|
|
export QR_MOCK_EXPECTED_FILE="$CLIENT_DIR/alice.conf"
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать QR\ny\n' >"${TEST_ROOT}/gum-script"; : >"$TEST_MOCK_LOG"
|
|
|
|
|
set +e; output=$(tui_select_client 2>"${TEST_ROOT}/qr-action.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'confirmed QR succeeds'
|
|
|
|
|
assert_contains 'MOCK-QR' "$output" 'QR output is returned'
|
|
|
|
|
assert_equal 1 "$(grep -c '^qrencode -t ansiutf8$' "$TEST_MOCK_LOG" || true)" 'QR argv is exact'
|
|
|
|
|
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG' "$TEST_MOCK_LOG" "${TEST_ROOT}/qr-action.err" || true)" 'QR action logs stay secret-free'
|
|
|
|
|
unset QR_MOCK_EXPECTED_FILE
|
|
|
|
|
|
|
|
|
|
begin_test 'missing qrencode is a normal fallback'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
local saved_path=$PATH
|
|
|
|
|
mkdir -p -- "${TEST_ROOT}/qr-no-command"
|
|
|
|
|
for utility in bash env wg cat stat sort paste date tail mv; do ln -s -- "$(command -v "$utility")" "${TEST_ROOT}/qr-no-command/$utility"; done
|
|
|
|
|
PATH="${TEST_ROOT}/qr-no-command"
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать QR\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="$TEST_ROOT/gum-script"
|
|
|
|
|
set +e; output=$(tui_select_client 2>"${TEST_ROOT}/qr-missing-action.err"); status=$?; set -e
|
|
|
|
|
PATH=$saved_path
|
|
|
|
|
assert_equal 0 "$status" 'missing QR dependency returns normally'
|
|
|
|
|
assert_contains "${CLIENT_DIR}/alice.conf" "$output" 'missing QR fallback gives config path'
|
|
|
|
|
assert_contains 'qrencode не найден' "$(<"${TEST_ROOT}/qr-missing-action.err")" 'missing QR fallback is actionable'
|
|
|
|
|
|
|
|
|
|
begin_test 'qrencode failure preserves backend error status'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
export QR_MOCK_FAIL=1
|
|
|
|
|
printf 'alice — BBBBBBBB\nПоказать QR\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="$TEST_ROOT/gum-script"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>"${TEST_ROOT}/qr-fail-action.err"; status=$?; set -e
|
|
|
|
|
unset QR_MOCK_FAIL
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'QR failure is nonzero'
|
|
|
|
|
assert_contains 'QR-код недоступен' "$(<"${TEST_ROOT}/qr-fail-action.err")" 'QR failure diagnostic is preserved'
|
|
|
|
|
|
|
|
|
|
begin_test 'unmanaged client has details and back actions only'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
CLIENT_NAMES+=(unmanaged); CLIENT_KEYS+=(EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=); CLIENT_ADDRESSES+=(192.0.2.9/32); CLIENT_ENDPOINTS+=(-); CLIENT_HANDSHAKES+=(0); CLIENT_RX+=(0); CLIENT_TX+=(0); CLIENT_STATES+=(unmanaged); CLIENT_ORIGINS+=(unmanaged)
|
|
|
|
|
printf 'Детали\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="$TEST_ROOT/gum-script"; : >"$GUM_CHOICES_CAPTURE"
|
|
|
|
|
set +e; tui_client_action_flow unmanaged 1 >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'unmanaged details remain readable'
|
|
|
|
|
assert_contains 'Детали' "$(<"$GUM_CHOICES_CAPTURE")" 'unmanaged action list includes details'
|
|
|
|
|
assert_contains 'Назад' "$(<"$GUM_CHOICES_CAPTURE")" 'unmanaged action list includes back'
|
|
|
|
|
assert_equal '' "$(grep -E 'Показать QR|Удалить|Показать конфигурацию' "$GUM_CHOICES_CAPTURE" || true)" 'unmanaged actions exclude config/QR/remove'
|
|
|
|
|
|
|
|
|
|
begin_test 'managed remove action uses exact shared backend'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
cp -- "$TEST_ROOT/remove-dumps/4" "$TEST_ROOT/remove-dumps/3"
|
|
|
|
|
printf 'alice — BBBBBBBB\nУдалить\ny\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="$TEST_ROOT/gum-script"
|
|
|
|
|
set +e; output=$(tui_select_client 2>"${TEST_ROOT}/action-remove.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'managed remove action succeeds'
|
|
|
|
|
assert_equal 0 "$(grep -c 'PublicKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' "$WG_CONF" || true)" 'managed remove action removes selected peer'
|
|
|
|
|
assert_contains 'Клиент удалён' "$output" 'managed remove action reports outcome'
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey' "$TEST_MOCK_LOG" "${TEST_ROOT}/action-remove.err" || true)" 'managed action remains secret-free'
|
|
|
|
|
|
|
|
|
|
begin_test 'client action cancellation is harmless'
|
|
|
|
|
prepare_m10_remove_fixture
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
printf '\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="$TEST_ROOT/gum-script"
|
|
|
|
|
set +e; tui_select_client >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'client action cancellation returns normally'
|
|
|
|
|
;;
|
|
|
|
|
tui-backup)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
begin_test 'TUI backup action creates a real validated backup and reports its path'
|
|
|
|
|
printf 'Создать резервную копию\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_backup_flow 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'real backup action success'
|
|
|
|
|
assert_contains 'Резервная копия создана:' "$output" 'real backup path is displayed'
|
|
|
|
|
assert_equal 2 "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'real backup directory exists'
|
|
|
|
|
begin_test 'TUI backup cancellation and validation failure preserve state'
|
|
|
|
|
before_backup=$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | sort)
|
|
|
|
|
printf 'Отмена\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_backup_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'backup cancellation'
|
|
|
|
|
assert_equal "$before_backup" "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | sort)" 'cancel leaves backups unchanged'
|
|
|
|
|
mv -- "$BACKUP_DIR" "${TEST_ROOT}/backups-real"
|
|
|
|
|
ln -s -- "${TEST_ROOT}/backups-real" "$BACKUP_DIR"
|
|
|
|
|
printf 'Создать резервную копию\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_backup_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_failure "$status" 'unsafe backup root failure is preserved'
|
|
|
|
|
rm -- "$BACKUP_DIR"
|
|
|
|
|
mv -- "${TEST_ROOT}/backups-real" "$BACKUP_DIR"
|
|
|
|
|
;;
|
|
|
|
|
tui-restore)
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
begin_test 'TUI restore real success applies validated backup and audits it'
|
|
|
|
|
backup_path=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup_path##*/}
|
|
|
|
|
printf '\n# changed before restore\n' >>"$WG_CONF"
|
|
|
|
|
printf 'Восстановить резервную копию\n%s\n%s\n' "$restore_id" "$restore_id" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_backup_flow 2>"${TEST_ROOT}/restore.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'real restore succeeds'
|
|
|
|
|
assert_equal "$(sha256sum "$backup_path/wg.conf" | awk '{print $1}')" "$(sha256sum "$WG_CONF" | awk '{print $1}')" 'restore applies exact backup bytes'
|
|
|
|
|
assert_contains "$restore_id" "$(<"$GUM_PAGER_CAPTURE")" 'restore preview shows selected id'
|
|
|
|
|
assert_contains 'manual' "$(<"$GUM_PAGER_CAPTURE")" 'restore preview shows operation details'
|
|
|
|
|
assert_equal 2 "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'restore keeps current-state and selected backups'
|
|
|
|
|
assert_contains 'syncconf' "$(<"$TEST_MOCK_LOG")" 'restore applies runtime configuration'
|
|
|
|
|
assert_contains 'operation=restore' "$(<"$LOG_DIR/wg-manager.log")" 'restore audit is recorded'
|
|
|
|
|
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|AAAAAAAA|BBBBBBBB' "$TEST_MOCK_LOG" "$LOG_DIR/wg-manager.log" "$TEST_ROOT/restore.err" || true)" 'restore output and logs are secret-free'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI restore cancellation and typed mismatch preserve state'
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
backup_path=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup_path##*/}
|
|
|
|
|
before_hash=$(sha256sum "$WG_CONF"); before_count=$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
|
|
|
|
printf 'Восстановить резервную копию\n%s\nwrong-id\n' "$restore_id" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_backup_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'typed restore mismatch is harmless'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'typed mismatch preserves config'
|
|
|
|
|
assert_equal "$before_count" "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'typed mismatch creates no backup'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI restore rejects an unoffered selection without mutation'
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
backup_path=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup_path##*/}; before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
printf 'Восстановить резервную копию\nnot-a-backup\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_backup_flow >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_VALIDATION" "$status" 'unoffered restore selection is rejected'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'unoffered restore leaves config unchanged'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI restore apply failure rolls back and retains backup'
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
backup_path=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup_path##*/}; printf '\n# changed before failed restore\n' >>"$WG_CONF"; before_hash=$(sha256sum "$WG_CONF")
|
|
|
|
|
export WG_SYNC_FAIL_ONCE_FILE="${TEST_ROOT}/restore-sync-fail"
|
|
|
|
|
printf 'Восстановить резервную копию\n%s\n%s\n' "$restore_id" "$restore_id" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_backup_flow >/dev/null 2>"${TEST_ROOT}/restore-fail.err"; status=$?; set -e
|
|
|
|
|
unset WG_SYNC_FAIL_ONCE_FILE
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'restore apply failure returns operation'
|
|
|
|
|
assert_equal "$before_hash" "$(sha256sum "$WG_CONF")" 'restore apply failure restores attempted state'
|
|
|
|
|
assert_contains 'operation=rollback' "$(<"$LOG_DIR/wg-manager.log")" 'restore rollback is audited'
|
|
|
|
|
assert_contains 'operation=restore target='"$restore_id"' client='"$restore_id"' key_suffix= outcome=failed error=transaction-failed' "$(<"$LOG_DIR/wg-manager.log")" 'restore failure is audited'
|
|
|
|
|
assert_equal 2 "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'restore failure retains backup copies'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI screen propagates critical restore rollback'
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
backup_path=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup_path##*/}; printf '\n# changed before critical restore\n' >>"$WG_CONF"
|
|
|
|
|
export WG_SYNC_FAIL_ONCE_FILE="${TEST_ROOT}/restore-critical-sync" TRANSACTION_ROLLBACK_REPLACE_FAIL=1
|
|
|
|
|
printf 'Резервные копии\nВосстановить резервную копию\n%s\n%s\nВыход\n' "$restore_id" "$restore_id" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_screen >/dev/null 2>"${TEST_ROOT}/restore-critical.err"; status=$?; set -e
|
|
|
|
|
unset WG_SYNC_FAIL_ONCE_FILE TRANSACTION_ROLLBACK_REPLACE_FAIL
|
|
|
|
|
assert_equal "$EXIT_ROLLBACK" "$status" 'critical restore rollback exits nonzero'
|
|
|
|
|
assert_contains 'КРИТИЧНО' "$(<"${TEST_ROOT}/restore-critical.err")" 'critical restore diagnostic is preserved'
|
|
|
|
|
;;
|
|
|
|
|
tui-backend-failure)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
begin_test 'TUI ordinary backup failure remains readable and bounded'
|
|
|
|
|
rm -rf -- "$BACKUP_DIR"
|
|
|
|
|
printf 'not a directory\n' >"$BACKUP_DIR"
|
|
|
|
|
before_backup=0
|
|
|
|
|
printf 'Резервные копии\nСоздать резервную копию\nВыход\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; output=$(tui_screen 2>"${TEST_ROOT}/backend-failure.err"); status=$?; set -e
|
|
|
|
|
rm -- "$BACKUP_DIR"
|
|
|
|
|
mkdir -- "$BACKUP_DIR"
|
|
|
|
|
chmod 700 -- "$BACKUP_DIR"
|
|
|
|
|
assert_equal 0 "$status" 'ordinary backend failure returns to menu'
|
|
|
|
|
assert_contains 'Предупреждение' "$(<"${TEST_ROOT}/backend-failure.err")" 'ordinary failure has context'
|
|
|
|
|
assert_equal "$before_backup" "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'failed backup creates no entry'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI repeated real backend failures are bounded'
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
rm -rf -- "$BACKUP_DIR"
|
|
|
|
|
printf 'not a directory\n' >"$BACKUP_DIR"
|
|
|
|
|
before_backup=0
|
|
|
|
|
printf 'Резервные копии\nСоздать резервную копию\nРезервные копии\nСоздать резервную копию\nРезервные копии\nСоздать резервную копию\nВыход\n' >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_screen >"${TEST_ROOT}/bounded.out" 2>"${TEST_ROOT}/bounded.err"; status=$?; set -e
|
|
|
|
|
rm -- "$BACKUP_DIR"
|
|
|
|
|
mkdir -- "$BACKUP_DIR"
|
|
|
|
|
chmod 700 -- "$BACKUP_DIR"
|
|
|
|
|
assert_equal "$EXIT_OPERATION" "$status" 'three real failures terminate with operation status'
|
|
|
|
|
assert_contains 'TUI завершён' "$(<"${TEST_ROOT}/bounded.err")" 'bounded failure diagnostic'
|
|
|
|
|
assert_equal "$before_backup" "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)" 'repeated failures create no backups'
|
|
|
|
|
|
|
|
|
|
begin_test 'TUI critical restore rollback propagates without stubs'
|
|
|
|
|
prepare_m10_restore_fixture
|
|
|
|
|
local critical_backup critical_id
|
|
|
|
|
critical_backup=$(create_backup "$WG_CONF" manual "$WG_INTERFACE")
|
|
|
|
|
critical_id=${critical_backup##*/}
|
|
|
|
|
printf '\n# changed before critical TUI restore\n' >>"$WG_CONF"
|
|
|
|
|
export WG_SYNC_FAIL_ONCE_FILE="${TEST_ROOT}/critical-sync" TRANSACTION_ROLLBACK_REPLACE_FAIL=1
|
|
|
|
|
printf 'Резервные копии\nВосстановить резервную копию\n%s\n%s\nВыход\n' "$critical_id" "$critical_id" >"${TEST_ROOT}/gum-script"
|
|
|
|
|
export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_screen >"${TEST_ROOT}/critical.out" 2>"${TEST_ROOT}/critical.err"; status=$?; set -e
|
|
|
|
|
unset WG_SYNC_FAIL_ONCE_FILE TRANSACTION_ROLLBACK_REPLACE_FAIL
|
|
|
|
|
assert_equal "$EXIT_ROLLBACK" "$status" 'critical rollback exits TUI immediately'
|
|
|
|
|
assert_contains 'КРИТИЧНО' "$(<"${TEST_ROOT}/critical.err")" 'critical rollback diagnostic is preserved'
|
|
|
|
|
assert_equal '' "${TUI_GUM_PATH:-}" 'critical route clears gum state'
|
|
|
|
|
;;
|
|
|
|
|
tui-no-secret-args)
|
|
|
|
|
prepare_m10_add_fixture
|
|
|
|
|
begin_test 'successful add keeps secrets out of TUI arguments and audit'
|
|
|
|
|
printf 'secretcheck\nПолный туннель\ny\ny\nn\n' >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; add_output=$(tui_add_flow 2>"${TEST_ROOT}/secret-add.err"); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'real add for secret boundary succeeds'
|
|
|
|
|
assert_equal 600 "$(stat -c '%a' "$CLIENT_DIR/secretcheck.conf")" 'generated config is restrictive'
|
|
|
|
|
assert_contains 'secretcheck.conf' "$add_output" 'successful add reports validated path'
|
|
|
|
|
secret_stream=$(cat "$TEST_MOCK_LOG" "$LOG_DIR/wg-manager.log" "${TEST_ROOT}/secret-add.err"; printf '%s\n' "$add_output")
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey|\[Interface\]|\[Peer\]' <<<"$secret_stream" || true)" 'successful add outputs and logs are secret-free'
|
|
|
|
|
|
|
|
|
|
unset WG_MOCK_DUMP_SEQUENCE_DIR
|
|
|
|
|
export WG_MOCK_DUMP_FILE="${TEST_ROOT}/dump-sequence/1"
|
|
|
|
|
load_client_model >/dev/null
|
|
|
|
|
local secret_label
|
|
|
|
|
secret_label='secretcheck — FFFFFFFF'
|
|
|
|
|
begin_test 'confirmed config is sent only to pager stdin'
|
|
|
|
|
: >"$GUM_PAGER_CAPTURE"; : >"$TEST_MOCK_LOG"
|
|
|
|
|
printf '%s\nПоказать конфигурацию\ny\n' "$secret_label" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_select_client >"${TEST_ROOT}/secret-config.out" 2>"${TEST_ROOT}/secret-config.err"; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'confirmed config action succeeds'
|
|
|
|
|
assert_contains 'PrivateKey' "$(<"$GUM_PAGER_CAPTURE")" 'confirmed config reaches pager stdin'
|
|
|
|
|
secret_stream=$(cat "$TEST_MOCK_LOG" "$LOG_DIR/wg-manager.log" "${TEST_ROOT}/secret-config.out" "${TEST_ROOT}/secret-config.err")
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey|\[Interface\]|\[Peer\]' <<<"$secret_stream" || true)" 'config secrets absent from args/logs/output'
|
|
|
|
|
|
|
|
|
|
begin_test 'confirmed QR receives config on stdin without leaking it to gum'
|
|
|
|
|
export QR_MOCK_EXPECTED_FILE="$CLIENT_DIR/secretcheck.conf"
|
|
|
|
|
: >"$TEST_MOCK_LOG"
|
|
|
|
|
printf '%s\nПоказать QR\ny\n' "$secret_label" >"${TEST_ROOT}/gum-script"; export GUM_SCRIPT_FILE="${TEST_ROOT}/gum-script"
|
|
|
|
|
set +e; tui_select_client >"${TEST_ROOT}/secret-qr.out" 2>"${TEST_ROOT}/secret-qr.err"; status=$?; set -e
|
|
|
|
|
unset QR_MOCK_EXPECTED_FILE
|
|
|
|
|
assert_equal 0 "$status" 'confirmed QR action succeeds'
|
|
|
|
|
assert_contains 'MOCK-QR' "$(<"${TEST_ROOT}/secret-qr.out")" 'QR output is returned'
|
|
|
|
|
assert_contains 'qrencode -t ansiutf8' "$(<"$TEST_MOCK_LOG")" 'QR argv is exact and nonsecret'
|
|
|
|
|
secret_stream=$(cat "$TEST_MOCK_LOG" "$LOG_DIR/wg-manager.log" "${TEST_ROOT}/secret-qr.out" "${TEST_ROOT}/secret-qr.err")
|
|
|
|
|
assert_equal '' "$(grep -E 'EEEE|GGGG|PrivateKey|PresharedKey|\[Interface\]|\[Peer\]' <<<"$secret_stream" || true)" 'QR secrets absent from args/logs/output'
|
|
|
|
|
;;
|
|
|
|
|
tui-no-gum)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
export WG_MANAGER_GUM="${TEST_ROOT}/missing-gum"
|
|
|
|
|
begin_test 'TUI without gum reports dependency while CLI remains independent'
|
|
|
|
|
set +e; output=$(tui_screen 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_DEPENDENCY" "$status" 'missing gum status'
|
|
|
|
|
assert_contains 'WG_MANAGER_GUM' "$output" 'missing gum diagnostic'
|
|
|
|
|
assert_equal '' "${TUI_GUM_PATH:-}" 'gum path is cleared after dependency failure'
|
|
|
|
|
set +e; output=$(main status 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'CLI status remains independent of gum'
|
|
|
|
|
;;
|
|
|
|
|
tui-non-tty)
|
|
|
|
|
prepare_m9_fixture
|
|
|
|
|
unset WG_MANAGER_TEST_TTY
|
|
|
|
|
begin_test 'TUI refuses noninteractive input before gum output'
|
|
|
|
|
set +e; output=$(tui_screen 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'non-TTY status'
|
|
|
|
|
assert_contains 'интерактивный терминал' "$output" 'non-TTY diagnostic'
|
|
|
|
|
assert_equal '' "$(<"$TEST_MOCK_LOG")" 'non-TTY has no gum invocation'
|
|
|
|
|
for tty_mode in stdin stdout none; do
|
|
|
|
|
export WG_MANAGER_TEST_TTY="$tty_mode"
|
|
|
|
|
set +e; tui_is_terminal >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_failure "$status" "TTY режим ${tty_mode} отклонён"
|
|
|
|
|
done
|
|
|
|
|
export WG_MANAGER_TEST_TTY=both
|
|
|
|
|
set +e; tui_is_terminal >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal 0 "$status" 'оба потока TTY принимаются в изолированном режиме'
|
|
|
|
|
set +e; main tui extra >/dev/null 2>&1; status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'tui extra argument is usage'
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
unset GUM_SCRIPT_FILE WG_MANAGER_GUM WG_MANAGER_TEST_TTY WG_MANAGER_IPV4_FORWARD_FILE WG_MANAGER_IPV6_FORWARD_FILE GUM_STYLE_CAPTURE GUM_PAGER_CAPTURE GUM_CHOICES_CAPTURE GUM_FAIL_CAPABILITY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_dependency_tests() {
|
|
|
|
@@ -2409,6 +3175,9 @@ EOF
|
|
|
|
|
set +e; output=$(remove_client alice extra 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'лишний аргумент remove отклонён'
|
|
|
|
|
assert_equal "$remove_before" "$(sha256sum -- "$fixture")" 'лишний аргумент не меняет конфиг'
|
|
|
|
|
set +e; output=$(remove_client alice --yes --expected-key BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB= 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'публичный remove не принимает ключ в аргументах'
|
|
|
|
|
assert_equal "$remove_before" "$(sha256sum -- "$fixture")" 'запрещённый ключ не меняет конфиг'
|
|
|
|
|
set +e; output=$(/bin/bash "$PROJECT_DIR/wg-manager.sh" remove 2>&1); status=$?; set -e
|
|
|
|
|
assert_equal "$EXIT_USAGE" "$status" 'отсутствующее имя remove отклонено'
|
|
|
|
|
set +e; output=$(remove_client absent --yes 2>&1); status=$?; set -e
|
|
|
|
@@ -3324,6 +4093,8 @@ elif [[ $test_group == add-client || $test_group == address-allocation || $test_
|
|
|
|
|
run_m4_tests
|
|
|
|
|
elif [[ $test_group == remove-client || $test_group == remove-cancel || $test_group == remove-exact-block || $test_group == remove-rollback || $test_group == archive-client || $test_group == archive-path-safety || $test_group == remove-preserves-neighbors ]]; then
|
|
|
|
|
run_m6_tests
|
|
|
|
|
elif [[ $test_group == gum-resolution || $test_group == tui-routing || $test_group == tui-cancel || $test_group == tui-read-only || $test_group == tui-no-gum || $test_group == tui-non-tty || $test_group == tui-add || $test_group == tui-remove || $test_group == tui-client-actions || $test_group == tui-backup || $test_group == tui-restore || $test_group == tui-backend-failure || $test_group == tui-no-secret-args ]]; then
|
|
|
|
|
run_m9_tests
|
|
|
|
|
elif [[ $test_group == audit || $test_group == audit-no-secrets || $test_group == backup-list || $test_group == restore || $test_group == restore-invalid || $test_group == restore-rollback || $test_group == backup-path-safety || $test_group == runtime-file-modes ]]; then
|
|
|
|
|
run_m8_tests
|
|
|
|
|
elif [[ $test_group == diagnostics || $test_group == diagnostics-systemd || $test_group == diagnostics-firewall || $test_group == diagnostics-forwarding || $test_group == diagnostics-runtime-diff || $test_group == diagnostics-read-only || $test_group == diagnostics-exit-codes ]]; then
|
|
|
|
@@ -3350,6 +4121,11 @@ else
|
|
|
|
|
run_m8_tests
|
|
|
|
|
done
|
|
|
|
|
unset m8_group
|
|
|
|
|
for m9_group in gum-resolution tui-routing tui-cancel tui-read-only tui-no-gum tui-non-tty tui-add tui-remove tui-client-actions tui-backup tui-restore tui-backend-failure tui-no-secret-args; do
|
|
|
|
|
test_group=$m9_group
|
|
|
|
|
run_m9_tests
|
|
|
|
|
done
|
|
|
|
|
unset m9_group
|
|
|
|
|
test_group=''
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|