aboutsummaryrefslogtreecommitdiff
path: root/scripts/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-31 11:43:03 -0500
committerCraig Jennings <c@cjennings.net>2026-05-31 11:43:03 -0500
commitb46619cd17ed4e36f2e59c1b600078521b2049ef (patch)
treef128aeef3f0f679a400595c896a98618266706d9 /scripts/tests
parent3640664e0fa11d7eb99c2900df57734b411e2d2b (diff)
downloadrulesets-b46619cd17ed4e36f2e59c1b600078521b2049ef.tar.gz
rulesets-b46619cd17ed4e36f2e59c1b600078521b2049ef.zip
feat(elisp): add coverage-summary to the Elisp bundle with missing-file detection
A line-weighted coverage total has a blind spot: a module no test loads never shows up in the SimpleCov report, so it can't drag the number down. The suite looks healthier than it is. This adds a summary that counts every source file on disk against the report and treats an absent file as 0%, weighting the project number by file instead of by line so untested modules stay visible. The script ships at languages/elisp/claude/scripts/coverage-summary.el, self-contained on stock Emacs (just the built-in json). It parses the undercover SimpleCov shape directly rather than depending on the editor's coverage engine, so it runs anywhere the bundle lands. I proved it against a real 103-file report: 93 tracked, 27 untested modules surfaced, project number 66.4%. Delivery follows the bundle convention. The script lives under the gitignored .claude/ footprint and gets auto-fixed on drift by sync-language-bundle.sh, which I made generic for any claude/scripts/* rather than coverage-specific. The Makefile targets ship as a project-owned fragment (languages/elisp/coverage-makefile.txt) that install-lang.sh seeds at the project root and sync drops into .ai/inbox/ when that convention exists. The bundle never edits the project's own Makefile. Tests: 12 ERT for the kernel (Normal/Boundary/Error per function), wired into make test via a new languages/*/tests/ discovery path, plus bats for the sync auto-fix and the inbox-drop guards. This is the Elisp pilot. The pattern is proven, so fanning out to Python, Go, and TypeScript is now a follow-up. Each one needs only its own parser and fragment. The plumbing is already generic.
Diffstat (limited to 'scripts/tests')
-rw-r--r--scripts/tests/install-lang.bats17
-rw-r--r--scripts/tests/sync-language-bundle.bats62
2 files changed, 79 insertions, 0 deletions
diff --git a/scripts/tests/install-lang.bats b/scripts/tests/install-lang.bats
index 523be99..a26c3d5 100644
--- a/scripts/tests/install-lang.bats
+++ b/scripts/tests/install-lang.bats
@@ -25,6 +25,23 @@ teardown() {
[ -d "$PROJECT/.claude/rules" ]
[ -d "$PROJECT/githooks" ]
[ -f "$PROJECT/CLAUDE.md" ]
+ # The coverage-summary script ships inside the gitignored .claude footprint.
+ [ -f "$PROJECT/.claude/scripts/coverage-summary.el" ]
+}
+
+@test "install-lang elisp: seeds the project-owned coverage Makefile fragment" {
+ run bash "$INSTALL_LANG" elisp "$PROJECT"
+
+ [ "$status" -eq 0 ]
+ [ -f "$PROJECT/coverage-makefile.txt" ]
+}
+
+@test "install-lang elisp: does not overwrite an existing fragment without FORCE" {
+ echo "MY OWN VERSION" > "$PROJECT/coverage-makefile.txt"
+ run bash "$INSTALL_LANG" elisp "$PROJECT"
+
+ [ "$status" -eq 0 ]
+ grep -qxF "MY OWN VERSION" "$PROJECT/coverage-makefile.txt"
}
@test "install-lang elisp: gitignores the full Claude tooling footprint" {
diff --git a/scripts/tests/sync-language-bundle.bats b/scripts/tests/sync-language-bundle.bats
index e641646..5e3b912 100644
--- a/scripts/tests/sync-language-bundle.bats
+++ b/scripts/tests/sync-language-bundle.bats
@@ -29,6 +29,10 @@ install_bundle() {
mkdir -p "$proj/.claude/hooks"
cp -r "$REAL_REPO/languages/$lang/claude/hooks/." "$proj/.claude/hooks/"
fi
+ if [ -d "$REAL_REPO/languages/$lang/claude/scripts" ]; then
+ mkdir -p "$proj/.claude/scripts"
+ cp -r "$REAL_REPO/languages/$lang/claude/scripts/." "$proj/.claude/scripts/"
+ fi
if [ -f "$REAL_REPO/languages/$lang/claude/settings.json" ]; then
cp "$REAL_REPO/languages/$lang/claude/settings.json" "$proj/.claude/settings.json"
fi
@@ -120,6 +124,64 @@ install_team_overlay() {
[ -x "$PROJ/.claude/hooks/validate-el.sh" ]
}
+# --- Auto-fix: .claude/scripts ---
+
+@test "sync: drifted bundle script is auto-fixed and restored" {
+ install_bundle elisp "$PROJ"
+ echo ";; junk drift" >> "$PROJ/.claude/scripts/coverage-summary.el"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [[ "$output" == *".claude/scripts/coverage-summary.el"* ]]
+ matches_canonical ".claude/scripts/coverage-summary.el" "$REAL_REPO/languages/elisp/claude/scripts/coverage-summary.el"
+}
+
+@test "sync: missing bundle script is re-copied" {
+ install_bundle elisp "$PROJ"
+ rm "$PROJ/.claude/scripts/coverage-summary.el"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [[ "$output" == *".claude/scripts/coverage-summary.el"* ]]
+ [ -f "$PROJ/.claude/scripts/coverage-summary.el" ]
+}
+
+# --- Project-owned: Makefile fragment via inbox ---
+
+@test "sync: coverage Makefile fragment is dropped into .ai/inbox when present" {
+ install_bundle elisp "$PROJ"
+ mkdir -p "$PROJ/.ai/inbox"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"inbox"* ]]
+ [ -f "$PROJ/.ai/inbox/from-rulesets-coverage-makefile.txt" ]
+ matches_canonical ".ai/inbox/from-rulesets-coverage-makefile.txt" "$REAL_REPO/languages/elisp/coverage-makefile.txt"
+}
+
+@test "sync: no .ai/inbox means no fragment drop and no empty .ai/ created" {
+ install_bundle elisp "$PROJ"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [[ "$output" != *"inbox"* ]]
+ [ ! -d "$PROJ/.ai" ]
+}
+
+@test "sync: fragment already adopted at project root is not re-dropped" {
+ install_bundle elisp "$PROJ"
+ mkdir -p "$PROJ/.ai/inbox"
+ cp "$REAL_REPO/languages/elisp/coverage-makefile.txt" "$PROJ/coverage-makefile.txt"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [ ! -f "$PROJ/.ai/inbox/from-rulesets-coverage-makefile.txt" ]
+}
+
+@test "sync: fragment already waiting in inbox is not duplicated" {
+ install_bundle elisp "$PROJ"
+ mkdir -p "$PROJ/.ai/inbox"
+ cp "$REAL_REPO/languages/elisp/coverage-makefile.txt" "$PROJ/.ai/inbox/from-rulesets-coverage-makefile.txt"
+ run bash "$SCRIPT" "$PROJ"
+ [ "$status" -eq 0 ]
+ [[ "$output" != *"inbox"* ]]
+}
+
# --- Surface-only: settings.json ---
@test "sync: drifted settings.json is surfaced, NOT modified, exit 3" {