Files
penguin-harness/packages/core/test/background-registry.test.ts
T
Yaowei Zheng 45bfae6e94 Initialize repository with harness code and assets
Initial import of all source code, config, and README assets: the
packages workspace (cli, core, server, web, docs, landing, skills),
build scripts, tooling config, and CI workflows.

Includes the data-layout revision made on this branch: the local data
root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the
installer keeps its binaries in ~/.penguin), and every Agent lives
under <project>/agents/<agent>/ — path helpers, the three
agent-enumeration scans, the system prompt, built-in Skills, tests
and docs all follow the new layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
2026-07-19 14:06:53 +08:00

65 lines
2.1 KiB
TypeScript

/**
* Behavior tests for BackgroundRegistry's idle reaping (a leak safety net).
*/
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { BackgroundRegistry } from "../src/environment/tools/background/index.js";
import type { BackgroundTask } from "../src/environment/tools/background/index.js";
type FakeTask = BackgroundTask & { killed: boolean };
function fakeTask(): FakeTask {
const task: FakeTask = {
lastUsed: 0,
running: true,
killed: false,
kill() {
task.killed = true;
},
killHard() {
task.killed = true;
},
};
return task;
}
describe("BackgroundRegistry idle reaping", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it("reaps sessions idle past the TTL and keeps recently accessed ones", () => {
const registry = new BackgroundRegistry<FakeTask>({ idPrefix: "proc", maxTasks: 4 });
const stale = fakeTask();
const fresh = fakeTask();
const staleId = registry.register(stale);
const freshId = registry.register(fresh);
// After 9 days, one access to fresh refreshes its lastUsed; stale is never accessed.
vi.advanceTimersByTime(9 * 24 * 60 * 60_000);
expect(registry.get(freshId)).toBe(fresh);
// stale, now idle a full 10 days, is reaped by the scheduled sweep and finalized;
// fresh has been idle only 1 day and is kept.
vi.advanceTimersByTime(24 * 60 * 60_000 + 60 * 60_000);
expect(registry.get(staleId)).toBeUndefined();
expect(stale.killed).toBe(true);
expect(registry.get(freshId)).toBe(fresh);
expect(fresh.killed).toBe(false);
registry.dispose();
});
it("stops the reap timer on dispose", () => {
const registry = new BackgroundRegistry<FakeTask>({ idPrefix: "proc", maxTasks: 4 });
const task = fakeTask();
registry.register(task);
registry.dispose();
expect(task.killed).toBe(true);
// After dispose the sweep timer is cleared, so fast-forwarding no longer triggers any reaping logic.
expect(() => vi.advanceTimersByTime(30 * 24 * 60 * 60_000)).not.toThrow();
});
});