From 1906cf66637af09b2c1e86c6cae627a4ee02215e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Mon, 13 Jul 2026 13:46:24 -0500 Subject: [PATCH] test(coord): verify neutral route compatibility --- ...teraction-coordination.routing.e2e.test.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 apps/gateway/src/coord/interaction-coordination.routing.e2e.test.ts diff --git a/apps/gateway/src/coord/interaction-coordination.routing.e2e.test.ts b/apps/gateway/src/coord/interaction-coordination.routing.e2e.test.ts new file mode 100644 index 0000000..3a9da8d --- /dev/null +++ b/apps/gateway/src/coord/interaction-coordination.routing.e2e.test.ts @@ -0,0 +1,88 @@ +import 'reflect-metadata'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { Global, Module } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { FastifyAdapter, type NestFastifyApplication } from '@nestjs/platform-fastify'; +import { AUTH } from '../auth/auth.tokens.js'; +import { AuthGuard } from '../auth/auth.guard.js'; +import { InteractionCoordinationController } from './interaction-coordination.controller.js'; +import { InteractionCoordinationService } from './interaction-coordination.service.js'; + +@Global() +@Module({ + providers: [ + { + provide: AUTH, + useValue: { + api: { + getSession: vi.fn(async ({ headers }: { headers: Headers }) => + headers.get('cookie') === 'session=trusted' + ? { user: { id: 'operator-1', tenantId: 'tenant-1' }, session: { id: 'session-1' } } + : null, + ), + }, + }, + }, + AuthGuard, + ], + exports: [AUTH, AuthGuard], +}) +class AuthenticatedRequestModule {} + +describe('InteractionCoordinationController route aliases', (): void => { + let app: NestFastifyApplication | undefined; + const coordination = { + handoff: vi.fn(async () => ({ handoffId: 'handoff-1' })), + observe: vi.fn(async () => ({ status: 'running' })), + result: vi.fn(async () => ({ status: 'completed' })), + }; + + beforeAll(async (): Promise => { + const moduleRef = await Test.createTestingModule({ + imports: [AuthenticatedRequestModule], + controllers: [InteractionCoordinationController], + providers: [{ provide: InteractionCoordinationService, useValue: coordination }], + }).compile(); + app = moduleRef.createNestApplication(new FastifyAdapter()); + await app.init(); + await app.getHttpAdapter().getInstance().ready(); + }); + afterAll(async (): Promise => app?.close()); + + it('routes handoff, observe, and result through the same AuthGuard-protected service for both prefixes', async (): Promise => { + if (!app) throw new Error('test app was not initialized'); + for (const prefix of ['/api/coord/interaction', '/api/coord/mos']) { + const headers = { cookie: 'session=trusted', 'x-correlation-id': `corr-${prefix}` }; + expect( + ( + await app.inject({ + method: 'POST', + url: `${prefix}/handoff`, + headers, + payload: { idempotencyKey: `key-${prefix}`, summary: 'handoff' }, + }) + ).statusCode, + ).toBe(201); + expect( + (await app.inject({ method: 'GET', url: `${prefix}/handoff-1/observe`, headers })) + .statusCode, + ).toBe(200); + expect( + (await app.inject({ method: 'GET', url: `${prefix}/handoff-1/result`, headers })) + .statusCode, + ).toBe(200); + } + expect(coordination.handoff).toHaveBeenCalledTimes(2); + expect(coordination.observe).toHaveBeenCalledTimes(2); + expect(coordination.result).toHaveBeenCalledTimes(2); + expect( + ( + await app.inject({ + method: 'POST', + url: '/api/coord/interaction/handoff', + payload: { idempotencyKey: 'denied', summary: 'x' }, + }) + ).statusCode, + ).toBe(401); + }); +});