From 42fc2e6acb46c8af7c3ea9cef047ada61bad4a9a Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 6 Jul 2026 19:20:25 +0200 Subject: [PATCH] job4: SPEC-03 curated-config-guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New lib/tests/curated-config-guard.test.sh (+4 assertions). Extracts restore_curated_configs() from install-plugins.sh AT TEST RUNTIME via awk '/^restore_curated_configs\(\) \{/,\/^\}/' (verified single- occurrence, column-0 closing brace) so drift in the real script propagates into the test instead of testing a frozen copy. Harness defines GUARDED_CONFIGS/CFG_SNAPSHOT/REPO/info() itself (the array literal at install-plugins.sh:41 is outside the extracted range). Sandbox REPO with the 3 fake guarded files + a pre-populated CFG_SNAPSHOT; mutates CLAUDE.md only (simulated installer drift); asserts: mutated file restored byte-identical (cmp -s), the other two guarded files' content unchanged (not touched by the restore loop), snapshot dir removed. Closes J4-03 (CRITICAL): the guard against graphify's installer clobbering CLAUDE.md/settings.json had zero test coverage. Mutation (copy of install-plugins.sh, lean scratch — only that one file, not the whole repo/.git): inverted the cmp condition (`! cmp -s` → `cmp -s`) at the line the report names. RED: T1 fails (the mutated file no longer gets restored — the inverted condition only copies when already identical, a no-op, and skips restoration exactly when it's needed). T2/T3/T4 stay green, confirming the mutation is localized to the restore path. GREEN: real repo unmutated, PASS=4 FAIL=0, shellcheck clean. --- lib/tests/curated-config-guard.test.sh | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lib/tests/curated-config-guard.test.sh diff --git a/lib/tests/curated-config-guard.test.sh b/lib/tests/curated-config-guard.test.sh new file mode 100644 index 0000000..bff7133 --- /dev/null +++ b/lib/tests/curated-config-guard.test.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# lib/tests/curated-config-guard.test.sh — SPEC-03 (J4-03). +# +# Drives install-plugins.sh's restore_curated_configs() in a sandbox. The SUT +# is extracted from the REAL script AT TEST RUNTIME (awk range, verified +# single-occurrence + column-0 closing brace) so drift in install-plugins.sh +# propagates into this test instead of testing a stale copy. GUARDED_CONFIGS, +# CFG_SNAPSHOT, REPO and an info() stub are defined here — the array literal +# at install-plugins.sh:41 is outside the extracted range. +set -u +INSTALL_SH="$(cd "$(dirname "$0")/../.." && pwd)/install-plugins.sh" +pass=0; fail=0 +check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1)); + printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; } + +SUT="$(mktemp)" +awk '/^restore_curated_configs\(\) \{/,/^\}/' "$INSTALL_SH" > "$SUT" + +REPO="$(mktemp -d)" +CFG_SNAPSHOT="$(mktemp -d)" +EXPECT="$(mktemp -d)" # our own reference copy — independent of CFG_SNAPSHOT (SUT rm -rf's it) +GUARDED_CONFIGS=("CLAUDE.md" ".claude/settings.json" "settings.json") +info() { :; } # stub — extracted body calls info(), irrelevant to the assertions + +mkdir -p "$REPO/.claude" +printf 'CLAUDE original\n' > "$REPO/CLAUDE.md" +printf '{"a":1}\n' > "$REPO/.claude/settings.json" +printf '{"b":2}\n' > "$REPO/settings.json" + +for f in "${GUARDED_CONFIGS[@]}"; do + mkdir -p "$CFG_SNAPSHOT/$(dirname "$f")" "$EXPECT/$(dirname "$f")" + cp "$REPO/$f" "$CFG_SNAPSHOT/$f" + cp "$REPO/$f" "$EXPECT/$f" +done + +# simulate installer drift: mutate ONE guarded file, leave the other two alone +printf 'CLAUDE CLOBBERED BY INSTALLER\n' > "$REPO/CLAUDE.md" + +# shellcheck source=/dev/null +source "$SUT" +restore_curated_configs + +cmp -s "$REPO/CLAUDE.md" "$EXPECT/CLAUDE.md" +check T1-mutated-file-restored "$?" 0 +cmp -s "$REPO/.claude/settings.json" "$EXPECT/.claude/settings.json" +check T2-untouched-local-settings-unchanged "$?" 0 +cmp -s "$REPO/settings.json" "$EXPECT/settings.json" +check T3-untouched-settings-unchanged "$?" 0 +[ ! -d "$CFG_SNAPSHOT" ] +check T4-snapshot-dir-removed "$?" 0 + +printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]