|
| 1 | +import assert from 'node:assert/strict'; |
| 2 | +import { test } from 'vitest'; |
| 3 | +import { DAEMON_COMMAND_GROUPS, INTERNAL_COMMANDS } from '../../command-catalog.ts'; |
| 4 | +import { FIND_COMMAND_HANDLERS } from '../handlers/find.ts'; |
| 5 | +import { INTERACTION_COMMAND_HANDLERS } from '../handlers/interaction.ts'; |
| 6 | +import { handleLeaseCommands, LEASE_COMMAND_HANDLERS } from '../handlers/lease.ts'; |
| 7 | +import { REACT_NATIVE_COMMAND_HANDLERS } from '../handlers/react-native.ts'; |
| 8 | +import { RECORD_TRACE_COMMAND_HANDLERS } from '../handlers/record-trace.ts'; |
| 9 | +import { SESSION_COMMAND_HANDLERS } from '../handlers/session.ts'; |
| 10 | +import { SNAPSHOT_COMMAND_HANDLERS } from '../handlers/snapshot.ts'; |
| 11 | +import { LeaseRegistry } from '../lease-registry.ts'; |
| 12 | + |
| 13 | +const handlerFamilies = [ |
| 14 | + { |
| 15 | + name: 'leaseHandler', |
| 16 | + commands: DAEMON_COMMAND_GROUPS.leaseHandler, |
| 17 | + handlers: LEASE_COMMAND_HANDLERS, |
| 18 | + }, |
| 19 | + { |
| 20 | + name: 'sessionHandler', |
| 21 | + commands: DAEMON_COMMAND_GROUPS.sessionHandler, |
| 22 | + handlers: SESSION_COMMAND_HANDLERS, |
| 23 | + }, |
| 24 | + { |
| 25 | + name: 'snapshot', |
| 26 | + commands: DAEMON_COMMAND_GROUPS.snapshot, |
| 27 | + handlers: SNAPSHOT_COMMAND_HANDLERS, |
| 28 | + }, |
| 29 | + { |
| 30 | + name: 'reactNativeHandler', |
| 31 | + commands: DAEMON_COMMAND_GROUPS.reactNativeHandler, |
| 32 | + handlers: REACT_NATIVE_COMMAND_HANDLERS, |
| 33 | + }, |
| 34 | + { |
| 35 | + name: 'recordTraceHandler', |
| 36 | + commands: DAEMON_COMMAND_GROUPS.recordTraceHandler, |
| 37 | + handlers: RECORD_TRACE_COMMAND_HANDLERS, |
| 38 | + }, |
| 39 | + { |
| 40 | + name: 'findHandler', |
| 41 | + commands: DAEMON_COMMAND_GROUPS.findHandler, |
| 42 | + handlers: FIND_COMMAND_HANDLERS, |
| 43 | + }, |
| 44 | + { |
| 45 | + name: 'interactionHandler', |
| 46 | + commands: DAEMON_COMMAND_GROUPS.interactionHandler, |
| 47 | + handlers: INTERACTION_COMMAND_HANDLERS, |
| 48 | + }, |
| 49 | +] as const; |
| 50 | + |
| 51 | +test('daemon handler routing groups match handler coverage', () => { |
| 52 | + for (const { name, commands, handlers } of handlerFamilies) { |
| 53 | + assert.deepEqual( |
| 54 | + Object.keys(handlers).sort(), |
| 55 | + [...commands].sort(), |
| 56 | + `${name} catalog must match its handler module`, |
| 57 | + ); |
| 58 | + } |
| 59 | +}); |
| 60 | + |
| 61 | +test('lease handler coverage table points at executable commands', async () => { |
| 62 | + const leaseRegistry = new LeaseRegistry(); |
| 63 | + const allocated = leaseRegistry.allocateLease({ tenantId: 'tenant-a', runId: 'run-a' }); |
| 64 | + |
| 65 | + for (const command of Object.keys(LEASE_COMMAND_HANDLERS)) { |
| 66 | + const response = await handleLeaseCommands({ |
| 67 | + req: { |
| 68 | + command, |
| 69 | + token: 'test-token', |
| 70 | + session: 'catalog-test', |
| 71 | + flags: { |
| 72 | + tenant: 'tenant-a', |
| 73 | + runId: 'run-a', |
| 74 | + ...(command === INTERNAL_COMMANDS.leaseAllocate |
| 75 | + ? {} |
| 76 | + : { leaseId: allocated.leaseId }), |
| 77 | + }, |
| 78 | + positionals: [], |
| 79 | + }, |
| 80 | + leaseRegistry, |
| 81 | + }); |
| 82 | + |
| 83 | + assert.notEqual(response, null, `${command} should be handled by lease handler`); |
| 84 | + } |
| 85 | +}); |
| 86 | + |
| 87 | +test('daemon handler routing groups are disjoint', () => { |
| 88 | + const ownerByCommand = new Map<string, string>(); |
| 89 | + for (const { name, commands } of handlerFamilies) { |
| 90 | + for (const command of commands) { |
| 91 | + const previousOwner = ownerByCommand.get(command); |
| 92 | + assert.equal( |
| 93 | + previousOwner, |
| 94 | + undefined, |
| 95 | + `${command} is routed by both ${previousOwner} and ${name}`, |
| 96 | + ); |
| 97 | + ownerByCommand.set(command, name); |
| 98 | + } |
| 99 | + } |
| 100 | +}); |
0 commit comments