import { describe, it, expect } from 'vitest'; import { WsFrameSchema, KNOWN_FRAME_TYPES } from '../ws-frames.js'; const VALID_UUID_A = '00000000-0000-0000-0000-000000000001'; const VALID_UUID_B = '00000000-0000-0000-0000-000000000002'; const VALID_UUID_C = '00000000-0000-0000-0000-000000000003'; const VALID_TIMESTAMP = '2026-05-22T14:30:00.000Z'; describe('WsFrameSchema (v1.13.11-a)', () => { it('accepts a well-formed chat_status frame', () => { const result = WsFrameSchema.safeParse({ type: 'chat_status', chat_id: VALID_UUID_A, status: 'streaming', at: VALID_TIMESTAMP, }); expect(result.success).toBe(true); }); it('rejects an unknown frame type', () => { const result = WsFrameSchema.safeParse({ type: 'cosmic_ray_strike', chat_id: VALID_UUID_A, }); expect(result.success).toBe(false); }); it('rejects a chat_status frame with invalid status enum', () => { // v1.12.1 dropped the legacy 'working' status. Any frame still emitting it // should fail validation — that's a drift catcher. const result = WsFrameSchema.safeParse({ type: 'chat_status', chat_id: VALID_UUID_A, status: 'working', at: VALID_TIMESTAMP, }); expect(result.success).toBe(false); }); it('rejects a UUID field with a non-UUID string', () => { const result = WsFrameSchema.safeParse({ type: 'chat_status', chat_id: 'not-a-uuid', status: 'idle', at: VALID_TIMESTAMP, }); expect(result.success).toBe(false); }); it('rejects negative token counts in usage frame', () => { const result = WsFrameSchema.safeParse({ type: 'usage', message_id: VALID_UUID_A, chat_id: VALID_UUID_B, completion_tokens: -1, ctx_used: 100, ctx_max: 1000, }); expect(result.success).toBe(false); }); it('accepts a usage frame with nullable token counts (pre-v1.13.7 history)', () => { const result = WsFrameSchema.safeParse({ type: 'usage', message_id: VALID_UUID_A, chat_id: VALID_UUID_B, completion_tokens: null, ctx_used: null, ctx_max: null, }); expect(result.success).toBe(true); }); it('accepts a tool_result frame with non-UUID tool_call_id (model-emitted)', () => { // Model-emitted tool_call_ids look like "call_abc123", not UUIDs. const result = WsFrameSchema.safeParse({ type: 'tool_result', tool_message_id: VALID_UUID_A, chat_id: VALID_UUID_B, tool_call_id: 'call_abc123', output: { whatever: true }, truncated: false, }); expect(result.success).toBe(true); }); it('accepts a compacted frame', () => { const result = WsFrameSchema.safeParse({ type: 'compacted', session_id: VALID_UUID_A, chat_id: VALID_UUID_B, summary_message_id: VALID_UUID_C, }); expect(result.success).toBe(true); }); it('accepts a session_workspace_updated frame', () => { const result = WsFrameSchema.safeParse({ type: 'session_workspace_updated', session_id: VALID_UUID_A, workspace_panes: [{ id: 'p1', kind: 'chat', chatIds: [], activeChatIdx: 0 }], }); expect(result.success).toBe(true); }); it('accepts a message_complete frame with a null model (external coder, no model selected)', () => { // Regression guard: the dispatcher publishes `model: task.model` (string | // null). When null, this MUST validate or publishFrame fail-closes and drops // the whole frame, incl. the status:'complete' transition. const result = WsFrameSchema.safeParse({ type: 'message_complete', message_id: VALID_UUID_A, chat_id: VALID_UUID_B, model: null, }); expect(result.success).toBe(true); }); it('every KNOWN_FRAME_TYPES entry has a discriminated branch', () => { // Probe each known type by attempting a minimal valid construction. // Failure here means the union and the KNOWN_FRAME_TYPES list drifted. for (const type of KNOWN_FRAME_TYPES) { const probe = WsFrameSchema.safeParse({ type, __dummy__: true }); // We expect FAILURE on every type because we're missing required fields, // but the failure must be ABOUT the missing fields, not about an unknown // type. A "Invalid discriminator value" error means the type isn't in // the union — that's a drift. if (probe.success) continue; const issues = probe.error.issues; const hasInvalidDiscriminator = issues.some( (i) => i.code === 'invalid_union_discriminator', ); expect(hasInvalidDiscriminator, `frame type '${type}' is missing from the discriminated union`).toBe(false); } }); });