mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
test: cover the pause-projection guard
I previously shipped this guard as defence-in-depth and said it could not be tested. That was wrong: the INTERLEAVING needs two concurrent writers, but the STATE it produces does not. A row still active while its own occurrence's card is already settled is constructible directly, and that is precisely the input the guard exists for. Both directions are covered — the settled card is not walked back to a pause, and an unsettled one still projects normally — and the first fails without the guard.
This commit is contained in:
parent
7ab5268fab
commit
4fa1f74eda
1 changed files with 57 additions and 0 deletions
|
|
@ -1102,6 +1102,63 @@ describe('card projection fences (regressions in the ordered projection)', () =>
|
|||
* projectLastRun keys off — so the NEXT projection saw an absent marker and let an
|
||||
* older occurrence's result overwrite the newer skip.
|
||||
*/
|
||||
/**
|
||||
* The pause wins its row transition and THEN projects, so a resume that terminalizes
|
||||
* in between leaves exactly this state: row still active, card already settled for the
|
||||
* SAME occurrence. Occurrence ordering alone permits the pause (equal `scheduledFor`),
|
||||
* which would pin the card to "Needs approval" for a run that had already finished —
|
||||
* and nothing corrects it until the next occurrence runs.
|
||||
*
|
||||
* The interleaving needs two concurrent writers, but the state it produces does not:
|
||||
* constructing it directly tests the guard on exactly the input it exists for.
|
||||
*/
|
||||
it('refuses to walk an occurrence card back from a settled status to a pause', async () => {
|
||||
const schedule = await methods.createSchedule(scheduleData());
|
||||
const scheduledFor = new Date('2026-07-26T10:00:00.000Z');
|
||||
await methods.insertScheduleRun(runData(schedule, { scheduledFor }));
|
||||
// The resumed generation already settled this occurrence's card.
|
||||
await mongoose.models.Schedule.updateOne(
|
||||
{ id: schedule.id },
|
||||
{
|
||||
$set: {
|
||||
lastRun: {
|
||||
status: 'success',
|
||||
firedAt: new Date(),
|
||||
scheduledFor,
|
||||
conversationId: 'convo-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// The in-flight pause now lands for that same occurrence.
|
||||
await methods.recordRunOutcome({
|
||||
scheduleId: schedule.id,
|
||||
scheduledFor,
|
||||
status: 'requires_action',
|
||||
conversationId: 'convo-1',
|
||||
autoDisableAfterFailures: 3,
|
||||
});
|
||||
|
||||
expect((await getSchedule(schedule.id)).lastRun?.status).toBe('success');
|
||||
});
|
||||
|
||||
it('still projects a pause when the occurrence card is not yet settled', async () => {
|
||||
const schedule = await methods.createSchedule(scheduleData());
|
||||
const scheduledFor = new Date('2026-07-26T10:00:00.000Z');
|
||||
await methods.insertScheduleRun(runData(schedule, { scheduledFor }));
|
||||
|
||||
await methods.recordRunOutcome({
|
||||
scheduleId: schedule.id,
|
||||
scheduledFor,
|
||||
status: 'requires_action',
|
||||
conversationId: 'convo-1',
|
||||
autoDisableAfterFailures: 3,
|
||||
});
|
||||
|
||||
expect((await getSchedule(schedule.id)).lastRun?.status).toBe('requires_action');
|
||||
});
|
||||
|
||||
it('keeps the occurrence marker when recording a skip', async () => {
|
||||
const schedule = await methods.createSchedule(scheduleData());
|
||||
const older = new Date('2026-07-26T10:00:00.000Z');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue