Fix double ScheduleModule.forRoot() registration doubling every cron/interval job (v0.21.7)
CI / build + unit (pull_request) Failing after 22s
CI / build + unit (pull_request) Failing after 22s
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## v0.21.7 — 2026-09-27 06:50
|
||||
|
||||
- Fixed double registration of scheduled jobs: `ScheduleModule.forRoot()` was called from both `WeatherModule` and `BatteryAlertModule`, so Nest created two ScheduleExplorers, each scanning all providers — every @Cron/@Interval job (hourly weather broadcast, per-minute battery check) fired twice. Removed the call from `battery.module.ts`; the single registration in `weather.module.ts` remains.
|
||||
- Tests: 174 → 175 — new regression spec (`src/schedule-singleton.spec.ts`) asserts `ScheduleModule.forRoot(` appears exactly once across all `src/**/*.module.ts`.
|
||||
|
||||
## v0.21.6 — 2026-09-26 10:37
|
||||
|
||||
- New `#help` command (owner request): replies with a short list of all available commands as ONE multi-line message (single packet, not one spaced packet per line) — `#ping`, the `#rss help/add/remove/get/sources/tags` family. Registered in the CommandService constructor next to `#ping`.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mesh-bot",
|
||||
"version": "0.21.6",
|
||||
"version": "0.21.7",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { MeshModule } from '../mesh/mesh.module.js';
|
||||
import { BatteryAlertService } from './battery.service.js';
|
||||
|
||||
@Module({
|
||||
imports: [ScheduleModule.forRoot(), MeshModule],
|
||||
imports: [MeshModule],
|
||||
providers: [BatteryAlertService],
|
||||
exports: [BatteryAlertService],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const srcDir = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
function moduleFiles(dir: string): string[] {
|
||||
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) =>
|
||||
entry.isDirectory()
|
||||
? moduleFiles(join(dir, entry.name))
|
||||
: entry.name.endsWith('.module.ts')
|
||||
? [join(dir, entry.name)]
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
describe('ScheduleModule.forRoot single registration', () => {
|
||||
// Regression guard (v0.21.7): each forRoot() call creates its own ScheduleExplorer that scans all providers, so a second call made every @Cron/@Interval job fire twice.
|
||||
it('appears exactly once across all src/**/*.module.ts', () => {
|
||||
const hits = moduleFiles(srcDir).flatMap((file) =>
|
||||
(
|
||||
readFileSync(file, 'utf8').match(/ScheduleModule\.forRoot\(/g) ?? []
|
||||
).map(() => file.slice(srcDir.length)),
|
||||
);
|
||||
expect(hits).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user