Files
TREK/server/tests/unit/nest/route-inventory.test.ts
T
2026-10-06 21:50:59 +02:00

104 lines
3.9 KiB
TypeScript

/**
* The anonymous surface of the server, as a list.
*
* This is what makes default-deny durable. The APP_GUARD protects what exists
* today; this fails when a NEW route arrives that answers without a session and
* nobody wrote it down. The same shape as the body-contract gate, and for the
* same reason: a rule with no inventory behind it decays into whatever the last
* person needed to make a test pass.
*/
import { AppModule } from '../../../src/nest/app.module';
import {
ANONYMOUS_GUARDED_ROUTE_ALLOW_LIST,
collectRouteGuards,
PUBLIC_ROUTE_ALLOW_LIST,
validateRouteGuards,
} from '../../../src/nest/common/validate-route-guards';
import { Test } from '@nestjs/testing';
import { describe, it, expect, vi } from 'vitest';
vi.mock('../../../src/db/database', async () => {
const { createSnapshotTestDb, buildDbMock } = await import('../../helpers/db-mock');
return buildDbMock(createSnapshotTestDb());
});
async function buildApp() {
// Plan 4 Task 4: `DatabaseService` is gone — nothing left in the graph
// needs overriding for this to compile.
const moduleRef = await Test.createTestingModule({ imports: [AppModule] }).compile();
return moduleRef.createNestApplication();
}
describe('route guard inventory', () => {
it('ROUTES-001: every @Public() route is in the reviewed list, and every listed one still exists', async () => {
const app = await buildApp();
try {
expect(() => validateRouteGuards(app)).not.toThrow();
} finally {
await app.close();
}
});
it('ROUTES-002: the list is sorted and free of duplicates, so a diff to it reads', () => {
expect([...PUBLIC_ROUTE_ALLOW_LIST].sort()).toEqual(PUBLIC_ROUTE_ALLOW_LIST);
expect(new Set(PUBLIC_ROUTE_ALLOW_LIST).size).toBe(PUBLIC_ROUTE_ALLOW_LIST.length);
});
it('ROUTES-003: an unlisted public route fails the gate, naming it', async () => {
const app = await buildApp();
try {
// Drop one entry: the gate must notice the route it no longer covers.
const short = PUBLIC_ROUTE_ALLOW_LIST.slice(1);
expect(() => validateRouteGuards(app, short)).toThrow(PUBLIC_ROUTE_ALLOW_LIST[0]);
} finally {
await app.close();
}
});
it('ROUTES-004: a stale entry fails too — an unpruned list is not a list', async () => {
const app = await buildApp();
try {
expect(() => validateRouteGuards(app, [...PUBLIC_ROUTE_ALLOW_LIST, 'GhostController.vanished'].sort())).toThrow(
'GhostController.vanished',
);
} finally {
await app.close();
}
});
it('ROUTES-005: nothing is left uncovered by accident — every route is public, optional, guarded or default-denied', async () => {
const app = await buildApp();
try {
const entries = collectRouteGuards(app);
// The inventory only lists routes that opted out of the default. Anything
// absent from it falls to GlobalAuthGuard, which is the safe direction.
const covers = ['public', 'optional-auth', 'declared-guards', 'declared-guards-anonymous'];
expect(entries.every((e) => covers.includes(e.cover))).toBe(true);
expect(entries.filter((e) => e.cover === 'public').length).toBe(PUBLIC_ROUTE_ALLOW_LIST.length);
expect(entries.filter((e) => e.cover === 'declared-guards-anonymous').map((e) => e.id)).toEqual(
ANONYMOUS_GUARDED_ROUTE_ALLOW_LIST,
);
} finally {
await app.close();
}
});
it('ROUTES-006: a guard chain that never authenticates fails the gate unless it is written down', async () => {
const app = await buildApp();
try {
expect(() => validateRouteGuards(app, PUBLIC_ROUTE_ALLOW_LIST, [])).toThrow(
ANONYMOUS_GUARDED_ROUTE_ALLOW_LIST[0],
);
expect(() =>
validateRouteGuards(app, PUBLIC_ROUTE_ALLOW_LIST, [
...ANONYMOUS_GUARDED_ROUTE_ALLOW_LIST,
'GhostController.anonymous',
]),
).toThrow('GhostController.anonymous');
} finally {
await app.close();
}
});
});