import { test, expect } from "bun:test"; import { createRecycleMonitor } from "../src/recycle.ts"; /** Fresh monitor with a small threshold so tests stay readable. */ function monitor(overrides: Partial[0]> = {}) { const recycled: string[] = []; const control = createRecycleMonitor({ threshold: 3, idleMs: 1000, onRecycle: (reason) => recycled.push(reason), ...overrides, }); return { control, recycled }; } test("stays quiet below the rebuild threshold", () => { const { control, recycled } = monitor(); control.recordRebuild(); control.recordRebuild(); control.tick(10_000); expect(recycled).toEqual([]); }); test("recycles once rebuilds pass the threshold and the server goes idle", () => { const { control, recycled } = monitor(); for (let i = 0; i < 3; i++) control.recordRebuild(); control.recordRequest(0); control.tick(1_500); expect(recycled.length).toBe(1); }); test("waits for the idle gap rather than cutting off active work", () => { // Recycling mid-request would drop it. The gap is the whole point. const { control, recycled } = monitor(); for (let i = 0; i < 3; i++) control.recordRebuild(); control.recordRequest(0); control.tick(500); expect(recycled).toEqual([]); control.recordRequest(900); control.tick(1_400); expect(recycled).toEqual([]); control.tick(2_000); expect(recycled.length).toBe(1); }); test("recycles only once even if it keeps being ticked", () => { const { control, recycled } = monitor(); for (let i = 0; i < 5; i++) control.recordRebuild(); control.recordRequest(0); control.tick(5_000); control.tick(6_000); control.tick(7_000); expect(recycled.length).toBe(1); }); test("a server that never served a request can still recycle", () => { const { control, recycled } = monitor(); for (let i = 0; i < 3; i++) control.recordRebuild(); control.tick(9_999); expect(recycled.length).toBe(1); }); test("reports how many rebuilds are being retained", () => { const { control } = monitor(); control.recordRebuild(); control.recordRebuild(); expect(control.retained()).toBe(2); });