test(FN-4954): cover pool double-lease throw and fresh fallback
Fusion-Task-Id: FN-4954 Fusion-Task-Lineage: b3dd2d2f-3aa3-48f1-8a56-56e66518d139
This commit is contained in:
committed by
gsxdsm
parent
c6453ca956
commit
b8514b658e
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { promisify } from "node:util";
|
||||
import { acquireTaskWorktree } from "../worktree-acquisition.js";
|
||||
import { classifyTaskWorktree } from "../worktree-pool.js";
|
||||
import { classifyTaskWorktree, PoolDoubleLeaseError } from "../worktree-pool.js";
|
||||
|
||||
vi.mock("../worktree-pool.js", async () => {
|
||||
const actual = await vi.importActual<any>("../worktree-pool.js");
|
||||
@@ -146,6 +146,29 @@ describe("acquireTaskWorktree", () => {
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-1", { worktree: null, branch: null, sessionFile: null });
|
||||
});
|
||||
|
||||
it("falls through to fresh creation when pool acquire throws PoolDoubleLeaseError", async () => {
|
||||
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
|
||||
const result = await acquireTaskWorktree({
|
||||
task,
|
||||
rootDir: process.cwd(),
|
||||
store,
|
||||
settings: { recycleWorktrees: true } as any,
|
||||
pool: {
|
||||
acquire: () => {
|
||||
throw new PoolDoubleLeaseError("/tmp/pooled", "FN-OTHER", "FN-1", "acquire");
|
||||
},
|
||||
prepareForTask: vi.fn(),
|
||||
release: vi.fn(),
|
||||
} as any,
|
||||
createWorktree,
|
||||
logger: { log: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
});
|
||||
|
||||
expect(result.source).toBe("fresh");
|
||||
expect(createWorktree).toHaveBeenCalled();
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-1", expect.stringContaining("Pool double-lease guard triggered"), undefined, undefined);
|
||||
});
|
||||
|
||||
it("creates fresh when pool disabled", async () => {
|
||||
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
|
||||
const result = await acquireTaskWorktree({
|
||||
|
||||
@@ -8,7 +8,7 @@ vi.mock("node:fs", () => ({
|
||||
realpathSync: vi.fn((path: string) => path),
|
||||
}));
|
||||
|
||||
import { WorktreePool } from "../worktree-pool.js";
|
||||
import { PoolDoubleLeaseError, WorktreePool } from "../worktree-pool.js";
|
||||
|
||||
// FN-4954 deterministic race backstop.
|
||||
describe("WorktreePool double-lease guard", () => {
|
||||
@@ -36,6 +36,31 @@ describe("WorktreePool double-lease guard", () => {
|
||||
expect(violations).toEqual([{ phase: "rehydrate", existingHolder: "FN-A" }]);
|
||||
});
|
||||
|
||||
it("throws PoolDoubleLeaseError when corrupted idle state tries to re-lease a leased path", () => {
|
||||
const violations: Array<{ phase: string; requestingTaskId: string; existingHolder: string }> = [];
|
||||
pool.setInvariantViolationHandler((violation) => {
|
||||
violations.push({
|
||||
phase: violation.phase,
|
||||
requestingTaskId: violation.requestingTaskId,
|
||||
existingHolder: violation.existingHolder,
|
||||
});
|
||||
});
|
||||
|
||||
pool.release("/tmp/wt-race");
|
||||
expect(pool.acquire("FN-A")).toBe("/tmp/wt-race");
|
||||
(pool as any).idle.add("/tmp/wt-race");
|
||||
|
||||
expect(() => pool.acquire("FN-B")).toThrow(PoolDoubleLeaseError);
|
||||
expect(violations).toEqual([{ phase: "acquire", requestingTaskId: "FN-B", existingHolder: "FN-A" }]);
|
||||
});
|
||||
|
||||
it("does not throw for same-task re-entry when no idle path exists", () => {
|
||||
pool.release("/tmp/wt-race");
|
||||
expect(pool.acquire("FN-A")).toBe("/tmp/wt-race");
|
||||
expect(() => pool.acquire("FN-A")).not.toThrow();
|
||||
expect(pool.acquire("FN-A")).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps release best-effort when releasing task differs", () => {
|
||||
const violations: Array<{ phase: string; requestingTaskId: string }> = [];
|
||||
pool.setInvariantViolationHandler((violation) => violations.push({ phase: violation.phase, requestingTaskId: violation.requestingTaskId }));
|
||||
|
||||
Reference in New Issue
Block a user