fix(schedule): close fixed-rate review gaps
This commit is contained in:
@@ -316,6 +316,18 @@ describe('fixed-rate records and durable progression', () => {
|
||||
.toThrow(ScheduleInputError)
|
||||
expect(() => createEveryScheduleRecord(ScheduleId('schedule-every'), 'x', 300, Number.NaN))
|
||||
.toThrow(ScheduleInputError)
|
||||
try {
|
||||
createEveryScheduleRecord(
|
||||
ScheduleId('schedule-every'),
|
||||
'x',
|
||||
300,
|
||||
Date.parse('0000-12-31T23:50:00.000Z'),
|
||||
)
|
||||
throw new Error('expected every lower-bound failure')
|
||||
} catch (error: unknown) {
|
||||
expect(error).toBeInstanceOf(ScheduleInputError)
|
||||
expect((error as ScheduleInputError).code).toBe('time_out_of_range')
|
||||
}
|
||||
})
|
||||
|
||||
it('selects the latest due occurrence and first strictly future anchor point', () => {
|
||||
@@ -412,6 +424,37 @@ describe('fixed-rate records and durable progression', () => {
|
||||
])).toThrow(/at least 300 seconds apart/)
|
||||
})
|
||||
|
||||
it('terminates every record when the shared gate has no four-digit-year admission', () => {
|
||||
const folded = foldScheduleEvents([
|
||||
scheduleEvent(everyCreateData(
|
||||
'schedule-final',
|
||||
'final batch',
|
||||
'9999-12-31T23:55:00.000Z',
|
||||
), 0),
|
||||
scheduleEvent(everyCreateData(
|
||||
'schedule-staggered',
|
||||
'staggered target',
|
||||
'9999-12-31T23:58:00.000Z',
|
||||
), 1),
|
||||
scheduleEvent(createData(
|
||||
'schedule-once',
|
||||
'one shot survives',
|
||||
'9999-12-31T23:59:00.000Z',
|
||||
), 2),
|
||||
scheduleEvent({
|
||||
version: 1,
|
||||
operation: 'dispatch',
|
||||
id: 'schedule-final',
|
||||
acceptedAt: '9999-12-31T23:57:30.000Z',
|
||||
}, 3),
|
||||
])
|
||||
expect(folded).toEqual({
|
||||
active: [expect.objectContaining({ id: 'schedule-once', kind: 'after' })],
|
||||
seenIds: ['schedule-final', 'schedule-staggered', 'schedule-once'],
|
||||
lastRecurringAcceptedAt: '9999-12-31T23:57:30.000Z',
|
||||
})
|
||||
})
|
||||
|
||||
it('derives each recurring receipt and renders one escaped batch payload', () => {
|
||||
const events = [
|
||||
scheduleEvent(everyCreateData(), 0),
|
||||
|
||||
@@ -348,6 +348,37 @@ describe('Schedule timer and admission runtime', () => {
|
||||
await owner.dispose()
|
||||
})
|
||||
|
||||
it('waits for the recurring gate instead of staggered recurring targets', async () => {
|
||||
const test = await harness()
|
||||
appendEvery(test, 'schedule-overdue', 300, Date.parse('2026-08-05T11:53:00.000Z'), 'overdue')
|
||||
const owner = ownerFor(test)
|
||||
owner.start()
|
||||
await settle()
|
||||
expect(test.followed).toHaveLength(1)
|
||||
|
||||
appendEvery(test, 'schedule-staggered', 300, Date.parse('2026-08-05T11:59:00.000Z'), 'staggered')
|
||||
owner.requestDrive()
|
||||
await settle()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(180_000)
|
||||
await settle()
|
||||
const flushesAtFirstDue = test.controls.flushCount
|
||||
expect(test.followed).toHaveLength(1)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(60_000)
|
||||
await settle()
|
||||
expect(test.controls.flushCount).toBe(flushesAtFirstDue)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(60_000)
|
||||
await settle()
|
||||
expect(test.followed).toHaveLength(2)
|
||||
const batch = test.followed[1]?.content[0]
|
||||
if (batch?.type !== 'text') throw new Error('expected recurring batch text')
|
||||
expect(batch.text).toContain('"schedule_id":"schedule-overdue"')
|
||||
expect(batch.text).toContain('"schedule_id":"schedule-staggered"')
|
||||
await owner.dispose()
|
||||
})
|
||||
|
||||
it('rechecks the wall clock after claiming maintenance before queuing', async () => {
|
||||
const test = await harness()
|
||||
appendAfter(test, 'schedule-1', 1, Date.now() - 1_000)
|
||||
|
||||
Reference in New Issue
Block a user