Files
dotfiles/tests/test_proxy_client_install.sh
T
ddadmin f679515a8f feat(proxy-client): добавлен самостоятельный установщик
- Зачем:
  - пакет должен устанавливаться отдельно от dotfiles без ручной раскладки и опасных замен.
- Что:
  - добавлен безопасный установщик со Stow и прямыми симлинками.
  - добавлены идемпотентная настройка Bash и защита машинных файлов.
  - обновлены инструкция и полный набор интеграционных проверок.
- Проверка:
  - env XDG_STATE_HOME=<временный каталог> bash tests/test_proxy_client_full.sh.
2026-07-21 22:22:04 +03:00

403 lines
16 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TEST_TMP_ROOT="$(mktemp -d /tmp/proxy-client-install-test.XXXXXX)"
trap 'rm -rf "$TEST_TMP_ROOT"' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1
}
assert_link_points_to() {
local link_path="$1" expected_path="$2" actual_path
[ -L "$link_path" ] || fail "Expected symlink: $link_path"
actual_path="$(readlink -f "$link_path")"
[ "$actual_path" = "$expected_path" ] \
|| fail "$link_path points to $actual_path, expected $expected_path"
}
assert_not_exists() {
local path="$1"
[ ! -e "$path" ] && [ ! -L "$path" ] || fail "Path should not exist: $path"
}
setup_package() {
local fixture_root="$1" package_dir="$fixture_root/source with spaces/proxy-client"
mkdir -p \
"$package_dir/.local/bin" \
"$package_dir/.config/proxy-client"
cp -a \
"$REPO_ROOT/proxy-client/.local/bin/proxy-client" \
"$package_dir/.local/bin/proxy-client"
cp -a \
"$REPO_ROOT/proxy-client/.config/proxy-client/shell.bash" \
"$REPO_ROOT/proxy-client/.config/proxy-client/examples" \
"$package_dir/.config/proxy-client/"
cp -a \
"$REPO_ROOT/proxy-client/.stow-local-ignore" \
"$REPO_ROOT/proxy-client/README.md" \
"$REPO_ROOT/proxy-client/install.sh" \
"$package_dir/"
printf '%s\n' 'MACHINE_CONFIG_SENTINEL' > "$package_dir/.config/proxy-client/config"
printf '%s\n' 'PRIVATE_PROFILE_SENTINEL' > "$package_dir/.config/proxy-client/default.url"
printf '%s\n' "$package_dir"
}
run_package_install() {
local package_dir="$1" home_dir="$2" stow_bin="$3"
shift 3
(
cd "$TEST_TMP_ROOT"
HOME="$home_dir" STOW_BIN="$stow_bin" \
"$package_dir/install.sh" "$@"
)
}
test_installs_same_public_artifacts_without_stow() {
local fixture_root package_dir home_dir output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
output="$(
run_package_install "$package_dir" "$home_dir" missing-stow --no-shell-setup
)"
printf '%s' "$output" | grep -Fq 'без Stow' \
|| fail "Expected direct-symlink method in output"
printf '%s' "$output" | grep -Fq \
'source ~/.config/proxy-client/shell.bash' \
|| fail "Expected direct shell-module hint with --no-shell-setup"
[ -d "$home_dir/.local/bin" ] && [ ! -L "$home_dir/.local/bin" ] \
|| fail "Expected ~/.local/bin to be a real directory"
[ -d "$home_dir/.config/proxy-client" ] \
&& [ ! -L "$home_dir/.config/proxy-client" ] \
|| fail "Expected proxy-client config home to be a real directory"
assert_link_points_to \
"$home_dir/.local/bin/proxy-client" \
"$package_dir/.local/bin/proxy-client"
assert_link_points_to \
"$home_dir/.config/proxy-client/shell.bash" \
"$package_dir/.config/proxy-client/shell.bash"
assert_link_points_to \
"$home_dir/.config/proxy-client/examples" \
"$package_dir/.config/proxy-client/examples"
assert_not_exists "$home_dir/.config/proxy-client/config"
assert_not_exists "$home_dir/.config/proxy-client/default.url"
assert_not_exists "$home_dir/README.md"
assert_not_exists "$home_dir/install.sh"
assert_not_exists "$home_dir/.bashrc"
}
test_configures_bash_once_by_default() {
local fixture_root package_dir home_dir output marker_count path_count source_count
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
output="$(run_package_install "$package_dir" "$home_dir" missing-stow)"
marker_count="$(grep -Fxc '# >>> proxy-client >>>' "$home_dir/.bashrc")"
path_count="$(grep -Fc 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc")"
source_count="$(
grep -Fc \
'[ -f "$HOME/.config/proxy-client/shell.bash" ] && source "$HOME/.config/proxy-client/shell.bash"' \
"$home_dir/.bashrc"
)"
[ "$marker_count" -eq 1 ] || fail "Expected one managed Bash block"
[ "$path_count" -eq 1 ] || fail "Expected one ~/.local/bin PATH line"
[ "$source_count" -eq 1 ] || fail "Expected one shell module source line"
printf '%s' "$output" | grep -Fq 'source ~/.bashrc' \
|| fail "Expected current-session reload hint"
}
test_installs_same_public_artifacts_with_stow() {
local fixture_root package_dir home_dir output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
output="$(run_package_install "$package_dir" "$home_dir" stow --no-shell-setup)"
printf '%s' "$output" | grep -Fq 'с помощью Stow' \
|| fail "Expected Stow method in output"
[ -d "$home_dir/.local/bin" ] && [ ! -L "$home_dir/.local/bin" ] \
|| fail "Expected ~/.local/bin to be a real directory"
[ -d "$home_dir/.config/proxy-client" ] \
&& [ ! -L "$home_dir/.config/proxy-client" ] \
|| fail "Expected proxy-client config home to be a real directory"
assert_link_points_to \
"$home_dir/.local/bin/proxy-client" \
"$package_dir/.local/bin/proxy-client"
assert_link_points_to \
"$home_dir/.config/proxy-client/shell.bash" \
"$package_dir/.config/proxy-client/shell.bash"
assert_link_points_to \
"$home_dir/.config/proxy-client/examples" \
"$package_dir/.config/proxy-client/examples"
assert_not_exists "$home_dir/.config/proxy-client/config"
assert_not_exists "$home_dir/.config/proxy-client/default.url"
assert_not_exists "$home_dir/README.md"
assert_not_exists "$home_dir/install.sh"
}
test_rejects_linked_parent_directories_before_changes() {
local fixture_root package_dir home_dir external_local external_config output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
external_local="$fixture_root/external-local"
external_config="$fixture_root/external-config"
mkdir -p "$home_dir" "$external_local" "$external_config"
ln -s "$external_local" "$home_dir/.local"
ln -s "$external_config" "$home_dir/.config"
if output="$(
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
)"; then
fail "Expected linked parent directories to be rejected"
fi
printf '%s' "$output" | grep -Fq "$home_dir/.local" \
|| fail "Expected ~/.local conflict in output"
printf '%s' "$output" | grep -Fq "$home_dir/.config" \
|| fail "Expected ~/.config conflict in output"
assert_not_exists "$external_local/bin"
assert_not_exists "$external_config/proxy-client"
assert_not_exists "$home_dir/.bashrc"
}
test_reports_all_target_conflicts_without_partial_install() {
local fixture_root package_dir home_dir other_target output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
other_target="$fixture_root/other-target"
mkdir -p "$home_dir/.local/bin" "$home_dir/.config/proxy-client/shell.bash"
printf '%s\n' 'KEEP_CLI' > "$home_dir/.local/bin/proxy-client"
printf '%s\n' 'KEEP_TARGET' > "$other_target"
ln -s "$other_target" "$home_dir/.config/proxy-client/examples"
if output="$(
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
)"; then
fail "Expected all target conflicts to be rejected"
fi
printf '%s' "$output" | grep -Fq "$home_dir/.local/bin/proxy-client" \
|| fail "Expected CLI conflict in output"
printf '%s' "$output" | grep -Fq "$home_dir/.config/proxy-client/shell.bash" \
|| fail "Expected shell module conflict in output"
printf '%s' "$output" | grep -Fq "$home_dir/.config/proxy-client/examples" \
|| fail "Expected examples conflict in output"
grep -Fq 'KEEP_CLI' "$home_dir/.local/bin/proxy-client" \
|| fail "Expected conflicting CLI file to stay unchanged"
[ -d "$home_dir/.config/proxy-client/shell.bash" ] \
|| fail "Expected conflicting shell directory to stay unchanged"
assert_link_points_to "$home_dir/.config/proxy-client/examples" "$other_target"
assert_not_exists "$home_dir/.bashrc"
}
test_rejects_broken_link_without_creating_other_targets() {
local fixture_root package_dir home_dir output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir/.local/bin"
ln -s "$fixture_root/missing-cli" "$home_dir/.local/bin/proxy-client"
if output="$(
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
)"; then
fail "Expected broken target link to be rejected"
fi
printf '%s' "$output" | grep -Fq "$home_dir/.local/bin/proxy-client" \
|| fail "Expected broken CLI link in output"
[ -L "$home_dir/.local/bin/proxy-client" ] \
|| fail "Expected broken CLI link to stay unchanged"
assert_not_exists "$home_dir/.config"
assert_not_exists "$home_dir/.bashrc"
}
test_preserves_machine_files_with_both_methods() {
local stow_bin fixture_root package_dir home_dir
for stow_bin in missing-stow stow; do
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir/.config/proxy-client"
printf '%s\n' 'HOME_CONFIG_SENTINEL' \
> "$home_dir/.config/proxy-client/config"
printf '%s\n' 'HOME_PROFILE_SENTINEL' \
> "$home_dir/.config/proxy-client/default.url"
run_package_install "$package_dir" "$home_dir" "$stow_bin" \
--no-shell-setup >/dev/null
[ ! -L "$home_dir/.config/proxy-client/config" ] \
|| fail "Machine config must not become a symlink"
[ ! -L "$home_dir/.config/proxy-client/default.url" ] \
|| fail "Private profile must not become a symlink"
grep -Fq 'HOME_CONFIG_SENTINEL' "$home_dir/.config/proxy-client/config" \
|| fail "Machine config changed"
grep -Fq 'HOME_PROFILE_SENTINEL' "$home_dir/.config/proxy-client/default.url" \
|| fail "Private profile changed"
done
}
test_accepts_existing_links_when_method_changes() {
local fixture_root package_dir home_dir
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
run_package_install "$package_dir" "$home_dir" missing-stow \
--no-shell-setup >/dev/null
run_package_install "$package_dir" "$home_dir" stow \
--no-shell-setup >/dev/null
assert_link_points_to \
"$home_dir/.local/bin/proxy-client" \
"$package_dir/.local/bin/proxy-client"
assert_link_points_to \
"$home_dir/.config/proxy-client/shell.bash" \
"$package_dir/.config/proxy-client/shell.bash"
assert_link_points_to \
"$home_dir/.config/proxy-client/examples" \
"$package_dir/.config/proxy-client/examples"
}
test_stow_completes_partial_correct_installation() {
local fixture_root package_dir home_dir
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
run_package_install "$package_dir" "$home_dir" missing-stow \
--no-shell-setup >/dev/null
rm "$home_dir/.config/proxy-client/shell.bash"
run_package_install "$package_dir" "$home_dir" stow \
--no-shell-setup >/dev/null
assert_link_points_to \
"$home_dir/.local/bin/proxy-client" \
"$package_dir/.local/bin/proxy-client"
assert_link_points_to \
"$home_dir/.config/proxy-client/shell.bash" \
"$package_dir/.config/proxy-client/shell.bash"
assert_link_points_to \
"$home_dir/.config/proxy-client/examples" \
"$package_dir/.config/proxy-client/examples"
}
test_does_not_duplicate_full_dotfiles_shell_setup() {
local fixture_root package_dir home_dir source_count
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
printf '%s\n' \
'[ -f ~/.config/proxy-client/shell.bash ] && source ~/.config/proxy-client/shell.bash' \
> "$home_dir/.bash_custom"
printf '%s\n' '[ -f ~/.bash_custom ] && source ~/.bash_custom' \
> "$home_dir/.bashrc"
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
grep -Fq '# >>> proxy-client >>>' "$home_dir/.bashrc" \
|| fail "Expected managed PATH block with full dotfiles"
grep -Fq 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc" \
|| fail "Expected ~/.local/bin setup with full dotfiles"
source_count="$(grep -Fc 'proxy-client/shell.bash' "$home_dir/.bashrc" || true)"
[ "$source_count" -eq 0 ] \
|| fail "Standalone block duplicated full dotfiles module source"
}
test_rejects_damaged_managed_bash_block_before_changes() {
local fixture_root package_dir home_dir output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
printf '%s\n' '# >>> proxy-client >>>' > "$home_dir/.bashrc"
if output="$(
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
)"; then
fail "Expected damaged managed Bash block to be rejected"
fi
printf '%s' "$output" | grep -Fq "$home_dir/.bashrc" \
|| fail "Expected ~/.bashrc conflict in output"
[ "$(wc -l < "$home_dir/.bashrc")" -eq 1 ] \
|| fail "Expected damaged Bash block to stay unchanged"
assert_not_exists "$home_dir/.local"
assert_not_exists "$home_dir/.config"
}
test_does_not_duplicate_existing_local_bin_path() {
local fixture_root package_dir home_dir path_count source_count
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' > "$home_dir/.bashrc"
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
path_count="$(grep -Fc 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc")"
source_count="$(grep -Fc 'proxy-client/shell.bash' "$home_dir/.bashrc")"
[ "$path_count" -eq 1 ] || fail "Existing ~/.local/bin PATH line was duplicated"
[ "$source_count" -eq 1 ] || fail "Expected shell module source in managed block"
}
test_rejects_incomplete_source_before_changes() {
local fixture_root package_dir home_dir output
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
package_dir="$(setup_package "$fixture_root")"
home_dir="$fixture_root/home"
mkdir -p "$home_dir"
rm "$package_dir/.config/proxy-client/shell.bash"
if output="$(
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
)"; then
fail "Expected incomplete source package to be rejected"
fi
printf '%s' "$output" | grep -Fq 'исходного пакета' \
|| fail "Expected source package error"
assert_not_exists "$home_dir/.local"
assert_not_exists "$home_dir/.config"
assert_not_exists "$home_dir/.bashrc"
}
main() {
test_installs_same_public_artifacts_without_stow
test_configures_bash_once_by_default
test_installs_same_public_artifacts_with_stow
test_rejects_linked_parent_directories_before_changes
test_reports_all_target_conflicts_without_partial_install
test_rejects_broken_link_without_creating_other_targets
test_preserves_machine_files_with_both_methods
test_accepts_existing_links_when_method_changes
test_stow_completes_partial_correct_installation
test_does_not_duplicate_full_dotfiles_shell_setup
test_rejects_damaged_managed_bash_block_before_changes
test_does_not_duplicate_existing_local_bin_path
test_rejects_incomplete_source_before_changes
echo "All standalone proxy-client install tests passed"
}
main "$@"