From 457ee019870d16ff05d4dfd4ddd485bc4bc55235 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Wed, 1 Jul 2026 15:34:35 +0000 Subject: [PATCH] test: make cancelStream harnesses tolerate the new reason param + stdout provenance log Codex gate on #5346 flagged two brittle static extractors that broke on the cancelStream(reason) signature change + the new '[stream] cancel requested' stdout log: - test_cancel_stream_owner_guard.py: the Node harness parsed ALL of stdout as JSON; the provenance console.info line (fires during runAll) polluted it. Parse the LAST non-empty stdout line (the result JSON is always emitted last, after runAll resolves). - test_sprint36.py: two extractors did src.find('async function cancelStream()') (exact, no params). Switched to a signature-tolerant regex and widened the catch-block window (the provenance log/comments now precede the try/catch). Both pre-existing tests, updated to match the intentional #5345 change (not the code bent to fit the test). --- tests/test_cancel_stream_owner_guard.py | 11 ++++++++++- tests/test_sprint36.py | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/test_cancel_stream_owner_guard.py b/tests/test_cancel_stream_owner_guard.py index 6b85b66f4..e149dd420 100644 --- a/tests/test_cancel_stream_owner_guard.py +++ b/tests/test_cancel_stream_owner_guard.py @@ -329,7 +329,16 @@ def _run_cancel_stream_scenarios() -> dict: f"--- stderr ---\n{completed.stderr}" ) try: - return json.loads(completed.stdout) + # The function under test legitimately emits diagnostic lines on stdout + # (e.g. cancelStream's '[stream] cancel requested' provenance log, added + # for #5345). Those fire DURING runAll(); the result JSON is written by + # `console.log(JSON.stringify(r))` only after runAll() resolves, so it is + # always the LAST non-empty stdout line. Parse that rather than assuming + # stdout is pure JSON. + lines = [ln for ln in completed.stdout.splitlines() if ln.strip()] + if not lines: + raise json.JSONDecodeError("empty stdout", completed.stdout or "", 0) + return json.loads(lines[-1]) except json.JSONDecodeError as e: raise AssertionError( f"node subprocess returned non-JSON output:\n" diff --git a/tests/test_sprint36.py b/tests/test_sprint36.py index 97b5d6fe3..89bc6f9f0 100644 --- a/tests/test_sprint36.py +++ b/tests/test_sprint36.py @@ -46,8 +46,11 @@ class TestCancelStreamCleanup: def _get_cancel_block(self): """Extract the cancelStream function body from boot.js.""" src = read("static/boot.js") - idx = src.find("async function cancelStream()") - assert idx != -1, "cancelStream not found in boot.js" + # Signature-tolerant: cancelStream now takes a `reason` param (#5345), so + # match the declaration regardless of its parameter list. + m = re.search(r"async function cancelStream\s*\(", src) + assert m is not None, "cancelStream not found in boot.js" + idx = m.start() # Find the closing brace — scan for the matching } depth = 0 end = idx @@ -122,8 +125,13 @@ class TestCancelStreamErrorPath: The status is cleared by setStatus('') unconditionally. """ src = read("static/boot.js") - idx = src.find("async function cancelStream()") - block = src[idx:idx + 400] + # Signature-tolerant match (cancelStream now takes a `reason` param, #5345). + m = re.search(r"async function cancelStream\s*\(", src) + assert m is not None, "cancelStream not found in boot.js" + idx = m.start() + # Widen the window: the provenance log + comments added for #5345 sit + # before the try/catch, so 400 chars no longer reaches the catch block. + block = src[idx:idx + 1200] # The old pattern was setStatus inside catch; new pattern has it outside # Look for the catch block specifically catch_idx = block.find("}catch(")