From d23dbf9c85051108cb0da6a4762253305e8d4d4a Mon Sep 17 00:00:00 2001 From: jhuang-tw <77732008+jhuang-tw@users.noreply.github.com> Date: Thu, 13 Aug 2026 01:24:13 -0700 Subject: [PATCH] test(router): cover Bash default project root --- skills/scripts/test-routing.sh | 62 +++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/skills/scripts/test-routing.sh b/skills/scripts/test-routing.sh index 309e2aa..d585e72 100644 --- a/skills/scripts/test-routing.sh +++ b/skills/scripts/test-routing.sh @@ -22,6 +22,7 @@ fi import json import pathlib import re +import shutil import subprocess import sys import tempfile @@ -66,11 +67,70 @@ with tempfile.TemporaryDirectory(prefix="rs-routing-bash-") as scratch: f"hint={hint!r} expect={expected} got={got} exit={result.returncode}" ) + # Regression: when neither --project-root nor --out-dir is supplied, the + # Bash router must behave like the PowerShell router and write under the + # caller's current project, not under the installed reverse-skill package. + source_skills = router_path.parent.parent + source_config = source_skills / "config" / "routing.json" + config = json.loads(source_config.read_text(encoding="utf-8-sig")) + default_case = cases[0] + default_skill_rel = pathlib.PurePosixPath( + config["routes"][default_case["expect"]]["skill"] + ) + + fixture_package = root / "package" + fixture_router = fixture_package / "skills" / "scripts" / "master-route.sh" + fixture_config = fixture_package / "skills" / "config" / "routing.json" + fixture_skill = fixture_package / "skills" / pathlib.Path(*default_skill_rel.parts) + fixture_router.parent.mkdir(parents=True, exist_ok=True) + fixture_config.parent.mkdir(parents=True, exist_ok=True) + fixture_skill.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(router_path, fixture_router) + shutil.copy2(source_config, fixture_config) + shutil.copy2(source_skills / pathlib.Path(*default_skill_rel.parts), fixture_skill) + + caller_root = root / "caller-project" + caller_root.mkdir() + default_result = subprocess.run( + ["bash", str(fixture_router), "--hint", default_case["hint"]], + cwd=caller_root, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + default_scopes = list((caller_root / "work").glob("master-route-*/route-scope.md")) + if default_result.returncode != 0: + failures.append( + "default-root router failed: " + f"exit={default_result.returncode} stderr={default_result.stderr.strip()!r}" + ) + elif len(default_scopes) != 1: + failures.append( + "default-root router did not write exactly one route-scope under caller project: " + f"found={len(default_scopes)}" + ) + else: + default_scope = default_scopes[0].read_text(encoding="utf-8") + project_match = re.search( + r"(?m)^- project_root:[ \t]*(.+?)[ \t]*$", + default_scope, + ) + if not project_match: + failures.append("default-root route-scope missing project_root") + elif pathlib.Path(project_match.group(1)).resolve() != caller_root.resolve(): + failures.append( + "default-root project_root mismatch: " + f"got={project_match.group(1)!r} expect={str(caller_root.resolve())!r}" + ) + else: + print("[PASS] default root -> caller project") + print(f"TOTAL={len(cases)} PASS={passed} FAIL={len(failures)}") if failures: for failure in failures: print(f"[FAIL] {failure}", file=sys.stderr) raise SystemExit(1) -print(f"OVERALL: ALL PASS ({passed})") +print(f"OVERALL: ALL PASS ({passed} routing cases + default-root regression)") PY