// Tests for scheduler cooldown logic import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; // ── minimal cooldown state machine ─────────────────────────────────────────── // Mirrors the cooldown guard in OcScheduler.poll() so we can test it in isolation. function makeCooldownGuard(cooldownMs: number) { let lastPushAt = 0; return { recordPush() { lastPushAt = Date.now(); }, isInCooldown(): boolean { if (lastPushAt === 0) return false; return Date.now() - lastPushAt < cooldownMs; }, remainingMs(): number { if (lastPushAt === 0) return 0; const elapsed = Date.now() - lastPushAt; return Math.max(0, cooldownMs - elapsed); }, }; } // ── tests ───────────────────────────────────────────────────────────────────── describe('scheduler cooldown logic', () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('not in cooldown before any push', () => { const guard = makeCooldownGuard(60_000); expect(guard.isInCooldown()).toBe(false); }); it('enters cooldown immediately after a push', () => { const guard = makeCooldownGuard(60_000); guard.recordPush(); expect(guard.isInCooldown()).toBe(true); }); it('remains in cooldown within the window', () => { const guard = makeCooldownGuard(60_000); guard.recordPush(); vi.advanceTimersByTime(59_999); // just under 60 s expect(guard.isInCooldown()).toBe(true); expect(guard.remainingMs()).toBeGreaterThan(0); }); it('exits cooldown after the window expires', () => { const guard = makeCooldownGuard(60_000); guard.recordPush(); vi.advanceTimersByTime(60_001); // just over 60 s expect(guard.isInCooldown()).toBe(false); expect(guard.remainingMs()).toBe(0); }); it('resets cooldown on a second push', () => { const guard = makeCooldownGuard(60_000); guard.recordPush(); vi.advanceTimersByTime(30_000); // 30 s into first cooldown guard.recordPush(); // second push resets the clock vi.advanceTimersByTime(30_000); // only 30 s since second push expect(guard.isInCooldown()).toBe(true); // still cooling down }); });