test: add a suite runner, gate it in CI, fix 8 failing tests - #60
Draft
yulonglin wants to merge 1 commit into
Draft
test: add a suite runner, gate it in CI, fix 8 failing tests#60yulonglin wants to merge 1 commit into
yulonglin wants to merge 1 commit into
Conversation
The suites in tests/, claude/hooks/ and scripts/tests/ were only ever invoked by hand — no CI ran them. Eight were failing on main at once with nobody noticing. Runner: - tests/run-all.sh discovers suites by naming convention, resolves each one's interpreter, and reports a missing interpreter as SKIP rather than dropping it. --strict makes SKIP a failure, which is what CI runs. CI: - .github/workflows/tests.yml runs the suite on push and PR. Checkout uses fetch-depth: 0 because the memory-tier test resolves a tag. - Adds a cargo test gate for tools/claude-tools: 4.4k lines of Rust that ships as a committed binary, previously compiled and released by CI with no test step and no PR gate. - .gitlab-ci.yml checked deploy.sh and install.sh with `bash -n`, but both are zsh; `bash -n deploy.sh` fails on line 768's zsh glob qualifiers. Replaced with tests/test_shell_syntax.sh, which dispatches on each file's shebang and covers 191 tracked scripts instead of 2. Fixes: - test_memory_tier_budget: AGGREGATE_CEILING was the literal sum of the per-file ceilings while safety-and-git.md was exempt from its own — double-counting the exemption, so the aggregate was unsatisfiable by construction. It is now derived from the exemption, and the exempt file's protected floor and prose allowance are each asserted. - test_memory_tier_budget: `git show <tag>:path` raised on clones without tags. Fetches the one tag it needs, skips legibly if it cannot. - test_vault_sync_gate: two tests that pass the gate reached the real resolve_ob() and died on obsidian-headless not being installed. Stubbed explicitly, not via autouse, so the test that proves the stop precedes the irreversible call keeps working. - test_simplify_reuse: asserted on "quality pass", wording the hook no longer emits. Now keys on the dirty-signal sentence. Each fix was mutation-tested: neutering the vault gate reddens 23 tests, rewording the nudge reddens 2, +600 bytes to a memory file reddens the aggregate, and a stray `if` reddens the syntax suite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Swn1T5hcQRUMmQfnepjQde
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to a test-coverage analysis of the repo. The headline finding was not that coverage is thin (though it is) but that nothing ran the tests — so eight suites were failing on
mainat once with nobody noticing.What was wrong
.gitlab-ci.ymlranbash -n install.sh+bash -n deploy.sh. Both files are#!/usr/bin/env zsh, so this used the wrong parser —bash -n deploy.shfails outright on line 768's zsh glob qualifiers ("$ctx_src"/*.json(N)), reporting valid zsh as a syntax error..github/workflows/build-claude-tools.ymlcompiles 4,465 lines of Rust, commits the binary intocustom_bins/, and publishes a release — with no test step, and only onmain, so PRs touching it were ungated.Changes
Runner —
tests/run-all.shDiscovers suites by naming convention (
tests/test_*.{sh,zsh,py},claude/hooks/test_*.sh,scripts/tests/test-*.sh), so a new suite needs no registration. Resolves each suite's interpreter rather than assuming one, and reports a missing interpreter asSKIPinstead of silently dropping it.--strictturnsSKIPinto failure, so a runner without zsh degrades into a red build rather than invisible coverage.CI —
.github/workflows/tests.ymlRuns
tests/run-all.sh --stricton push and PR. Checkout usesfetch-depth: 0because the memory-tier suite resolves a tag. A second job adds the missingcargo testgate fortools/claude-tools; it is green-on-zero-tests today by design — the point is that the gate exists before the tests do..gitlab-ci.yml's syntax check is replaced bytests/test_shell_syntax.sh, which dispatches on each file's shebang and covers 191 tracked scripts instead of 2 — including the 46 shebanged executables incustom_bins/, which had no syntax gate at all.Fixes
test_memory_tier_budgetAGGREGATE_CEILINGwas the literal sum of the per-file ceilings whilesafety-and-git.mdwas exempt from its own — double-counting the exemption, making the aggregate unsatisfiable by construction. Now derived from the exemption; the exempt file's protected floor and prose allowance are each asserted separately.test_memory_tier_budgetgit show <tag>:pathraisedCalledProcessErroron clones without tags — infrastructure failure wearing the costume of a content regression. Now fetches the one tag it needs, and skips legibly if it cannot.test_vault_sync_gateresolve_ob()and died on obsidian-headless not being installed. Stubbed explicitly rather than via an autouse fixture, sotest_filters_never_invokes_ob_when_blocked— which proves the stop precedes the irreversible call — keeps working.test_simplify_reusequality pass, wordingsimplify_nudge.shno longer emits. Now keys on the dirty-signal sentence, which is also the only phrase distinguishing the two message parts.Verification
tests/run-all.sh --strict— 19 suites, 0 failed, 0 skipped (with zsh installed).Each fix was mutation-tested to confirm it still asserts something:
exclusion_blocker()ifin a hookNot in scope
The analysis flagged these as the next priorities, none addressed here:
tools/claude-toolshas zero tests; 38 of the 49 hooks wired insettings.jsonare untested (12 of them returndeny);scripts/shared/helpers.shhas ~55 untested functions includingsafe_symlink,merge_json_settingsandsync_authorized_keys_union.claude/hooks/README.mdnow names the specific hooks worth writing next.Generated by Claude Code