61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import { createHeartbeatController } from '../../src/heartbeat-controller';
|
|
|
|
describe('heartbeat controller', () => {
|
|
beforeEach(() => vi.useFakeTimers());
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
test('does not overlap ticks while a heartbeat is in flight', async () => {
|
|
let resolveHeartbeat!: (value: { cancelRequested: boolean }) => void;
|
|
const heartbeat = vi.fn(
|
|
() =>
|
|
new Promise<{ cancelRequested: boolean }>((resolve) => {
|
|
resolveHeartbeat = resolve;
|
|
}),
|
|
);
|
|
const controller = createHeartbeatController({
|
|
intervalMs: 30_000,
|
|
heartbeat,
|
|
onCancel: vi.fn(),
|
|
onError: vi.fn(),
|
|
});
|
|
|
|
controller.start('job-1');
|
|
await vi.advanceTimersByTimeAsync(90_000);
|
|
expect(heartbeat).toHaveBeenCalledOnce();
|
|
|
|
resolveHeartbeat({ cancelRequested: false });
|
|
await Promise.resolve();
|
|
await vi.advanceTimersByTimeAsync(30_000);
|
|
expect(heartbeat).toHaveBeenCalledTimes(2);
|
|
controller.stop('job-1');
|
|
});
|
|
|
|
test('removes the timer before awaiting cancellation teardown', async () => {
|
|
let finishCancel!: () => void;
|
|
const onCancel = vi.fn(
|
|
() =>
|
|
new Promise<void>((resolve) => {
|
|
finishCancel = resolve;
|
|
}),
|
|
);
|
|
const heartbeat = vi.fn().mockResolvedValue({ cancelRequested: true });
|
|
const controller = createHeartbeatController({
|
|
intervalMs: 30_000,
|
|
heartbeat,
|
|
onCancel,
|
|
onError: vi.fn(),
|
|
});
|
|
|
|
controller.start('job-1');
|
|
await vi.advanceTimersByTimeAsync(30_000);
|
|
expect(onCancel).toHaveBeenCalledOnce();
|
|
expect(controller.has('job-1')).toBe(false);
|
|
|
|
await vi.advanceTimersByTimeAsync(90_000);
|
|
expect(heartbeat).toHaveBeenCalledOnce();
|
|
finishCancel();
|
|
});
|
|
});
|