153 lines
5.5 KiB
TypeScript
153 lines
5.5 KiB
TypeScript
import type { ApprovalRequest } from "@fusion/core";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
SandboxProvisioningPendingError,
|
|
requireSandboxProvisioningApproval,
|
|
} from "../provisioning-gate.js";
|
|
|
|
function makeApproval(id: string, status: ApprovalRequest["status"]): ApprovalRequest {
|
|
return {
|
|
id,
|
|
status,
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
targetAction: {
|
|
category: "sandbox_provisioning",
|
|
action: "provision",
|
|
summary: "Provision sandbox backend",
|
|
resourceType: "command",
|
|
resourceId: "install-bubblewrap",
|
|
context: { backendId: "bubblewrap", operation: "install-bubblewrap", params: {} },
|
|
},
|
|
taskId: "FN-4641",
|
|
runId: "run-1",
|
|
requestedAt: new Date().toISOString(),
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
}
|
|
|
|
describe("requireSandboxProvisioningApproval", () => {
|
|
it("allows auto-approved backend", async () => {
|
|
const createApprovalRequest = vi.fn();
|
|
const result = await requireSandboxProvisioningApproval({
|
|
backendId: "native",
|
|
operation: "prepare",
|
|
description: "Prepare native backend",
|
|
context: {
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest,
|
|
},
|
|
});
|
|
expect(result).toEqual({ outcome: "allow" });
|
|
expect(createApprovalRequest).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("creates pending request and pauses for approval in default mode", async () => {
|
|
const createApprovalRequest = vi.fn(async () => makeApproval("apr-1", "pending"));
|
|
const pauseForApproval = vi.fn(async () => undefined);
|
|
|
|
await expect(
|
|
requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
taskId: "FN-4641",
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest,
|
|
pauseForApproval,
|
|
},
|
|
}),
|
|
).rejects.toBeInstanceOf(SandboxProvisioningPendingError);
|
|
|
|
expect(createApprovalRequest).toHaveBeenCalledTimes(1);
|
|
expect(pauseForApproval).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("returns execute-once-then-complete after approval exists", async () => {
|
|
const result = await requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
taskId: "FN-4641",
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest: vi.fn(async () => null),
|
|
findApprovalByDedupeKey: vi.fn(async () => ({ id: "apr-1", status: "approved" as const })),
|
|
},
|
|
});
|
|
|
|
expect(result).toEqual({ outcome: "execute-once-then-complete", approvalRequestId: "apr-1" });
|
|
});
|
|
|
|
it("throws block when prior approval was denied", async () => {
|
|
await expect(
|
|
requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest: vi.fn(async () => null),
|
|
findApprovalByDedupeKey: vi.fn(async () => ({ id: "apr-1", status: "denied" as const })),
|
|
},
|
|
}),
|
|
).rejects.toBeInstanceOf(SandboxProvisioningPendingError);
|
|
});
|
|
|
|
it("allows never mode without creating approval", async () => {
|
|
const createApprovalRequest = vi.fn(async () => makeApproval("apr-1", "pending"));
|
|
const result = await requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: { sandboxProvisioning: { approvalMode: "never" } },
|
|
createApprovalRequest,
|
|
},
|
|
});
|
|
expect(result).toEqual({ outcome: "allow" });
|
|
expect(createApprovalRequest).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("produces stable dedupe key for identical inputs", async () => {
|
|
const createApprovalRequest = vi.fn(async () => makeApproval("apr-1", "pending"));
|
|
|
|
const first = requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
taskId: "FN-4641",
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest,
|
|
},
|
|
}).catch((error) => error as SandboxProvisioningPendingError);
|
|
|
|
const second = requireSandboxProvisioningApproval({
|
|
backendId: "bubblewrap",
|
|
operation: "install",
|
|
description: "Install bubblewrap",
|
|
context: {
|
|
taskId: "FN-4641",
|
|
requester: { actorId: "agent-1", actorType: "agent", actorName: "Executor" },
|
|
settings: undefined,
|
|
createApprovalRequest,
|
|
},
|
|
}).catch((error) => error as SandboxProvisioningPendingError);
|
|
|
|
const [firstError, secondError] = await Promise.all([first, second]);
|
|
expect(firstError).toBeInstanceOf(SandboxProvisioningPendingError);
|
|
expect(secondError).toBeInstanceOf(SandboxProvisioningPendingError);
|
|
expect((firstError as SandboxProvisioningPendingError).dedupeKey).toBe(
|
|
(secondError as SandboxProvisioningPendingError).dedupeKey,
|
|
);
|
|
});
|
|
});
|