import { IModule, ProviderDefinition, InjectionToken } from '@omnitron-dev/titan/nexus'; import { Application } from '@omnitron-dev/titan/application'; import type { IApplicationConfig } from '@omnitron-dev/titan/application'; // Import TestContainer from local nexus testing utilities (now in this package) import { TestContainer, createTestContainer } from './nexus/test-container.js'; /** * Options for creating a test module */ export interface TestModuleOptions { modules?: IModule[]; providers?: Array<[InjectionToken, ProviderDefinition]>; mocks?: Array<{ token: InjectionToken; mock: any; spy?: boolean; }>; config?: Partial; autoMock?: boolean; } /** * Test module for Titan applications */ export class TestModule { private container: TestContainer; private app?: Application; constructor(private options: TestModuleOptions = {}) { this.container = createTestContainer({ modules: options.modules || [], providers: options.providers || [], mocks: options.mocks || [], autoMock: options.autoMock || false, }); } /** * Get the test container */ getContainer(): TestContainer { return this.container; } /** * Create an application instance for testing */ async createApplication(): Promise { if (this.app) { return this.app; } const config: IApplicationConfig = { name: 'test-app', version: '0.0.0-test', ...this.options.config, }; this.app = new Application(config); // Use the test container for dependency injection (this.app as any).container = this.container; // Load modules if provided if (this.options.modules) { for (const module of this.options.modules) { this.container.loadModule(module); } } return this.app; } /** * Mock a dependency */ mock(token: InjectionToken, mock: T, spy = false): this { this.container.mock(token, mock, spy); return this; } /** * Stub a dependency with partial implementation */ stub(token: InjectionToken, stub: Partial): this { this.container.stub(token, stub); return this; } /** * Override a dependency */ override(token: InjectionToken): { useValue: (value: T) => TestModule; useClass: (cls: new (...args: any[]) => T) => TestModule; useFactory: (factory: (...args: any[]) => T) => TestModule; } { return { useValue: (value: T) => { this.container.override(token).useValue(value); return this; }, useClass: (cls: new (...args: any[]) => T) => { this.container.override(token).useClass(cls); return this; }, useFactory: (factory: (...args: any[]) => T) => { this.container.override(token).useFactory(factory); return this; }, }; } /** * Spy on a method */ spy(token: InjectionToken, method: keyof T): any { return this.container.spy(token, method); } /** * Get a service from the container */ get(token: InjectionToken): T { return this.container.resolve(token); } /** * Reset all mocks */ resetMocks(): void { this.container.resetMocks(); } /** * Clear all mocks */ clearMocks(): void { this.container.clearMocks(); } /** * Restore to original state */ restore(): void { this.container.restore(); } /** * Clean up resources */ async cleanup(): Promise { if (this.app) { await this.app.stop(); this.app = undefined; } this.container.restore(); } } /** * Create a test module */ export function createTestModule(options: TestModuleOptions = {}): TestModule { return new TestModule(options); } /** * Test module builder for fluent API */ export class TestModuleBuilder { private options: TestModuleOptions = { modules: [], providers: [], mocks: [], config: {}, }; /** * Add a module to test */ withModule(module: IModule): this { this.options.modules!.push(module); return this; } /** * Add a provider */ withProvider(token: InjectionToken, provider: ProviderDefinition): this { this.options.providers!.push([token, provider]); return this; } /** * Add a mock */ withMock(token: InjectionToken, mock: T, spy = false): this { this.options.mocks!.push({ token, mock, spy }); return this; } /** * Set application config */ withConfig(config: Partial): this { this.options.config = { ...this.options.config, ...config }; return this; } /** * Enable auto-mocking */ withAutoMock(): this { this.options.autoMock = true; return this; } /** * Build the test module */ build(): TestModule { return new TestModule(this.options); } } /** * Create a test module builder */ export function testModule(): TestModuleBuilder { return new TestModuleBuilder(); }