test(config): T-1281 — canvas-version parity, using its own history as fixture
Five cases, including the failure path that matters — this is the gate with five shipped regressions behind it. A fixture repo does not work here, and finding out why exposed a real inconsistency in the port: canvas_sources globs from a __file__-derived root, so under SR_REPO_ROOT the service would diff the fixture while globbing the real tree. Git goes through config.repo_root(); the registry does not. Harmless in production since they are the same repo, but it is the same no-root-override asymmetry the domain map noted about the old scripts, now inside the new code. Not fixed here — making it dynamic means restructuring six module-level constants in a module the old script still imports. Real history is the better fixture anyway: both implementations see identical input, nothing is mutated, and nothing can drift from the thing it models. Two of the cases are genuine historical instances of the regression this gate exists to catch — T-1237 and T-1194 both changed canvas generation and were bumped only after the fact. The history that produced the check, used as its own test. The test also asserts no changed file is dropped from the failure message. That list is the actionable half; "something changed" without saying what leaves the reader to re-derive the intersection by hand. Proven to fail by truncating the touched-file set, which reported both the exit-code divergence and all three omitted filenames by name. Each case also asserts the OLD script still behaves as the case claims, so a rewritten history would say so rather than silently checking nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -195,6 +195,81 @@ def check_fact_ids(failures: list[str]) -> None:
|
||||
)
|
||||
|
||||
|
||||
# Ranges from the repo's own history, chosen so both implementations see
|
||||
# identical input with nothing mutated. A fixture repo would not work here: the
|
||||
# canvas registry globs from a __file__-derived root, so under SR_REPO_ROOT the
|
||||
# new service would diff the fixture while globbing the real tree. Real history
|
||||
# sidesteps that asymmetry entirely — and the failure cases below are genuine
|
||||
# instances of the regression this gate exists to catch, not constructed ones.
|
||||
#
|
||||
# SHAs are hardcoded because history is immutable. If a rebase ever invalidates
|
||||
# one, the test says so by name rather than silently checking nothing.
|
||||
CANVAS_CASES = [
|
||||
# (label, base, head, expect non-zero)
|
||||
("canvas-needs-bump", "4e503c356~1", "4e503c356", True), # T-1237: bumped later
|
||||
("canvas-needs-bump-2", "566b56651~1", "566b56651", True), # T-1194: same
|
||||
("canvas-bumped", "9b146f9e1~1", "9b146f9e1", False), # changed AND bumped
|
||||
("canvas-clean", "6b31111cd~1", "6b31111cd", False), # governance only
|
||||
("canvas-no-base", "no-such-ref-anywhere", "HEAD", False), # skip, not fail
|
||||
]
|
||||
|
||||
|
||||
def check_canvas_version(failures: list[str]) -> None:
|
||||
"""Parity for the gate with the worst track record — five shipped regressions.
|
||||
|
||||
Its failure path is the one that matters, so two of the five cases are real
|
||||
commits that changed canvas generation without bumping the version. Both
|
||||
were bumped after the fact, which is precisely the history that produced
|
||||
this check.
|
||||
"""
|
||||
for label, base, head, expect_failure in CANVAS_CASES:
|
||||
old = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "tooling" / "check-canvas-version"),
|
||||
"--base",
|
||||
base,
|
||||
"--head",
|
||||
head,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=REPO_ROOT,
|
||||
)
|
||||
new = subprocess.run(
|
||||
["reach", "check", "canvas-version", "--base", base, "--head", head],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=REPO_ROOT,
|
||||
env={**os.environ, "SR_OUTPUT_FORMAT": "text"},
|
||||
)
|
||||
|
||||
if (old.returncode != 0) != expect_failure:
|
||||
failures.append(
|
||||
f"[{label}] the OLD script exited {old.returncode}; this range no "
|
||||
"longer sets up the case it claims to — history may have been rewritten"
|
||||
)
|
||||
if old.returncode != new.returncode:
|
||||
failures.append(
|
||||
f"[{label}] exit differs: old={old.returncode} new={new.returncode}"
|
||||
)
|
||||
|
||||
if expect_failure:
|
||||
# The changed files are the actionable half of this failure; a
|
||||
# message that says "something changed" without saying what leaves
|
||||
# the reader to re-derive the intersection by hand.
|
||||
for stream in (old.stdout + old.stderr,):
|
||||
for line in stream.splitlines():
|
||||
token = line.strip()
|
||||
if token.endswith((".rs", ".gd")) and token not in (
|
||||
new.stdout + new.stderr
|
||||
):
|
||||
failures.append(
|
||||
f"[{label}] new output omits a changed file the old one "
|
||||
f"named: {token}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if shutil.which("reach") is None:
|
||||
print(
|
||||
@@ -269,6 +344,7 @@ def main() -> int:
|
||||
)
|
||||
|
||||
check_fact_ids(failures)
|
||||
check_canvas_version(failures)
|
||||
|
||||
if failures:
|
||||
print("test_check_parity: FAIL", file=sys.stderr)
|
||||
@@ -278,7 +354,8 @@ def main() -> int:
|
||||
|
||||
print(
|
||||
f"test_check_parity: OK — client-version {len(CASES)} paths, "
|
||||
f"fact-ids {len(FACT_CASES)} paths, all agreeing on exit code and content"
|
||||
f"fact-ids {len(FACT_CASES)} paths, canvas-version {len(CANVAS_CASES)} paths, "
|
||||
"all agreeing on exit code and content"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user