import { describe, expect, test } from "bun:test"; import { createGeneration } from "./v3-generation.js"; import { buildRpcRequest, buildRpcResponse, rpcRequestSchema, rpcRequestWithViewIdSchema, rpcResponseSchema, rpcResponseWithViewIdSchema, } from "./v3-rpc-messages.js"; describe("v3 rpc messages", () => { test("accepts a valid rpc request", () => { const rpcId = crypto.randomUUID(); const result = rpcRequestSchema.safeParse({ t: "getuserfeedback:rpc:request", gen: createGeneration(1), rpcId, method: "flow.submit", params: { answer: 42 }, idemKey: "idem-1", }); expect(result.success).toBe(true); expect(result.success && result.data.rpcId).toBe(rpcId); }); test("rejects a non-uuid rpcId", () => { expect( rpcRequestSchema.safeParse({ t: "getuserfeedback:rpc:request", gen: createGeneration(1), rpcId: "not-a-uuid", method: "flow.submit", params: undefined, idemKey: "idem-1", }).success, ).toBe(false); }); test("parses both rpc response branches and defaults retriable", () => { const okResult = rpcResponseSchema.safeParse({ t: "getuserfeedback:rpc:response", gen: createGeneration(2), rpcId: crypto.randomUUID(), ok: true, result: { done: true }, }); expect(okResult.success).toBe(true); const errorResult = rpcResponseSchema.safeParse({ t: "getuserfeedback:rpc:response", gen: createGeneration(2), rpcId: crypto.randomUUID(), ok: false, error: "boom", }); expect(errorResult.success).toBe(true); expect(errorResult.success && errorResult.data).toMatchObject({ ok: false, error: "boom", retriable: false, }); }); test("builds requests and responses that satisfy their own schemas", () => { const request = buildRpcRequest({ gen: 3, rpcId: crypto.randomUUID(), method: "flow.open", params: { flowId: "flow-a" }, idemKey: "idem-2", viewId: "view-a", }); expect(request.gen).toEqual(createGeneration(3)); expect(rpcRequestSchema.safeParse(request).success).toBe(true); expect(rpcRequestWithViewIdSchema.safeParse(request).success).toBe(true); const success = buildRpcResponse({ gen: createGeneration(3), rpcId: crypto.randomUUID(), ok: true, result: { ok: 1 }, viewId: "view-a", }); expect(rpcResponseSchema.safeParse(success).success).toBe(true); expect(rpcResponseWithViewIdSchema.safeParse(success).success).toBe(true); const failure = buildRpcResponse({ gen: 3, rpcId: crypto.randomUUID(), ok: false, error: "nope", }); expect(failure).toMatchObject({ ok: false, retriable: false }); expect(rpcResponseSchema.safeParse(failure).success).toBe(true); const retriableFailure = buildRpcResponse({ gen: 3, rpcId: crypto.randomUUID(), ok: false, error: "nope", retriable: true, }); expect(retriableFailure).toMatchObject({ retriable: true }); expect(rpcResponseSchema.safeParse(retriableFailure).success).toBe(true); }); });