import { Test } from '@nestjs/testing'; import { DiscoveryModule, DiscoveryService, MetadataScanner, Reflector, } from '@nestjs/core'; import { PATH_METADATA } from '@nestjs/common/constants'; import { AppModule } from '../src/app.module'; import { IS_PUBLIC_KEY } from '../src/auth/decorators/public.decorator'; import { REQUIRE_PERMISSION_KEY } from '../src/auth/decorators/require-permission.decorator'; import { PERMISSION_CATALOG } from '../src/auth/permissions/permission-catalog'; // Routes intentionally left without a role/permission check: they act on the // caller's own data (notifications) or are the auth bootstrap endpoint that // every authenticated user must be able to reach regardless of permissions. const ALLOWLIST = new Set([ 'NotificationsController.findAll', 'NotificationsController.markRead', 'AuthController.me', 'ProfileController.updateProfile', 'ProfileController.changePassword', 'ProfileController.deactivateAccount', 'ProfileController.uploadAvatar', 'ProfileController.deleteAvatar', 'ProfileController.getAvatar', 'ProfileController.enrollMfa', 'ProfileController.confirmMfaEnrollment', 'ProfileController.disableMfa', ]); describe('Authorization coverage (e2e)', () => { it('every non-public route requires a catalog-valid permission', async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule, DiscoveryModule], }).compile(); const discovery = moduleRef.get(DiscoveryService); const reflector = moduleRef.get(Reflector); const scanner = moduleRef.get(MetadataScanner); const catalogKeys = new Set( PERMISSION_CATALOG.map((entry) => entry.key), ); const uncoveredRoutes: string[] = []; const invalidPermissionKeys: string[] = []; for (const wrapper of discovery.getControllers()) { const instance = wrapper.instance as object | undefined; if (!instance) { continue; } const constructor = instance.constructor; const controllerName = constructor.name; const prototype = Object.getPrototypeOf(instance) as object; const isPublicClass = reflector.get(IS_PUBLIC_KEY, constructor) === true; for (const methodName of scanner.getAllMethodNames(prototype)) { const handler = (prototype as Record unknown>)[ methodName ]; const isRouteHandler = Reflect.getMetadata(PATH_METADATA, handler) !== undefined; if (!isRouteHandler) { continue; } const routeKey = `${controllerName}.${methodName}`; const isPublicMethod = reflector.get(IS_PUBLIC_KEY, handler) === true; if (isPublicClass || isPublicMethod || ALLOWLIST.has(routeKey)) { continue; } const permissions = reflector.getAllAndOverride( REQUIRE_PERMISSION_KEY, [handler, constructor], ); const hasPermissions = Array.isArray(permissions) && permissions.length > 0; if (!hasPermissions) { uncoveredRoutes.push(routeKey); } if (hasPermissions) { for (const key of permissions) { if (!catalogKeys.has(key)) { invalidPermissionKeys.push(`${routeKey} -> ${key}`); } } } } } expect(uncoveredRoutes).toEqual([]); expect(invalidPermissionKeys).toEqual([]); await moduleRef.close(); }); });