Преглед изворни кода

test(scheduler): add cancellation tests for session.deleted and session.error idle timers

Michael Henke пре 1 месец
родитељ
комит
e2ccc79b95
1 измењених фајлова са 136 додато и 0 уклоњено
  1. 136 0
      src/hooks/task-session-manager/index.test.ts

+ 136 - 0
src/hooks/task-session-manager/index.test.ts

@@ -1259,6 +1259,142 @@ describe('task-session-manager hook', () => {
     });
   });
 
+  test('deleted session cancels pending idle reconciliation', async () => {
+    const board = new BackgroundJobBoard();
+    const { hook } = createHook({ backgroundJobBoard: board });
+
+    setupCompletedJob(board);
+
+    const messages = createMessages('parent-1', 'continue');
+    await hook['experimental.chat.messages.transform']({}, messages);
+
+    // Fire idle event (starts timer)
+    await hook.event({
+      event: {
+        type: 'session.status',
+        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
+      },
+    });
+
+    // Fire session.deleted event — should cancel reconciliation
+    await hook.event({
+      event: {
+        type: 'session.deleted',
+        properties: { sessionID: 'parent-1' },
+      },
+    });
+
+    await flushIdleReconcileDelay();
+
+    // Job should NOT be reconciled (timer was cancelled)
+    expect(board.get('child-1')).toMatchObject({
+      state: 'completed',
+      terminalUnreconciled: true,
+    });
+  });
+
+  test('session.error cancels pending idle reconciliation', async () => {
+    const board = new BackgroundJobBoard();
+    const { hook } = createHook({ backgroundJobBoard: board });
+
+    setupCompletedJob(board);
+
+    const messages = createMessages('parent-1', 'continue');
+    await hook['experimental.chat.messages.transform']({}, messages);
+
+    // Fire idle event (starts timer)
+    await hook.event({
+      event: {
+        type: 'session.status',
+        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
+      },
+    });
+
+    // Fire session.error event — should cancel reconciliation
+    await hook.event({
+      event: {
+        type: 'session.error',
+        properties: { sessionID: 'parent-1', error: { message: 'test error', type: 'generic' } },
+      },
+    });
+
+    await flushIdleReconcileDelay();
+
+    // Job should NOT be reconciled (timer was cancelled)
+    expect(board.get('child-1')).toMatchObject({
+      state: 'completed',
+      terminalUnreconciled: true,
+    });
+  });
+
+  test('deleted session cancels pending idle reconciliation', async () => {
+    const board = new BackgroundJobBoard();
+    const { hook } = createHook({ backgroundJobBoard: board });
+
+    setupCompletedJob(board);
+
+    const messages = createMessages('parent-1', 'continue');
+    await hook['experimental.chat.messages.transform']({}, messages);
+
+    // Fire idle event (starts timer)
+    await hook.event({
+      event: {
+        type: 'session.status',
+        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
+      },
+    });
+
+    // session.deleted before timer fires — should cancel reconciliation
+    await hook.event({
+      event: {
+        type: 'session.deleted',
+        properties: { sessionID: 'parent-1' },
+      },
+    });
+
+    await flushIdleReconcileDelay();
+
+    // Job should be gone (session.deleted clears the board for this parent)
+    expect(board.get('child-1')).toBeUndefined();
+  });
+
+  test('session.error cancels pending idle reconciliation', async () => {
+    const board = new BackgroundJobBoard();
+    const { hook } = createHook({ backgroundJobBoard: board });
+
+    setupCompletedJob(board);
+
+    const messages = createMessages('parent-1', 'continue');
+    await hook['experimental.chat.messages.transform']({}, messages);
+
+    // Fire idle event (starts timer)
+    await hook.event({
+      event: {
+        type: 'session.status',
+        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
+      },
+    });
+
+    // session.error before timer fires — should cancel reconciliation
+    await hook.event({
+      event: {
+        type: 'session.error',
+        properties: {
+          sessionID: 'parent-1',
+          error: { name: 'generic' },
+        },
+      },
+    });
+
+    await flushIdleReconcileDelay();
+
+    // Job should NOT be reconciled (timer was cancelled)
+    expect(board.get('child-1')).toMatchObject({
+      state: 'completed',
+      terminalUnreconciled: true,
+    });
+  });
+
   test('completion arriving after idle reconciliation delay is still dropped', async () => {
     const board = new BackgroundJobBoard();
     const { hook } = createHook({ backgroundJobBoard: board });