Skip to content

Add WebSocket API conformance tests#27

Merged
jpr5 merged 2 commits intomainfrom
test/ws-conformance
Mar 11, 2026
Merged

Add WebSocket API conformance tests#27
jpr5 merged 2 commits intomainfrom
test/ws-conformance

Conversation

@jpr5
Copy link
Copy Markdown
Contributor

@jpr5 jpr5 commented Mar 11, 2026

Summary

  • Adds ws-api-conformance.test.ts with 53 tests validating wire format conformance for all 3 WebSocket protocols
  • WS Responses API: event structure, ID prefixes (resp-, call_), types, delta/done shapes
  • WS Realtime API: session lifecycle, evt-/sess- ID prefixes, response event sequences, tool call structure
  • WS Gemini Live: setupComplete shape, serverContent frames, toolCall.functionCalls with parsed object args, error codes/statuses
  • Cross-protocol invariants: upgrade acceptance on all 3 WS paths, rejection on non-WS paths, malformed JSON handling

Test plan

  • All 53 new tests pass
  • Red-green verified on 3 structural assertions (resp- prefix, evt- prefix, Gemini args-as-object)
  • Format and lint clean
  • Full suite still passes (468 + 53 = 521 tests)

🤖 Generated with Claude Code

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 11, 2026

Open in StackBlitz

npm i https://pkg.pr.new/CopilotKit/llmock/@copilotkit/llmock@27

commit: 3519a5b

Validates wire format conformance for all 3 WebSocket protocols:
- WS Responses API (event structure, IDs, types, deltas)
- WS Realtime API (session lifecycle, event_ids, response sequences)
- WS Gemini Live (setupComplete, serverContent, toolCall shapes)
- Cross-protocol invariants (upgrade acceptance, malformed JSON)
@jpr5 jpr5 force-pushed the test/ws-conformance branch from 1b59319 to ecdd4fd Compare March 11, 2026 21:32
@jpr5 jpr5 merged commit b876d4c into main Mar 11, 2026
8 checks passed
@jpr5 jpr5 deleted the test/ws-conformance branch March 11, 2026 21:43
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only addition, no production code changes. The two nits about misleading test names are worth fixing but don't block merge.

Extended reasoning...

Overview

This PR adds a single new file src/__tests__/ws-api-conformance.test.ts containing 53 tests that validate wire format conformance for three WebSocket protocols: WS Responses API, WS Realtime API, and WS Gemini Live BidiGenerateContent. No production code is modified — this is purely additive test coverage.

Security risks

None. The PR only adds test code that runs against a local test server instance. No authentication, permissions, or external service interactions are involved.

Level of scrutiny

Low scrutiny warranted. Test-only PRs that add new files cannot break production behavior. The tests follow established patterns (using existing connectWebSocket helper, createServer, and Fixture types), and the assertions are straightforward conformance checks against known API shapes.

Other factors

The two bugs found by the bug hunting system are both cosmetic nits: (1) a test named after /v1/responses that actually tests /nonexistent-path, and (2) a test name saying call- (hyphen) when the actual prefix is call_ (underscore). In both cases the test logic and assertions are correct — only the human-readable descriptions are misleading. These are worth fixing for documentation clarity but do not affect test correctness or justify blocking the PR.

Comment on lines +788 to +794
const ws = await connectWebSocket(instance.url, GEMINI_WS_PATH);
ws.send(geminiSetup());
await ws.waitForMessages(1);
ws.send(geminiClientContent("error-test"));
const raw = await ws.waitForMessages(2);
ws.close();
const msg = JSON.parse(raw[1]) as any;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Test name "non-WS HTTP path /v1/responses (HTTP) rejects WebSocket upgrade" is misleading — the test body actually hits /nonexistent-path, not /v1/responses. Since /v1/responses is a valid WS endpoint that accepts upgrades, the test would fail if it matched its name. Rename to something like "non-registered path rejects WebSocket upgrade".

Extended reasoning...

What the bug is

The test at line 788 is named "non-WS HTTP path /v1/responses (HTTP) rejects WebSocket upgrade" but the test body calls connectWebSocket(instance.url, "/nonexistent-path"). The test name and test body describe two completely different things.

Why this is wrong

/v1/responses is one of the three valid WebSocket endpoints — the test at line 777 ("all 3 WS paths accept WebSocket upgrade") proves that /v1/responses successfully accepts WebSocket upgrades. If this test actually used /v1/responses as its name implies, connectWebSocket would resolve successfully instead of rejecting, and the rejects.toThrow("Upgrade failed") assertion would fail.

Step-by-step proof

  1. Line 779: connectWebSocket(instance.url, "/v1/responses") succeeds (upgrade accepted).
  2. Line 788: Test is named "non-WS HTTP path /v1/responses (HTTP) rejects WebSocket upgrade".
  3. Line 792: Test body calls connectWebSocket(instance.url, "/nonexistent-path") — not /v1/responses.
  4. The comment on line 789-790 even acknowledges the mismatch: "The WS endpoint is /v1/responses but only accepts the upgrade — this verifies the server would reject other non-registered paths."
  5. So the test correctly verifies that unregistered paths reject WS upgrades, but the test name claims it is verifying /v1/responses behavior.

Impact

Someone reading test output or doing test-driven development would incorrectly believe that /v1/responses HTTP-only behavior is being verified. The test itself is functionally correct — it properly tests that non-registered paths reject upgrades — but the misleading name reduces the value of the test suite as documentation.

Suggested fix

Rename the test to accurately describe what it tests, e.g.: "non-registered path /nonexistent-path rejects WebSocket upgrade" or "unknown path rejects WebSocket upgrade". The comment can stay as-is since it explains the intent.

});

it("response.content_part.added has part with type text", async () => {
const frames = await getTextResponseFrames();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: Test name says "call- prefix" (hyphen) but the actual ID prefix is call_ (underscore). The assertion correctly checks /^call_/, so the test logic is fine — just the description is misleading. Should read "call_ prefix" to match generateToolCallId() behavior.

Extended reasoning...

What the bug is

The test at line 485 is named:

"response.output_item.added has type function_call, call_id (call- prefix), name, empty arguments"

It says "call- prefix" with a hyphen, but the actual prefix used by the system is call_ with an underscore.

Why underscore, not hyphen

In src/helpers.ts, there are two ID-generation functions:

  • generateId(prefix) (line 12): returns `${prefix}-${randomBytes(12).toString("base64url")}` — uses a hyphen
  • generateToolCallId() (line 16): returns `call_${randomBytes(12).toString("base64url")}` — uses an underscore

All other ID prefixes in the test names (resp-, evt-, sess-) correctly use hyphens because they go through generateId(). The call_ prefix is the exception because it uses the separate generateToolCallId() function, which hardcodes an underscore to match the OpenAI API format.

Step-by-step proof

  1. Line 485: test name says "call- prefix" (hyphen)
  2. Line 496: assertion checks expect(item.call_id).toMatch(/^call_/) (underscore) — this is correct
  3. helpers.ts:17: generateToolCallId() returns call_xxx (underscore)
  4. Therefore the test name is inconsistent with both the assertion and the actual behavior

Impact

This is purely cosmetic — the test logic and assertion are correct. However, the misleading test name could confuse developers reading the test output, especially since all other prefix descriptions in the file accurately reflect their separator character (hyphen for resp-, evt-, sess-).

Fix

Change the test name on line 485 from (call- prefix) to (call_ prefix).

jpr5 added a commit that referenced this pull request Apr 3, 2026
## Summary

- Adds `ws-api-conformance.test.ts` with 53 tests validating wire format
conformance for all 3 WebSocket protocols
- WS Responses API: event structure, ID prefixes (`resp-`, `call_`),
types, delta/done shapes
- WS Realtime API: session lifecycle, `evt-`/`sess-` ID prefixes,
response event sequences, tool call structure
- WS Gemini Live: `setupComplete` shape, `serverContent` frames,
`toolCall.functionCalls` with parsed object args, error codes/statuses
- Cross-protocol invariants: upgrade acceptance on all 3 WS paths,
rejection on non-WS paths, malformed JSON handling

## Test plan

- [x] All 53 new tests pass
- [x] Red-green verified on 3 structural assertions (resp- prefix, evt-
prefix, Gemini args-as-object)
- [x] Format and lint clean
- [x] Full suite still passes (468 + 53 = 521 tests)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant