wire-events.spec.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. /**
  2. * Wire-to-typed-event bridge (web input-triggers cut 1): host/commands-changed
  3. * → ctx 'commands/changed'; each established connection generation →
  4. * ctx 'connection/reset' (the forced cache-invalidation broadcast).
  5. */
  6. import { Context } from 'cordis'
  7. import { describe, expect, it } from 'vitest'
  8. import type { ConnectionHandle, ConnectionSinks } from '@deepseek-ai/dsh-client-connection/client'
  9. import * as RuntimeClient from '../src/client/index.ts'
  10. import { FakeApiClient } from './fake-api.ts'
  11. interface Bench {
  12. ctx: Context
  13. sinks: ConnectionSinks | undefined
  14. }
  15. async function mount(): Promise<Bench> {
  16. const ctx = new Context()
  17. const api = new FakeApiClient()
  18. const bench: Bench = { ctx, sinks: undefined }
  19. const handle: ConnectionHandle = {
  20. api,
  21. isLoopback: true,
  22. start: (sinks) => {
  23. bench.sinks = sinks
  24. return { stop: () => {} }
  25. },
  26. }
  27. ctx.reflect.provide('connection', handle)
  28. await ctx.plugin(RuntimeClient).await()
  29. return bench
  30. }
  31. describe('wire event bridge', () => {
  32. it('broadcasts commands/changed on a host/commands-changed frame, not on other host frames', async () => {
  33. const bench = await mount()
  34. let changed = 0
  35. bench.ctx.on('commands/changed', () => { changed++ })
  36. bench.sinks?.onHostEnvelope?.({ rpcId: 'r1' as never, payload: { type: 'host/commands-changed' } })
  37. expect(changed).toBe(1)
  38. bench.sinks?.onHostEnvelope?.({
  39. rpcId: 'r2' as never,
  40. payload: { type: 'host/session-status', sessionId: 's1' as never, running: true },
  41. })
  42. expect(changed).toBe(1)
  43. })
  44. it('broadcasts the settings/credentials/models invalidations with their frame payloads', async () => {
  45. const bench = await mount()
  46. const seen: unknown[][] = []
  47. bench.ctx.on('settings/changed', ns => seen.push(['settings', ns]))
  48. bench.ctx.on('credentials/changed', ref => seen.push(['credentials', ref]))
  49. bench.ctx.on('models/changed', () => seen.push(['models']))
  50. bench.sinks?.onHostEnvelope?.({ rpcId: 'r3' as never, payload: { type: 'host/settings-changed', ns: 'llm-pi-ai' } })
  51. bench.sinks?.onHostEnvelope?.({ rpcId: 'r4' as never, payload: { type: 'host/credentials-changed', ref: 'OPENAI_API_KEY' } })
  52. bench.sinks?.onHostEnvelope?.({ rpcId: 'r5' as never, payload: { type: 'host/models-changed' } })
  53. expect(seen).toEqual([
  54. ['settings', 'llm-pi-ai'],
  55. ['credentials', 'OPENAI_API_KEY'],
  56. ['models'],
  57. ])
  58. })
  59. it('broadcasts connection/reset on every established generation (reconnect invalidation)', async () => {
  60. const bench = await mount()
  61. let resets = 0
  62. bench.ctx.on('connection/reset', () => { resets++ })
  63. bench.sinks?.onConnected?.()
  64. bench.sinks?.onConnected?.() // second generation after a reconnect
  65. expect(resets).toBe(2)
  66. })
  67. })