/** * Contract Validation Tests for Bootstrap Module * * These tests validate that the bootstrap module is properly configured and that * the initialization process follows expected patterns. The bootstrap module is * an internal initialization module that runs during application startup and * does not expose HTTP endpoints. * * Key Findings: * - Bootstrap module is an internal initialization module (not an API endpoint) * - It runs during application startup via OnModuleInit lifecycle hook * - It seeds the database with initial data (admin users, achievements, racing data) * - It does not expose any HTTP controllers or endpoints * - No API client exists in the website app for bootstrap operations * - No bootstrap-related endpoints are defined in the OpenAPI spec */ import * as fs from 'fs/promises'; import * as path from 'path'; import { describe, expect, it } from 'vitest'; interface OpenAPISpec { openapi: string; info: { title: string; description: string; version: string; }; paths: Record; components: { schemas: Record; }; } describe('Bootstrap Module Contract Validation', () => { const apiRoot = path.join(__dirname, '../..'); const openapiPath = path.join(apiRoot, 'apps/api/openapi.json'); const bootstrapModulePath = path.join(apiRoot, 'apps/api/src/domain/bootstrap/BootstrapModule.ts'); const bootstrapAdaptersPath = path.join(apiRoot, 'adapters/bootstrap'); describe('OpenAPI Spec Integrity for Bootstrap', () => { it('should NOT have bootstrap endpoints defined in OpenAPI spec', async () => { const content = await fs.readFile(openapiPath, 'utf-8'); const spec: OpenAPISpec = JSON.parse(content); // Bootstrap is an internal module, not an API endpoint // Verify no bootstrap-related paths exist const bootstrapPaths = Object.keys(spec.paths).filter(p => p.includes('bootstrap')); expect(bootstrapPaths.length).toBe(0); }); it('should NOT have bootstrap-related DTOs in OpenAPI spec', async () => { const content = await fs.readFile(openapiPath, 'utf-8'); const spec: OpenAPISpec = JSON.parse(content); // Bootstrap module doesn't expose DTOs for API consumption // It uses internal DTOs for seeding data const bootstrapSchemas = Object.keys(spec.components.schemas).filter(s => s.toLowerCase().includes('bootstrap') || s.toLowerCase().includes('seed') ); expect(bootstrapSchemas.length).toBe(0); }); }); describe('Bootstrap Module Structure', () => { it('should have BootstrapModule defined', async () => { const bootstrapModuleExists = await fs.access(bootstrapModulePath).then(() => true).catch(() => false); expect(bootstrapModuleExists).toBe(true); }); it('should have BootstrapModule implement OnModuleInit', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify it implements OnModuleInit lifecycle hook expect(bootstrapModuleContent).toContain('implements OnModuleInit'); expect(bootstrapModuleContent).toContain('async onModuleInit()'); }); it('should have BootstrapModule with proper dependencies', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify required dependencies are injected expect(bootstrapModuleContent).toContain('@Inject(ENSURE_INITIAL_DATA_TOKEN)'); expect(bootstrapModuleContent).toContain('@Inject(SEED_DEMO_USERS_TOKEN)'); expect(bootstrapModuleContent).toContain('@Inject(\'Logger\')'); expect(bootstrapModuleContent).toContain('@Inject(\'RacingSeedDependencies\')'); }); it('should have BootstrapModule with proper imports', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify persistence modules are imported expect(bootstrapModuleContent).toContain('RacingPersistenceModule'); expect(bootstrapModuleContent).toContain('SocialPersistenceModule'); expect(bootstrapModuleContent).toContain('AchievementPersistenceModule'); expect(bootstrapModuleContent).toContain('IdentityPersistenceModule'); expect(bootstrapModuleContent).toContain('AdminPersistenceModule'); }); }); describe('Bootstrap Adapters Structure', () => { it('should have EnsureInitialData adapter', async () => { const ensureInitialDataPath = path.join(bootstrapAdaptersPath, 'EnsureInitialData.ts'); const ensureInitialDataExists = await fs.access(ensureInitialDataPath).then(() => true).catch(() => false); expect(ensureInitialDataExists).toBe(true); }); it('should have SeedDemoUsers adapter', async () => { const seedDemoUsersPath = path.join(bootstrapAdaptersPath, 'SeedDemoUsers.ts'); const seedDemoUsersExists = await fs.access(seedDemoUsersPath).then(() => true).catch(() => false); expect(seedDemoUsersExists).toBe(true); }); it('should have SeedRacingData adapter', async () => { const seedRacingDataPath = path.join(bootstrapAdaptersPath, 'SeedRacingData.ts'); const seedRacingDataExists = await fs.access(seedRacingDataPath).then(() => true).catch(() => false); expect(seedRacingDataExists).toBe(true); }); it('should have racing seed factories', async () => { const racingDir = path.join(bootstrapAdaptersPath, 'racing'); const racingDirExists = await fs.access(racingDir).then(() => true).catch(() => false); expect(racingDirExists).toBe(true); // Verify key factory files exist const racingFiles = await fs.readdir(racingDir); expect(racingFiles).toContain('RacingDriverFactory.ts'); expect(racingFiles).toContain('RacingTeamFactory.ts'); expect(racingFiles).toContain('RacingLeagueFactory.ts'); expect(racingFiles).toContain('RacingRaceFactory.ts'); }); }); describe('Bootstrap Configuration', () => { it('should have bootstrap configuration in environment', async () => { const envPath = path.join(apiRoot, 'apps/api/src/env.ts'); const envContent = await fs.readFile(envPath, 'utf-8'); // Verify bootstrap configuration functions exist expect(envContent).toContain('getEnableBootstrap'); expect(envContent).toContain('getForceReseed'); }); it('should have bootstrap enabled by default', async () => { const envPath = path.join(apiRoot, 'apps/api/src/env.ts'); const envContent = await fs.readFile(envPath, 'utf-8'); // Verify bootstrap is enabled by default (for dev/test) expect(envContent).toContain('GRIDPILOT_API_BOOTSTRAP'); expect(envContent).toContain('true'); // Default value }); }); describe('Bootstrap Initialization Logic', () => { it('should have proper initialization sequence', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify initialization sequence expect(bootstrapModuleContent).toContain('await this.ensureInitialData.execute()'); expect(bootstrapModuleContent).toContain('await this.shouldSeedRacingData()'); expect(bootstrapModuleContent).toContain('await this.shouldSeedDemoUsers()'); }); it('should have environment-aware seeding logic', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify environment checks expect(bootstrapModuleContent).toContain('process.env.NODE_ENV'); expect(bootstrapModuleContent).toContain('production'); expect(bootstrapModuleContent).toContain('inmemory'); expect(bootstrapModuleContent).toContain('postgres'); }); it('should have force reseed capability', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify force reseed logic expect(bootstrapModuleContent).toContain('getForceReseed()'); expect(bootstrapModuleContent).toContain('Force reseed enabled'); }); }); describe('Bootstrap Data Seeding', () => { it('should seed initial admin user', async () => { const ensureInitialDataPath = path.join(bootstrapAdaptersPath, 'EnsureInitialData.ts'); const ensureInitialDataContent = await fs.readFile(ensureInitialDataPath, 'utf-8'); // Verify admin user seeding expect(ensureInitialDataContent).toContain('admin@gridpilot.local'); expect(ensureInitialDataContent).toContain('Admin'); expect(ensureInitialDataContent).toContain('signupUseCase'); }); it('should seed achievements', async () => { const ensureInitialDataPath = path.join(bootstrapAdaptersPath, 'EnsureInitialData.ts'); const ensureInitialDataContent = await fs.readFile(ensureInitialDataPath, 'utf-8'); // Verify achievement seeding expect(ensureInitialDataContent).toContain('DRIVER_ACHIEVEMENTS'); expect(ensureInitialDataContent).toContain('STEWARD_ACHIEVEMENTS'); expect(ensureInitialDataContent).toContain('ADMIN_ACHIEVEMENTS'); expect(ensureInitialDataContent).toContain('COMMUNITY_ACHIEVEMENTS'); expect(ensureInitialDataContent).toContain('createAchievementUseCase'); }); it('should seed demo users', async () => { const seedDemoUsersPath = path.join(bootstrapAdaptersPath, 'SeedDemoUsers.ts'); const seedDemoUsersContent = await fs.readFile(seedDemoUsersPath, 'utf-8'); // Verify demo user seeding expect(seedDemoUsersContent).toContain('SeedDemoUsers'); expect(seedDemoUsersContent).toContain('execute'); }); it('should seed racing data', async () => { const seedRacingDataPath = path.join(bootstrapAdaptersPath, 'SeedRacingData.ts'); const seedRacingDataContent = await fs.readFile(seedRacingDataPath, 'utf-8'); // Verify racing data seeding expect(seedRacingDataContent).toContain('SeedRacingData'); expect(seedRacingDataContent).toContain('execute'); expect(seedRacingDataContent).toContain('RacingSeedDependencies'); }); }); describe('Bootstrap Providers', () => { it('should have BootstrapProviders defined', async () => { const bootstrapProvidersPath = path.join(apiRoot, 'apps/api/src/domain/bootstrap/BootstrapProviders.ts'); const bootstrapProvidersExists = await fs.access(bootstrapProvidersPath).then(() => true).catch(() => false); expect(bootstrapProvidersExists).toBe(true); }); it('should have proper provider tokens', async () => { const bootstrapProvidersContent = await fs.readFile( path.join(apiRoot, 'apps/api/src/domain/bootstrap/BootstrapProviders.ts'), 'utf-8' ); // Verify provider tokens are defined expect(bootstrapProvidersContent).toContain('ENSURE_INITIAL_DATA_TOKEN'); expect(bootstrapProvidersContent).toContain('SEED_DEMO_USERS_TOKEN'); }); }); describe('Bootstrap Module Integration', () => { it('should be imported in main app module', async () => { const appModulePath = path.join(apiRoot, 'apps/api/src/app.module.ts'); const appModuleContent = await fs.readFile(appModulePath, 'utf-8'); // Verify BootstrapModule is imported expect(appModuleContent).toContain('BootstrapModule'); expect(appModuleContent).toContain('./domain/bootstrap/BootstrapModule'); }); it('should be included in app module imports', async () => { const appModulePath = path.join(apiRoot, 'apps/api/src/app.module.ts'); const appModuleContent = await fs.readFile(appModulePath, 'utf-8'); // Verify BootstrapModule is in imports array expect(appModuleContent).toMatch(/imports:\s*\[[^\]]*BootstrapModule[^\]]*\]/s); }); }); describe('Bootstrap Module Tests', () => { it('should have unit tests for BootstrapModule', async () => { const bootstrapModuleTestPath = path.join(apiRoot, 'apps/api/src/domain/bootstrap/BootstrapModule.test.ts'); const bootstrapModuleTestExists = await fs.access(bootstrapModuleTestPath).then(() => true).catch(() => false); expect(bootstrapModuleTestExists).toBe(true); }); it('should have postgres seed tests', async () => { const postgresSeedTestPath = path.join(apiRoot, 'apps/api/src/domain/bootstrap/BootstrapModule.postgres-seed.test.ts'); const postgresSeedTestExists = await fs.access(postgresSeedTestPath).then(() => true).catch(() => false); expect(postgresSeedTestExists).toBe(true); }); it('should have racing seed tests', async () => { const racingSeedTestPath = path.join(apiRoot, 'apps/api/src/domain/bootstrap/RacingSeed.test.ts'); const racingSeedTestExists = await fs.access(racingSeedTestPath).then(() => true).catch(() => false); expect(racingSeedTestExists).toBe(true); }); }); describe('Bootstrap Module Contract Summary', () => { it('should document that bootstrap is an internal module', async () => { const bootstrapModuleContent = await fs.readFile(bootstrapModulePath, 'utf-8'); // Verify bootstrap is documented as internal initialization expect(bootstrapModuleContent).toContain('Initializing application data'); expect(bootstrapModuleContent).toContain('Bootstrap disabled'); }); it('should have no API client in website app', async () => { const websiteApiDir = path.join(apiRoot, 'apps/website/lib/api'); const apiFiles = await fs.readdir(websiteApiDir); // Verify no bootstrap API client exists const bootstrapFiles = apiFiles.filter(f => f.toLowerCase().includes('bootstrap')); expect(bootstrapFiles.length).toBe(0); }); it('should have no bootstrap endpoints in OpenAPI', async () => { const content = await fs.readFile(openapiPath, 'utf-8'); const spec: OpenAPISpec = JSON.parse(content); // Verify no bootstrap paths exist const allPaths = Object.keys(spec.paths); const bootstrapPaths = allPaths.filter(p => p.toLowerCase().includes('bootstrap')); expect(bootstrapPaths.length).toBe(0); }); }); });