mirror of
https://github.com/whyour/qinglong.git
synced 2026-10-01 05:13:50 +08:00
refactor: migrate cron scheduling to node-cron (#3079)
* refactor: migrate cron scheduling to node-cron * feat: support annually midnight and minutely cron macros * fix: harden cron recovery and system scheduler compatibility
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { createCronJob } = require('../../back/shared/cronScheduler');
|
||||
const { isValidCronSchedule } = require('../../back/shared/cronSchedule');
|
||||
|
||||
// node-cron caches timezone formatters; model each deployment in a fresh process.
|
||||
if (!process.env.QL_MACRO_TEST_TZ) {
|
||||
for (const tz of ['UTC', 'Asia/Shanghai']) {
|
||||
test(`macro execution in ${tz}`, () => {
|
||||
const result = require('node:child_process').spawnSync(process.execPath,
|
||||
['-r', 'ts-node/register/transpile-only', '--test', __filename],
|
||||
{ env: { ...process.env, TZ: tz, QL_MACRO_TEST_TZ: tz }, encoding: 'utf8' });
|
||||
assert.equal(result.status, 0, result.stdout + result.stderr);
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const tz of process.env.QL_MACRO_TEST_TZ ? [process.env.QL_MACRO_TEST_TZ] : []) {
|
||||
for (const [macro, start, next] of [
|
||||
['@annually', [2026, 11, 31, 23, 59, 59], [[2027, 0, 1, 0, 0, 0], [2028, 0, 1, 0, 0, 0]]],
|
||||
['@midnight', [2026, 8, 27, 23, 59, 59], [[2026, 8, 28, 0, 0, 0], [2026, 8, 29, 0, 0, 0]]],
|
||||
['@minutely', [2026, 8, 27, 12, 34, 59], [[2026, 8, 27, 12, 35, 0], [2026, 8, 27, 12, 36, 0]]],
|
||||
]) {
|
||||
test(`${macro} fires at the expected local times in ${tz}`, async (t) => {
|
||||
const previousTZ = process.env.TZ;
|
||||
process.env.TZ = tz;
|
||||
t.after(() => { if (previousTZ === undefined) delete process.env.TZ; else process.env.TZ = previousTZ; });
|
||||
t.mock.timers.enable({ apis: ['Date', 'setTimeout', 'setInterval'], now: new Date(...start) });
|
||||
const expected = next.map((parts) => new Date(...parts).getTime());
|
||||
const actual = [];
|
||||
assert.equal(isValidCronSchedule(` ${macro} `), true);
|
||||
const job = createCronJob(macro, (date) => actual.push(date.getTime()), {
|
||||
name: macro, logger: { warn() {}, error() {} },
|
||||
});
|
||||
t.after(() => job.cancel());
|
||||
for (const time of expected) {
|
||||
t.mock.timers.tick(time - Date.now() - 1);
|
||||
await new Promise(setImmediate);
|
||||
assert.equal(actual.includes(time), false, 'must not fire early');
|
||||
t.mock.timers.tick(1);
|
||||
await new Promise(setImmediate);
|
||||
}
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test('new aliases do not enable unknown macros or raw parser extensions', () => {
|
||||
for (const schedule of ['@annuallyx', '@midnightx', '@minutelyx', '@secondly', '@reboot', 'H * * * *', '0 /5 * * * *']) {
|
||||
assert.equal(isValidCronSchedule(schedule), false, schedule);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user