fix(tenders): register poll cron in onApplicationBootstrap (fresh-DB bootstrap)
On a fresh database the DÖE poll cron was never registered: TenderScheduler read the doe-opendata poll config in its onModuleInit, which raced ahead of TendersModule.onModuleInit seeding that config. The scheduler saw the config absent → skipped registering the single global cron that drives pollDueSources (DÖE + RSS + email-alert) → the platform ingested NOTHING until a second restart. Observed live on a fresh prod DB (0 tenders, 'doe-opendata config inactive — cron job not registered', lastIngestedDay null despite isActive=true). Move the scheduler to onApplicationBootstrap, which runs after every module's onModuleInit, so the seed is guaranteed complete before the config is read. Adds a regression test asserting the lifecycle choice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -87,7 +87,7 @@ describe('TenderSchedulerService — poll-once-fan-out-many (INGEST-06, Success
|
||||
|
||||
// Platform boot: the scheduler initializes exactly once, independent of
|
||||
// any tenant — this is the one and only cron-job registration.
|
||||
await scheduler.onModuleInit();
|
||||
await scheduler.onApplicationBootstrap();
|
||||
expect(addCronJob).toHaveBeenCalledTimes(1);
|
||||
expect(addCronJob.mock.calls[0][0]).toBe('tender-doe-poll');
|
||||
|
||||
@@ -139,4 +139,22 @@ describe('TenderSchedulerService — poll-once-fan-out-many (INGEST-06, Success
|
||||
expect(source).not.toMatch(/findFirst/);
|
||||
expect(source).toMatch(/findUnique/);
|
||||
});
|
||||
|
||||
it('registers the cron via onApplicationBootstrap (NOT onModuleInit) so the poll-config seed runs first on a fresh DB (#prod-bootstrap)', () => {
|
||||
// Regression guard: TendersModule.onModuleInit seeds the doe-opendata
|
||||
// poll config. onModuleInit hooks run in an unspecified order relative to
|
||||
// one another, so on a FRESH database the scheduler must NOT read the
|
||||
// config in its own onModuleInit — it would see the config absent and
|
||||
// never register the single global cron that drives DÖE + RSS + email-alert
|
||||
// ingestion (observed live on a fresh prod DB: 0 tenders, cron unregistered).
|
||||
// onApplicationBootstrap runs after every module's onModuleInit → seed done.
|
||||
const source = readFileSync(join(__dirname, 'tender-scheduler.service.ts'), 'utf8');
|
||||
expect(source).toMatch(/implements OnApplicationBootstrap/);
|
||||
expect(source).toMatch(/async onApplicationBootstrap\(/);
|
||||
expect(source).not.toMatch(/async onModuleInit\(/);
|
||||
// The lifecycle method actually exists on the instance.
|
||||
expect(typeof TenderSchedulerService.prototype.onApplicationBootstrap).toBe(
|
||||
'function',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user