bfcc7b2acb71ccd7a996800c331df20d6d09b839
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9ee6d54cc3 |
fix(#4306): extend fault-injection fd-swallow fix across the whole suite (#4308)
* fix(#4306): forward real bytes through io.test.cjs's fault-injection mocks The bug #1008 fault-injection tests mock fs.writeSync scoped only by file descriptor. On their "success" arms (the retry-after-EAGAIN/EINTR call, and the short-write simulation) they fabricated a return byte count without ever calling the real writeSync -- the bytes went into a local array and nowhere else. node:test's process-isolation runner (default on Node >= 22) reads each test file's own stdout to parse its child-to-parent result protocol. If the runner's own reporter write for an adjacent test lands on fd 1 while one of these mocks is installed, that write was silently swallowed instead of reaching the real pipe -- observed in CI as "Unable to deserialize cloned data" (a corrupted/truncated byte stream on the parent's read side), not a thrown exception. Every "success" arm now forwards the real bytes to orig()/restore() instead of fabricating a return value, so anything else sharing the fd during the mocked window still gets its bytes delivered for real. writeAllSync (the only production caller reaching this mock) always passes a Buffer, so the forwarded calls use the buffer-form fs.writeSync overload unambiguously. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#4306): extend fault-injection fd-swallow fix across the whole suite The originally-fixed instance (tests/io.test.cjs) was one occurrence of a copy-pasted defect: mocked fs.writeSync arms fabricated a return byte count without ever forwarding the call to the real fs.writeSync, silently discarding bytes. Under node:test's process-isolated runner, the parent reads the child's real stdout to parse v8-serialized report frames interleaved with plain output (confirmed against node's own lib/internal/test_runner/runner.js and a matching upstream issue, nodejs/node#64061) — a swallowed write on that fd corrupts the parent's parse ("Unable to deserialize cloned data"). Adds a shared, safe capture helper to tests/helpers.cjs, captureFdSync(fd, fn): it always forwards every write to the real fs.writeSync first, then records only the observed fd's bytes, sliced by the real return count (not the requested length), decoded once via Buffer.concat so a short write can't split a multi-byte codepoint across two decodes. 17 test files migrate their local copy of the unsafe mock to this shared helper. tests/worktree-base-ref.test.cjs keeps a narrower in-place fix instead (it needs to record every fd a write touched, which the shared helper doesn't expose). tests/io.test.cjs gets two follow-up correctness fixes on top of the already-committed forwarding fix: the EAGAIN/EINTR/short-write arms now derive their recorded chunk from the real return count everywhere (including the string-form overload), and the short-write test no longer forces a Buffer-shaped truncation call onto a string-form write that could land on the same fd. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
57437071e7 |
test(#3244): codex smoke test across the emit, validate and repair seam (#3298)
* test(#3244): end-to-end codex smoke test across the emit and validate seam Test-only. The only place in this epic where the emitter (Phase 1) and the validator (Phase 2) meet the same bytes. Every phase so far tested its own half against fixtures it authored, and per #2371 a fixture written by the gate's own author can only confirm what that author already believed. Rows 7 and 10 feed a REAL emitted tree to Phase 2's checkCodexModelPosture: if the emitter and the validator disagree about what "clean" means, nothing else in the suite can see it. Scope corrected from the epic, which scoped this to model_profile: inherit. That profile is the one this epic does NOT change — readGsdRuntimeProfileResolver returns null for it, so a Codex install under inherit omitted the model before Phase 1 too, and a test scoped only to it would pass identically before and after the change it exists to prove. Row 1 uses `balanced`, the default and the path that actually lost its pin; inherit is row 4, the unchanged control. Honest classification, because this is a smoke test and claiming otherwise would be false: NO row here is red-first. Phases 1 and 2 are merged and working, so every row passes today. Rows 1-6 and 9 are regression guards; rows 7 and 10 are cross-phase integration. Their value is that nothing else can catch the drift they cover, not that they are red now. The RED checkpoint is therefore skipped deliberately for this phase rather than spent proving a tautology. Rows 8 and 11 — the same cross-checks against Phase 3's repairer — are deliberately ABSENT, not forgotten. Phase 3 (#3243) is still in CI and its module does not exist on next yet. They land once it merges; writing them against a surface that does not exist would have meant inventing the assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(#3244): close the emit-validate-repair loop with rows 8 and 11 These were deliberately absent from the previous commit because Phase 3 had not merged; writing them against a module that did not exist would have meant inventing the assertion. It has merged, so they land now. Row 8: a freshly-installed tree reports ZERO changes from the sync's dry run. If a brand-new install needs repair, the emitter and the repairer disagree about what a correct file looks like. Row 11 is the sharpest row in the phase: after installing with an explicit real-Codex model_overrides pin, the sync reports that agent skipped rather than synced, and the file is byte-identical afterwards. An over-eager stripper that removes any `model` line passes every Phase 3 unit test and fails only here. Byte-identity is asserted by comparing file CONTENTS, not mtime — a rewrite with identical bytes still moves mtime, so an mtime check would pass exactly the implementation this row exists to catch. With these, all four cross-phase rows are in place, and this suite is the only place in the epic where the emitter, the validator and the repairer meet the same bytes. Every phase before this tested its own half against fixtures it authored, and per #2371 those can only confirm what their author already believed. Both rows are cross-phase integration and pass today, like the rest of this suite. No row here is red-first and none is described as such. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(#3244): extract withAgentsDir instead of try/finally in test bodies Review finding. Rows 7 and 10 put try/finally directly inside the test callback to save and restore GSD_AGENTS_DIR. CONTRIBUTING prohibits that in test bodies — it masks failures — and permits it only inside standalone helpers. The phase's own test matrix restated the rule and it was violated anyway. lint:ci does not catch this; it is a prose standard, which is precisely why it survived to review. It was also inconsistent with this file's own conventions: runCodexInstall, runEffortSyncDryRun and captureStderr already factor save/restore into helpers. withAgentsDir now follows the same shape and both sites use it. No assertion changed — both rows still drive the real checkCodexModelPosture against the real installed tree, which is the whole point of them. Swept the rest of the file: the only other try blocks are inside the two pre-existing helpers and were already compliant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |