import { ForbiddenException } from '@nestjs/common'; import type { ExecutionContext } from '@nestjs/common'; import type { Reflector } from '@nestjs/core'; import { TenantGuard, type AuthenticatedSession } from './tenant.guard'; interface FakeRequest { session?: AuthenticatedSession; body?: Record; query?: Record; } function contextFor(request: FakeRequest, meta: { isPublic?: boolean; isAuthOnly?: boolean } = {}) { const reflector = { getAllAndOverride: jest.fn((key: string) => { if (key === 'isPublic') { return meta.isPublic ?? false; } if (key === 'isAuthOnly') { return meta.isAuthOnly ?? false; } return false; }), } as unknown as Reflector; const context = { getHandler: () => ({}), getClass: () => ({}), switchToHttp: () => ({ getRequest: () => request }), } as unknown as ExecutionContext; return { guard: new TenantGuard(reflector), context }; } const session: AuthenticatedSession = { requestId: '00000000-0000-0000-0000-0000000000r1', correlationId: '00000000-0000-0000-0000-0000000000c1', sessionId: 'sess-1', userId: '11111111-1111-1111-1111-111111111111', tenantId: '22222222-2222-2222-2222-222222222222', workspaceId: '44444444-4444-4444-4444-444444444444', membershipId: '55555555-5555-5555-5555-555555555555', role: 'member', roles: ['member'], permissions: ['task:read'], purpose: 'user_request', timezone: 'Europe/Bucharest', source: 'web', }; describe('TenantGuard', () => { it('rejects requests without an active tenant session (deny-by-default)', () => { const { guard, context } = contextFor({ body: {}, query: {} }); expect(() => guard.canActivate(context)).toThrow(ForbiddenException); }); it('rejects client-supplied tenantId that differs from the session tenant', () => { const { guard, context } = contextFor({ session, body: { tenantId: '33333333-3333-3333-3333-333333333333' }, query: {}, }); expect(() => guard.canActivate(context)).toThrow(ForbiddenException); }); it('rejects tenantId smuggled through the query string', () => { const { guard, context } = contextFor({ session, body: {}, query: { tenantId: '33333333-3333-3333-3333-333333333333' }, }); expect(() => guard.canActivate(context)).toThrow(ForbiddenException); }); it('allows a request whose session tenant matches', () => { const { guard, context } = contextFor({ session, body: {}, query: {} }); expect(guard.canActivate(context)).toBe(true); }); it('allows public routes without a session', () => { const { guard, context } = contextFor({}, { isPublic: true }); expect(guard.canActivate(context)).toBe(true); }); it('allows auth-only bootstrap routes without a tenant', () => { const { guard, context } = contextFor({}, { isAuthOnly: true }); expect(guard.canActivate(context)).toBe(true); }); });