mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
cc79f561e5
JwtAuthGuard, OptionalJwtAuthGuard, CookieAuthGuard, AdminGuard, the global and MFA guards, @CurrentUser, @Public, JWT verification and the ephemeral token service lived in the auth domain, so every controller that checked a session depended on the whole account module and its imports. They import no domain themselves; under src/nest/auth-core they are shared kernel like common/, every importer points there directly, and AtlasModule and NotificationsModule stop importing AuthModule, which they only did for the demo gate. lint:boundaries counts auth-core as kernel: 57 fewer cyclic domain edges, the largest cycle shrinks from 46 to 40 domains.
65 lines
2.9 KiB
TypeScript
65 lines
2.9 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { HttpException } from '@nestjs/common';
|
|
import { Test } from '@nestjs/testing';
|
|
import { AppModule } from '../../../src/nest/app.module';
|
|
import { FeaturesController } from '../../../src/nest/health/features.controller';
|
|
import { AdminGuard } from '../../../src/nest/auth-core/admin.guard';
|
|
|
|
vi.mock('../../../src/config', async () => {
|
|
const { readEnv } = await import('../../../src/app-config');
|
|
const env = readEnv();
|
|
return {
|
|
ENCRYPTION_KEY: 'wiring-test-inert-encryption-key',
|
|
JWT_SECRET: 'wiring-test-inert-jwt-secret',
|
|
updateJwtSecret: vi.fn(),
|
|
DEFAULT_LANGUAGE: env.app.defaultLanguage,
|
|
SESSION_DURATION: env.session.duration,
|
|
SESSION_DURATION_MS: env.session.durationMs,
|
|
SESSION_DURATION_SECONDS: env.session.durationSeconds,
|
|
SESSION_DURATION_REMEMBER: env.session.durationRemember,
|
|
SESSION_DURATION_REMEMBER_MS: env.session.durationRememberMs,
|
|
SESSION_DURATION_REMEMBER_SECONDS: env.session.durationRememberSeconds,
|
|
};
|
|
});
|
|
|
|
function ctx(user: unknown) {
|
|
return { switchToHttp: () => ({ getRequest: () => ({ user }) }) } as never;
|
|
}
|
|
|
|
describe('AppModule wiring', () => {
|
|
it('registers the upstream feature modules and Tours exactly once', () => {
|
|
// Only plain class imports: forRoot()/forFeature() entries are dynamic
|
|
// (MikroOrmModule legitimately appears twice) and some resolve async.
|
|
const imports = Reflect.getMetadata('imports', AppModule) as unknown[];
|
|
const names = imports
|
|
.filter((entry): entry is { name: string } => typeof entry === 'function')
|
|
.map(entry => entry.name);
|
|
expect(new Set(names).size).toBe(names.length);
|
|
expect(names).toEqual(expect.arrayContaining([
|
|
'GoogleQuotaModule', 'ReceiptScanModule', 'SchoolHolidaysModule', 'DocSyncModule',
|
|
'DawarichModule', 'NotificationsModule', 'ToursModule',
|
|
]));
|
|
expect(names.filter(name => name === 'ToursModule')).toHaveLength(1);
|
|
});
|
|
|
|
it('compiles with the global filter + DB provider and resolves the controller', async () => {
|
|
// 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();
|
|
// The one test that builds the whole AppModule: resolving a controller out of
|
|
// it proves every module in the graph compiled, not just this one.
|
|
expect(moduleRef.get(FeaturesController)).toBeInstanceOf(FeaturesController);
|
|
});
|
|
});
|
|
|
|
describe('AdminGuard', () => {
|
|
const guard = new AdminGuard();
|
|
it('allows admins', () => {
|
|
expect(guard.canActivate(ctx({ role: 'admin' }))).toBe(true);
|
|
});
|
|
it('blocks non-admins and anonymous with 403 { error }', () => {
|
|
expect(() => guard.canActivate(ctx({ role: 'user' }))).toThrow(HttpException);
|
|
expect(() => guard.canActivate(ctx(undefined))).toThrow(HttpException);
|
|
});
|
|
});
|