mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:29:13 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
InvalidLocalScheduleError,
|
||||
assertLocalSchedulePageSize,
|
||||
resolveLocalScheduleDecision,
|
||||
} = require('../dist/scheduler/localScheduler');
|
||||
|
||||
const MINUTE = 60_000;
|
||||
|
||||
function nextMinute(schedule, afterMs) {
|
||||
if (schedule.expression !== '* * * * *' || schedule.timezone !== 'UTC') {
|
||||
throw new Error('unsupported test schedule');
|
||||
}
|
||||
return Math.floor(afterMs / MINUTE + 1) * MINUTE;
|
||||
}
|
||||
|
||||
function candidate(overrides = {}) {
|
||||
return {
|
||||
projectId: 'default',
|
||||
triggerId: 'trigger-1',
|
||||
triggerRevision: 1,
|
||||
triggerContentDigest: 'a'.repeat(64),
|
||||
triggerUpdatedAtMs: 1,
|
||||
taskId: 'task-1',
|
||||
taskRevision: 1,
|
||||
taskContentDigest: 'b'.repeat(64),
|
||||
expression: '* * * * *',
|
||||
timezone: 'UTC',
|
||||
misfirePolicy: 'skip',
|
||||
stateVersion: 0,
|
||||
nextFireAtMs: MINUTE,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('admits one on-time occurrence and advances beyond the observation', () => {
|
||||
assert.deepEqual(
|
||||
resolveLocalScheduleDecision(
|
||||
candidate(),
|
||||
MINUTE + 1_000,
|
||||
5_000,
|
||||
nextMinute,
|
||||
),
|
||||
{
|
||||
candidate: candidate(),
|
||||
observedAtMs: MINUTE + 1_000,
|
||||
scheduledForMs: MINUTE,
|
||||
nextFireAtMs: 2 * MINUTE,
|
||||
disposition: 'admit',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('applies skip and fire-once misfire without replaying a backlog', () => {
|
||||
const observedAtMs = 10 * MINUTE + 30_000;
|
||||
const skipped = resolveLocalScheduleDecision(
|
||||
candidate(),
|
||||
observedAtMs,
|
||||
5_000,
|
||||
nextMinute,
|
||||
);
|
||||
assert.equal(skipped.disposition, 'skip');
|
||||
assert.equal(skipped.nextFireAtMs, 11 * MINUTE);
|
||||
|
||||
const admitted = resolveLocalScheduleDecision(
|
||||
candidate({ misfirePolicy: 'fire_once' }),
|
||||
observedAtMs,
|
||||
5_000,
|
||||
nextMinute,
|
||||
);
|
||||
assert.equal(admitted.disposition, 'admit');
|
||||
assert.equal(admitted.scheduledForMs, MINUTE);
|
||||
assert.equal(admitted.nextFireAtMs, 11 * MINUTE);
|
||||
});
|
||||
|
||||
test('initializes migrated state without inventing a due occurrence', () => {
|
||||
const decision = resolveLocalScheduleDecision(
|
||||
candidate({ triggerUpdatedAtMs: 30_000, nextFireAtMs: null }),
|
||||
40_000,
|
||||
5_000,
|
||||
nextMinute,
|
||||
);
|
||||
assert.deepEqual(
|
||||
{
|
||||
disposition: decision.disposition,
|
||||
nextFireAtMs: decision.nextFireAtMs,
|
||||
scheduledForMs: decision.scheduledForMs,
|
||||
},
|
||||
{
|
||||
disposition: 'initialize',
|
||||
nextFireAtMs: MINUTE,
|
||||
scheduledForMs: undefined,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects widened candidates, invalid cron and unbounded pages', () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveLocalScheduleDecision(
|
||||
{ ...candidate(), extra: true },
|
||||
MINUTE,
|
||||
0,
|
||||
nextMinute,
|
||||
),
|
||||
InvalidLocalScheduleError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveLocalScheduleDecision(
|
||||
candidate({ expression: 'invalid cron' }),
|
||||
MINUTE,
|
||||
0,
|
||||
nextMinute,
|
||||
),
|
||||
InvalidLocalScheduleError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveLocalScheduleDecision(
|
||||
candidate(),
|
||||
MINUTE,
|
||||
0,
|
||||
(_schedule, afterMs) => afterMs,
|
||||
),
|
||||
InvalidLocalScheduleError,
|
||||
);
|
||||
assert.throws(() => assertLocalSchedulePageSize(257), RangeError);
|
||||
});
|
||||
Reference in New Issue
Block a user