e2e: hermetic ADK cassettes, matchFileSnapshot migration, seinfeld gzip fix#1966
Open
Stephen Belanger (Qard) wants to merge 7 commits intomainfrom
Open
e2e: hermetic ADK cassettes, matchFileSnapshot migration, seinfeld gzip fix#1966Stephen Belanger (Qard) wants to merge 7 commits intomainfrom
Stephen Belanger (Qard) wants to merge 7 commits intomainfrom
Conversation
- Remove dotenv loading from vitest.setup.ts — mise handles .env loading - Drop the dotenv dev-dependency from e2e/package.json - Simplify test:e2e:record to an inline env-var prefix; delete the record-cassettes.mjs wrapper script that did nothing beyond that - Delete dev-packages/seinfeld/LICENSE (repo-level license applies) - Delete dev-packages/seinfeld/scripts/migrate-from-legacy.mjs — the one-time migration it performed is complete and the file is unreferenced - Clarify the format versioning rationale in seinfeld/src/format/v1.ts - Sort JSON keys when writing cassette files (file-store.ts) so re-recordings produce deterministic diffs and snapshot comparisons are not confused by non-deterministic key insertion order Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…faults - Replace global cassette-filters.mjs registry with per-scenario cassette-filter.mjs files; cassette-preload.mjs now dynamically imports them from the scenario dir - Default redact to 'paranoid' in seinfeld recorder (was opt-in) - Gate provider key placeholder injection on replay mode only (not record/passthrough) - Delete obsolete cassette-filters.mjs and record-cassettes.mjs helper scripts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Node.js undici decompresses gzip/deflate at the HTTP layer before passing the body to MSW handlers. The stored body bytes are therefore already plain JSON/text. buildResponse was preserving the original content-encoding header, which caused callers (e.g. Google ADK) to attempt a second gunzip of already-decoded bytes, producing a zlib "incorrect header check" error and making the response unreadable. Fix: strip content-encoding, transfer-encoding, and content-length from the Response built by buildResponse (both replay and record return paths). Also switch handleRecord to return buildResponse() instead of realResponse.clone() for non-binary-draft bodies. After recordResponseDraft() tees the body stream, clone() can return an empty body on some Node versions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DRAIN_DELAY_MS was temporarily raised to 15000ms during ADK cassette debugging. The root cause (gzip content-encoding bug in seinfeld) is now fixed, so restore the original 2-second drain delay. Also remove the temporary onRecord stderr callback that was added for diagnostics. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Record cassettes for both ADK versions (0.6.1 and 1.0.0) and update snapshots to match. The cassette filter ignores query params (Google API key) and all body fields (volatile functionCall IDs), relying on callIndex alone for stable matching. Both variants now produce two cassette entries: call 0 returns a functionCall for get_weather, call 1 returns the final answer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The new matchFileSnapshot wrapper in helpers/file-snapshot.ts is a no-op in canary mode (where snapshot comparison is skipped because live API responses are non-deterministic). All scenario test files and assertions modules are migrated to use the new helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
fix(seinfeld):
buildResponsewas preservingcontent-encoding: gzipwhile serving already-decoded body bytes (undici decompresses at the HTTP layer). Callers like Google ADK would attempt a second gunzip and getincorrect header check. Fixed by strippingcontent-encoding,transfer-encoding, andcontent-lengthfrom replayed/recorded responses. Also switcheshandleRecordto returnbuildResponse()instead ofrealResponse.clone()for non-binary-draft bodies, avoiding empty-body issues from double-tee'd streams.feat(e2e/google-adk): Records hermetic cassettes for both ADK variants (0.6.1 and 1.0.0). Each cassette has two Gemini entries — call 0 returns a
functionCallforget_weather, call 1 returns the final answer. A per-scenariocassette-filter.mjsignores the?key=query param and all body fields (volatilefunctionCallIdUUIDs), so matching relies solely oncallIndex.refactor(e2e): Adds a
matchFileSnapshotwrapper inhelpers/file-snapshot.tsthat is a no-op in canary mode. All scenario test files and assertions modules are migrated fromtoMatchFileSnapshotto the new helper, so canary runs skip snapshot comparison for non-deterministic live API responses.chore(e2e): Restores
DRAIN_DELAY_MSto 2000ms and removes the temporaryonRecorddebug callback fromcassette-preload.mjs. Also adds theinstallRecordModeGuardfunction that prevents premature cassette flush during multi-step ADK tool-call flows.Test plan
pnpm run test:e2e:hermetic -t "google adk"passes (32/32) without any Google API keymatchFileSnapshotmigration verified: zero remainingtoMatchFileSnapshotcalls ine2e/scenarios/🤖 Generated with Claude Code