rpc-schema.host.spec.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import { describe, expect, it } from 'vitest'
  2. import { RpcId, transportError } from '../src/rpc.ts'
  3. import {
  4. clientRequestSchema,
  5. rpcErrorSchema,
  6. rpcIdSchema,
  7. rpcMessageSchema,
  8. rpcResultSchema,
  9. serverResponseSchema,
  10. } from '../src/rpc-schema.ts'
  11. import { z } from 'zod'
  12. describe('Connection RPC schema', () => {
  13. it('brands any validated string correlation id', () => {
  14. expect(RpcId('abc')).toBe('abc')
  15. expect(rpcIdSchema.parse('')).toBe('')
  16. expect(() => rpcIdSchema.parse(42)).toThrow()
  17. })
  18. it('folds transport exceptions into an internal failure', () => {
  19. expect(transportError(new Error('wire down'))).toEqual({
  20. ok: false,
  21. error: { code: 'gateway/internal', message: 'wire down', details: {} },
  22. })
  23. expect(transportError('raw')).toMatchObject({
  24. ok: false,
  25. error: { code: 'gateway/internal', message: 'raw' },
  26. })
  27. })
  28. it('validates generic failures and both result branches', () => {
  29. expect(rpcErrorSchema.parse({ code: 'domain-failure', message: 'failed', details: { id: 'x' } }))
  30. .toEqual({ code: 'domain-failure', message: 'failed', details: { id: 'x' } })
  31. expect(() => rpcErrorSchema.parse({ code: 1, message: 'failed', details: {} })).toThrow()
  32. expect(() => rpcErrorSchema.parse({ code: 'failed', message: 'failed', details: [] })).toThrow()
  33. const schema = rpcResultSchema(z.object({ n: z.number() }))
  34. expect(schema.parse({ ok: true, value: { n: 1 } })).toEqual({ ok: true, value: { n: 1 } })
  35. expect(schema.parse({ ok: false, error: { code: 'failed', message: 'x', details: {} } }))
  36. .toMatchObject({ ok: false })
  37. expect(() => schema.parse({ ok: true, error: {} })).toThrow()
  38. })
  39. it('validates both envelope directions and valueless success', () => {
  40. const request = { type: 'client-request', rpcId: 'r1', method: 'settings/describe', payload: { args: {} } }
  41. const response = { type: 'server-response', rpcId: 'r1', result: { ok: true, value: 1 } }
  42. expect(clientRequestSchema.parse(request).method).toBe('settings/describe')
  43. expect(serverResponseSchema.parse(response).rpcId).toBe('r1')
  44. for (const message of [request, response]) expect(rpcMessageSchema.parse(message)).toBeTruthy()
  45. expect(() => rpcMessageSchema.parse({ type: 'other', rpcId: 'x' })).toThrow()
  46. expect(() => clientRequestSchema.parse({ type: 'client-request', rpcId: 'r1' })).toThrow()
  47. expect(() => serverResponseSchema.parse({ type: 'server-response', rpcId: 'r1' })).toThrow()
  48. expect(() => serverResponseSchema.parse({ type: 'server-response', rpcId: 'r1', result: {} })).toThrow()
  49. expect(serverResponseSchema.parse({
  50. type: 'server-response', rpcId: 'r1', result: { ok: true },
  51. }).rpcId).toBe('r1')
  52. })
  53. })