Files
codebase-memory-mcp/tests/windows/test_hook_augment.py
T
FlipperandClaude Opus 4.8 0eb2c58913 test(windows): convert fixed reds to green guards, wire CI, rewrite drive test
Rebased onto current main and reworked in response to review. Three of the four
Windows reds were fixed upstream since the branch was cut at b075f05, so they are
now green regression guards; the fourth stays a genuine known-red.

- test_non_ascii_path.py (#636/#357): green guard - fixed by #700 (per-pass
  readers now route through cbm_fopen -> _wfopen). Re-verified green on main.
- test_hook_augment.py (#618): green guard - fixed by #619 (cbm_is_walkable_abs_path
  accepts drive-letter X:/ cwd). Re-verified green on main.
- test_ui_drive_listing.py (#548): rewritten. The fix exposes drives via a new
  roots[] field, not the dirs[] array the old test asserted (which would stay red
  against fixed code). Now asserts every fixed drive is in roots and browsable.
  Re-verified green on main (drives C:/D:/E:).
- test_cli_non_ascii_arg.py (#423/#20): unchanged - main() is still narrow-argv
  with no wide command line, so this remains genuinely red (the keeper).

- scripts/test-windows.ps1: split green guards (gate CI) from opt-in known-reds;
  add -GuardsOnly; run indexing in-process (CBM_INDEX_SUPERVISOR=0) so a guard
  reflects the path/hook/drive fix under test, not the index-worker spawn path.
- .github/workflows/_test.yml: new test-windows-guards job builds the product+UI
  binary (scripts/build.sh --with-ui) and runs the guards with -GuardsOnly so
  #700/#619/#548 stay enforced on Windows CI.
- RED_TEST_ANALYSIS.md: refreshed to record the landed fixes and current status.

Signed-off-by: Flipper <jacobphilipp@ymail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 13:22:41 +02:00

110 lines
4.6 KiB
Python

r"""GREEN regression guard — the PreToolUse hook augmenter fires on Windows.
Guards the fix for issue #618 (landed on main via #619) at the product surface.
`codebase-memory-mcp hook-augment` is the non-blocking Claude Code PreToolUse
Grep/Glob augmenter: given a hook payload it should emit a `hookSpecificOutput`
with `additionalContext` listing graph symbols that match the searched token.
Before #619 it emitted nothing for every payload on Windows: `src/cli/hook_augment.c`
gated on POSIX-style absolute paths (`cwd[0] == '/'` and a walk-up loop over
`dir[0] == '/'`). A Windows `cwd` is a drive-letter path (`C:\...` / `C:/...`),
so `cwd[0]` was never `'/'` and the augmenter bailed before querying the graph.
#619 added `cbm_is_walkable_abs_path` (accepts `X:/` drive-letter roots), so the
augmenter now fires for a drive-letter cwd.
This test indexes a repo with a known symbol, confirms `search_graph` finds it
(control — proves the index and project name are fine), then invokes
`hook-augment` exactly as the installed PreToolUse hook does and asserts a
`hookSpecificOutput` payload is produced. It fails (red) if that fix regresses.
Also passes on Linux/macOS (`cwd` starts with `/`).
Exit code: 0 == augmenter fired (green), 1 == no-op (regression), 2 == setup error.
Usage:
python test_hook_augment.py <path-to-codebase-memory-mcp[.exe]>
"""
import json
import os
import shutil
import subprocess
import sys
import tempfile
SYMBOL = "someIndexedSymbol"
SRC = "export function %s(a: number): number { return a + 1; }\n" % SYMBOL
def run_cli(binary, cache, args, stdin=None, timeout=120):
env = dict(os.environ)
env["CBM_CACHE_DIR"] = cache
return subprocess.run([binary] + args, capture_output=True, timeout=timeout,
env=env, input=stdin)
def main():
if len(sys.argv) < 2:
print("usage: python test_hook_augment.py <binary>")
return 2
binary = os.path.abspath(sys.argv[1])
if not os.path.exists(binary):
print("FAIL: binary not found: %s" % binary)
return 2
work = tempfile.mkdtemp(prefix="cbm_win_hook_")
try:
repo = os.path.join(work, "repo")
os.makedirs(os.path.join(repo, "src"), exist_ok=True)
with open(os.path.join(repo, "src", "m.ts"), "wb") as f:
f.write(SRC.encode("utf-8"))
cache = os.path.join(work, "cache")
os.makedirs(cache, exist_ok=True)
# repo_path / cwd in the forward-slash drive form Claude Code passes.
repo_fwd = repo.replace("\\", "/")
idx = run_cli(binary, cache, ["cli", "index_repository",
json.dumps({"repo_path": repo_fwd})])
idx_out = (idx.stdout or b"").decode("utf-8", "replace")
if '"nodes"' not in idx_out:
print("SETUP FAIL: index did not run:\n%s" % idx_out[:300])
return 2
# Control: prove the symbol is indexed and queryable.
lp = run_cli(binary, cache, ["cli", "list_projects", "{}"])
projects = json.loads((lp.stdout or b"").decode("utf-8", "replace"))["projects"]
name = projects[0]["name"]
sg = run_cli(binary, cache, ["cli", "search_graph",
json.dumps({"label": "Function",
"name_pattern": ".*%s.*" % SYMBOL,
"project": name})])
if SYMBOL not in (sg.stdout or b"").decode("utf-8", "replace"):
print("SETUP FAIL: control search_graph did not find %s" % SYMBOL)
return 2
print("control: search_graph finds %s in project %s" % (SYMBOL, name))
# Invoke hook-augment exactly as the installed PreToolUse hook does.
payload = json.dumps({
"hook_event_name": "PreToolUse",
"tool_name": "Grep",
"cwd": repo_fwd,
"tool_input": {"pattern": SYMBOL},
}).encode("utf-8")
ha = run_cli(binary, cache, ["hook-augment"], stdin=payload, timeout=60)
out = (ha.stdout or b"").decode("utf-8", "replace").strip()
print("hook-augment rc=%d stdout=%r" % (ha.returncode, out[:200]))
fired = ("hookSpecificOutput" in out) and ("additionalContext" in out)
if fired:
print("\nGREEN: PreToolUse augmenter emitted additionalContext.")
return 0
print("\nREGRESSION (red): hook-augment produced no hookSpecificOutput on "
"Windows (drive-letter cwd rejected — has the #619 "
"cbm_is_walkable_abs_path handling in hook_augment.c regressed?).")
return 1
finally:
shutil.rmtree(work, ignore_errors=True)
if __name__ == "__main__":
sys.exit(main())