r028: recover stuck tool call slots

This commit is contained in:
Mikei386 committed 2026-08-31 13:05:26 +02:00
1 parent 5d2550668a
commit 1c41175a86
11 files changed
+266 -48

No files matched your search

+32
View File
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
import { parseConfig, SAFE_DEFAULT_TOOLS } from "./auth";
import { installCommunityApp, runReadOnlyCommand, sanitizeLogOutput } from "./helpers";
import { getToolRisk, toolByName } from "./tools";
import { ToolCallGate, withHardTimeout } from "./concurrency";
describe("secure tool configuration", () => {
test("new and incomplete configs use the read-only baseline", () => {
@@ -117,6 +118,37 @@ describe("direct Unraid terminal", () => {
});
});
describe("tool-call concurrency recovery", () => {
test("stale leases heal without corrupting newer leases", () => {
const gate = new ToolCallGate(1);
const first = gate.acquire("first", 10, 100);
expect(first).not.toBeNull();
expect(gate.acquire("blocked", 10, 105)).toBeNull();
const second = gate.acquire("second", 100, 111);
expect(second).not.toBeNull();
gate.release(first!.id); // late release must not affect the newer lease
expect(gate.snapshot(112).active).toBe(1);
gate.release(second!.id);
expect(gate.snapshot(113).active).toBe(0);
});
test("hard timeout rejects work that never settles", async () => {
const never = new Promise<string>(() => {});
await expect(withHardTimeout(never, 20, "stuck test")).rejects.toThrow(
"exceeded hard timeout",
);
});
test("shell timeout rejects even when a descendant keeps pipes open", async () => {
const tool = toolByName("unraid_system_shell");
expect(tool).toBeDefined();
await expect(
tool!.handler({ command: "sleep 2 &", timeout_seconds: 1 }),
).rejects.toThrow("timed out after 1 seconds");
});
});
describe("read-only shell", () => {
test("executes an allowlisted program without a shell", async () => {
const result = JSON.parse(await runReadOnlyCommand("ls", ["-ld", "/"], 5));