racing typeorm
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import 'reflect-metadata';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
type SetupOptions = {
|
||||
persistence: 'postgres' | 'inmemory';
|
||||
nodeEnv: string | undefined;
|
||||
bootstrapEnabled: boolean;
|
||||
leaguesCount: number;
|
||||
};
|
||||
|
||||
describe('BootstrapModule Postgres racing seed gating (unit)', () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
async function setup({
|
||||
persistence,
|
||||
nodeEnv,
|
||||
bootstrapEnabled,
|
||||
leaguesCount,
|
||||
}: SetupOptions): Promise<{
|
||||
seedExecute: ReturnType<typeof vi.fn>;
|
||||
ensureExecute: ReturnType<typeof vi.fn>;
|
||||
leagueCountAll: ReturnType<typeof vi.fn>;
|
||||
}> {
|
||||
process.env.NODE_ENV = nodeEnv;
|
||||
|
||||
vi.doMock('../../env', async () => {
|
||||
const actual = await vi.importActual<typeof import('../../env')>('../../env');
|
||||
return {
|
||||
...actual,
|
||||
getApiPersistence: () => persistence,
|
||||
getEnableBootstrap: () => bootstrapEnabled,
|
||||
};
|
||||
});
|
||||
|
||||
const seedExecute = vi.fn(async () => undefined);
|
||||
vi.doMock('../../../../../adapters/bootstrap/SeedRacingData', () => {
|
||||
class SeedRacingData {
|
||||
execute = seedExecute;
|
||||
}
|
||||
return { SeedRacingData };
|
||||
});
|
||||
|
||||
const { BootstrapModule } = await import('./BootstrapModule');
|
||||
|
||||
const ensureExecute = vi.fn(async () => undefined);
|
||||
|
||||
const leagueCountAll = vi.fn(async () => leaguesCount);
|
||||
|
||||
const bootstrapModule = new BootstrapModule(
|
||||
{ execute: ensureExecute } as any,
|
||||
{ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
|
||||
{
|
||||
leagueRepository: { countAll: leagueCountAll },
|
||||
} as any,
|
||||
);
|
||||
|
||||
await bootstrapModule.onModuleInit();
|
||||
|
||||
return { seedExecute, ensureExecute, leagueCountAll };
|
||||
}
|
||||
|
||||
it('seeds when inmemory + bootstrap enabled (existing behavior)', async () => {
|
||||
const { seedExecute, ensureExecute, leagueCountAll } = await setup({
|
||||
persistence: 'inmemory',
|
||||
nodeEnv: 'test',
|
||||
bootstrapEnabled: true,
|
||||
leaguesCount: 123,
|
||||
});
|
||||
|
||||
expect(ensureExecute).toHaveBeenCalledTimes(1);
|
||||
expect(leagueCountAll).toHaveBeenCalledTimes(0);
|
||||
expect(seedExecute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('seeds when postgres + non-production + bootstrap enabled + empty', async () => {
|
||||
const { seedExecute, ensureExecute, leagueCountAll } = await setup({
|
||||
persistence: 'postgres',
|
||||
nodeEnv: 'development',
|
||||
bootstrapEnabled: true,
|
||||
leaguesCount: 0,
|
||||
});
|
||||
|
||||
expect(ensureExecute).toHaveBeenCalledTimes(1);
|
||||
expect(leagueCountAll).toHaveBeenCalledTimes(1);
|
||||
expect(seedExecute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not seed when postgres + non-production + bootstrap enabled + not empty', async () => {
|
||||
const { seedExecute, ensureExecute, leagueCountAll } = await setup({
|
||||
persistence: 'postgres',
|
||||
nodeEnv: 'development',
|
||||
bootstrapEnabled: true,
|
||||
leaguesCount: 1,
|
||||
});
|
||||
|
||||
expect(ensureExecute).toHaveBeenCalledTimes(1);
|
||||
expect(leagueCountAll).toHaveBeenCalledTimes(1);
|
||||
expect(seedExecute).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('does not seed when postgres + production (even if empty)', async () => {
|
||||
const { seedExecute, ensureExecute, leagueCountAll } = await setup({
|
||||
persistence: 'postgres',
|
||||
nodeEnv: 'production',
|
||||
bootstrapEnabled: true,
|
||||
leaguesCount: 0,
|
||||
});
|
||||
|
||||
expect(ensureExecute).toHaveBeenCalledTimes(1);
|
||||
expect(leagueCountAll).toHaveBeenCalledTimes(0);
|
||||
expect(seedExecute).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('does not seed when postgres + non-production + bootstrap disabled (even if empty)', async () => {
|
||||
const { seedExecute, ensureExecute, leagueCountAll } = await setup({
|
||||
persistence: 'postgres',
|
||||
nodeEnv: 'development',
|
||||
bootstrapEnabled: false,
|
||||
leaguesCount: 0,
|
||||
});
|
||||
|
||||
expect(ensureExecute).toHaveBeenCalledTimes(0);
|
||||
expect(leagueCountAll).toHaveBeenCalledTimes(0);
|
||||
expect(seedExecute).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
@@ -2,13 +2,13 @@ import type { Logger } from '@core/shared/application';
|
||||
import type { EnsureInitialData } from '../../../../../adapters/bootstrap/EnsureInitialData';
|
||||
import { SeedRacingData, type RacingSeedDependencies } from '../../../../../adapters/bootstrap/SeedRacingData';
|
||||
import { Inject, Module, OnModuleInit } from '@nestjs/common';
|
||||
import { getApiPersistence } from '../../env';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { getApiPersistence, getEnableBootstrap } from '../../env';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
|
||||
import { BootstrapProviders, ENSURE_INITIAL_DATA_TOKEN } from './BootstrapProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
providers: BootstrapProviders,
|
||||
})
|
||||
export class BootstrapModule implements OnModuleInit {
|
||||
@@ -21,9 +21,14 @@ export class BootstrapModule implements OnModuleInit {
|
||||
async onModuleInit() {
|
||||
console.log('[Bootstrap] Initializing application data...');
|
||||
try {
|
||||
if (!getEnableBootstrap()) {
|
||||
this.logger.info('[Bootstrap] Bootstrap disabled via GRIDPILOT_API_BOOTSTRAP; skipping initialization');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.ensureInitialData.execute();
|
||||
|
||||
if (this.shouldSeedRacingData()) {
|
||||
if (await this.shouldSeedRacingData()) {
|
||||
await new SeedRacingData(this.logger, this.seedDeps).execute();
|
||||
}
|
||||
|
||||
@@ -34,7 +39,21 @@ export class BootstrapModule implements OnModuleInit {
|
||||
}
|
||||
}
|
||||
|
||||
private shouldSeedRacingData(): boolean {
|
||||
return getApiPersistence() === 'inmemory';
|
||||
private async shouldSeedRacingData(): Promise<boolean> {
|
||||
const persistence = getApiPersistence();
|
||||
|
||||
if (persistence === 'inmemory') return true;
|
||||
if (persistence !== 'postgres') return false;
|
||||
if (process.env.NODE_ENV === 'production') return false;
|
||||
|
||||
return this.isRacingDatabaseEmpty();
|
||||
}
|
||||
|
||||
private async isRacingDatabaseEmpty(): Promise<boolean> {
|
||||
const count = await this.seedDeps.leagueRepository.countAll?.();
|
||||
if (typeof count === 'number') return count === 0;
|
||||
|
||||
const leagues = await this.seedDeps.leagueRepository.findAll();
|
||||
return leagues.length === 0;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
|
||||
import { DashboardService } from './DashboardService';
|
||||
import { DashboardController } from './DashboardController';
|
||||
import { DashboardProviders } from './DashboardProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
controllers: [DashboardController],
|
||||
providers: [DashboardService, ...DashboardProviders],
|
||||
exports: [DashboardService],
|
||||
|
||||
@@ -14,7 +14,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
password: process.env.DATABASE_PASSWORD || 'password',
|
||||
database: process.env.DATABASE_NAME || 'gridpilot',
|
||||
}),
|
||||
// entities: [AnalyticsSnapshotOrmEntity, EngagementOrmEntity],
|
||||
autoLoadEntities: true,
|
||||
synchronize: process.env.NODE_ENV !== 'production',
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
|
||||
import { DriverService } from './DriverService';
|
||||
import { DriverController } from './DriverController';
|
||||
import { DriverProviders } from './DriverProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
|
||||
controllers: [DriverController],
|
||||
providers: [DriverService, ...DriverProviders],
|
||||
exports: [DriverService],
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { LeagueService } from './LeagueService';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueProviders } from './LeagueProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule],
|
||||
imports: [RacingPersistenceModule],
|
||||
controllers: [LeagueController],
|
||||
providers: LeagueProviders,
|
||||
exports: [LeagueService],
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { ProtestsController } from './ProtestsController';
|
||||
import { ProtestsService } from './ProtestsService';
|
||||
import { ProtestsProviders } from './ProtestsProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule],
|
||||
imports: [RacingPersistenceModule],
|
||||
providers: [ProtestsService, ...ProtestsProviders],
|
||||
controllers: [ProtestsController],
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { RaceService } from './RaceService';
|
||||
import { RaceController } from './RaceController';
|
||||
import { RaceProviders } from './RaceProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule],
|
||||
imports: [RacingPersistenceModule],
|
||||
controllers: [RaceController],
|
||||
providers: [RaceService, ...RaceProviders],
|
||||
exports: [RaceService],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { AuthModule } from '../auth/AuthModule';
|
||||
import { PolicyModule } from '../policy/PolicyModule';
|
||||
import { SponsorService } from './SponsorService';
|
||||
@@ -7,7 +7,7 @@ import { SponsorController } from './SponsorController';
|
||||
import { SponsorProviders } from './SponsorProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule, AuthModule, PolicyModule],
|
||||
imports: [RacingPersistenceModule, AuthModule, PolicyModule],
|
||||
controllers: [SponsorController],
|
||||
providers: SponsorProviders,
|
||||
exports: [SponsorService],
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryRacingPersistenceModule } from '../../persistence/inmemory/InMemoryRacingPersistenceModule';
|
||||
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
|
||||
import { TeamService } from './TeamService';
|
||||
import { TeamController } from './TeamController';
|
||||
import { TeamProviders } from './TeamProviders';
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryRacingPersistenceModule],
|
||||
imports: [RacingPersistenceModule],
|
||||
controllers: [TeamController],
|
||||
providers: [TeamService, ...TeamProviders],
|
||||
exports: [TeamService],
|
||||
|
||||
Reference in New Issue
Block a user