101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
/**
|
||
* reconciler-wiring.test.ts
|
||
*
|
||
* A2aTaskReconciler のライフサイクル(start/stop)を fake timers で検証。
|
||
* server.ts への結線は Task 6 の e2e テストで確認するため、ここは reconciler
|
||
* 単体に留める。
|
||
*/
|
||
|
||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||
import { A2aTaskReconciler } from './reconciler.js';
|
||
|
||
afterEach(() => {
|
||
vi.useRealTimers();
|
||
});
|
||
|
||
describe('A2aTaskReconciler lifecycle', () => {
|
||
it('start() triggers an immediate reconcile tick', async () => {
|
||
vi.useFakeTimers();
|
||
|
||
let callCount = 0;
|
||
const fakeRepo = {
|
||
listNonTerminalA2aTasks: () => { callCount++; return []; },
|
||
} as any;
|
||
|
||
const reconciler = new A2aTaskReconciler({ repo: fakeRepo, intervalMs: 1000 });
|
||
reconciler.start();
|
||
|
||
// Drain the microtask queue so the immediate async tick completes.
|
||
await Promise.resolve();
|
||
|
||
expect(callCount).toBeGreaterThanOrEqual(1);
|
||
});
|
||
|
||
it('periodic interval fires after intervalMs', async () => {
|
||
vi.useFakeTimers();
|
||
|
||
let callCount = 0;
|
||
const fakeRepo = {
|
||
listNonTerminalA2aTasks: () => { callCount++; return []; },
|
||
} as any;
|
||
|
||
const reconciler = new A2aTaskReconciler({ repo: fakeRepo, intervalMs: 1000 });
|
||
reconciler.start();
|
||
|
||
// Drain immediate tick.
|
||
await Promise.resolve();
|
||
const afterStart = callCount;
|
||
|
||
// Advance one full interval.
|
||
await vi.advanceTimersByTimeAsync(1000);
|
||
|
||
expect(callCount).toBeGreaterThan(afterStart);
|
||
});
|
||
|
||
it('stop() prevents further ticks after interval advances', async () => {
|
||
vi.useFakeTimers();
|
||
|
||
let callCount = 0;
|
||
const fakeRepo = {
|
||
listNonTerminalA2aTasks: () => { callCount++; return []; },
|
||
} as any;
|
||
|
||
const reconciler = new A2aTaskReconciler({ repo: fakeRepo, intervalMs: 1000 });
|
||
reconciler.start();
|
||
|
||
// Drain immediate tick.
|
||
await Promise.resolve();
|
||
|
||
reconciler.stop();
|
||
const countAfterStop = callCount;
|
||
|
||
// Advance several intervals — the cleared timer must not fire.
|
||
await vi.advanceTimersByTimeAsync(5000);
|
||
|
||
expect(callCount).toBe(countAfterStop);
|
||
});
|
||
|
||
it('double start() does not leak a second interval', async () => {
|
||
vi.useFakeTimers();
|
||
|
||
let callCount = 0;
|
||
const fakeRepo = {
|
||
listNonTerminalA2aTasks: () => { callCount++; return []; },
|
||
} as any;
|
||
|
||
const reconciler = new A2aTaskReconciler({ repo: fakeRepo, intervalMs: 1000 });
|
||
reconciler.start();
|
||
reconciler.start(); // should replace, not add
|
||
|
||
await Promise.resolve();
|
||
const afterDoubleStart = callCount;
|
||
|
||
await vi.advanceTimersByTimeAsync(1000);
|
||
|
||
// Only one interval should fire; if two leaked, callCount would jump by 2.
|
||
expect(callCount).toBe(afterDoubleStart + 1);
|
||
|
||
reconciler.stop();
|
||
});
|
||
});
|