feat(FN-4640): complete Step 2 — add sandbox run auditor domain
Fusion-Task-Id: FN-4640 Fusion-Task-Lineage: 4a91265f-1714-4854-b08d-7ddb06074253
This commit is contained in:
committed by
gsxdsm
parent
ef7eb2f3ea
commit
b28b1949a6
65
packages/engine/src/__tests__/run-audit.sandbox.test.ts
Normal file
65
packages/engine/src/__tests__/run-audit.sandbox.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { RunAuditEventInput, TaskStore } from "@fusion/core";
|
||||
import { createRunAuditor } from "../run-audit.js";
|
||||
|
||||
class AuditStoreStub {
|
||||
events: RunAuditEventInput[] = [];
|
||||
recordRunAuditEvent(event: RunAuditEventInput): void {
|
||||
this.events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
describe("createRunAuditor sandbox domain", () => {
|
||||
it("records sandbox events with metadata", async () => {
|
||||
const store = new AuditStoreStub();
|
||||
const auditor = createRunAuditor(store as unknown as TaskStore, {
|
||||
runId: "run-1",
|
||||
agentId: "agent-1",
|
||||
taskId: "FN-1",
|
||||
phase: "execute",
|
||||
});
|
||||
|
||||
await auditor.sandbox({
|
||||
type: "sandbox:run",
|
||||
target: "native",
|
||||
metadata: { timeoutMs: 12000, exitCode: 0 },
|
||||
});
|
||||
|
||||
expect(store.events).toHaveLength(1);
|
||||
expect(store.events[0]).toMatchObject({
|
||||
runId: "run-1",
|
||||
agentId: "agent-1",
|
||||
taskId: "FN-1",
|
||||
domain: "sandbox",
|
||||
mutationType: "sandbox:run",
|
||||
target: "native",
|
||||
});
|
||||
expect(store.events[0].metadata).toMatchObject({
|
||||
phase: "execute",
|
||||
timeoutMs: 12000,
|
||||
exitCode: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("no-ops when context is null", async () => {
|
||||
const store = new AuditStoreStub();
|
||||
const auditor = createRunAuditor(store as unknown as TaskStore, null);
|
||||
|
||||
await auditor.sandbox({ type: "sandbox:prepare", target: "native" });
|
||||
|
||||
expect(store.events).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("no-ops when store lacks recordRunAuditEvent", async () => {
|
||||
const storeWithoutAudit = {} as TaskStore;
|
||||
const auditor = createRunAuditor(storeWithoutAudit, {
|
||||
runId: "run-2",
|
||||
agentId: "agent-2",
|
||||
taskId: "FN-2",
|
||||
});
|
||||
|
||||
await expect(
|
||||
auditor.sandbox({ type: "sandbox:failure", target: "native", metadata: { errorMessage: "boom" } }),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -140,6 +140,8 @@ export type FilesystemMutationType =
|
||||
| "session:write"
|
||||
| "session:delete";
|
||||
|
||||
export type SandboxMutationType = "sandbox:prepare" | "sandbox:run" | "sandbox:failure" | "sandbox:fallback";
|
||||
|
||||
/** Input for a git-domain audit event. */
|
||||
export interface GitAuditInput {
|
||||
type: GitMutationType;
|
||||
@@ -167,6 +169,15 @@ export interface FilesystemAuditInput {
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Input for a sandbox-domain audit event. */
|
||||
export interface SandboxAuditInput {
|
||||
type: SandboxMutationType;
|
||||
/** Target of the mutation (e.g., backend id). */
|
||||
target: string;
|
||||
/** Optional structured metadata. */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Interface for emitting run-audit events. */
|
||||
export interface RunAuditor {
|
||||
/** Emit a git-domain audit event. No-op if no run context is available. */
|
||||
@@ -175,6 +186,8 @@ export interface RunAuditor {
|
||||
database(input: DatabaseAuditInput): Promise<void>;
|
||||
/** Emit a filesystem-domain audit event. No-op if no run context is available. */
|
||||
filesystem(input: FilesystemAuditInput): Promise<void>;
|
||||
/** Emit a sandbox-domain audit event. No-op if no run context is available. */
|
||||
sandbox(input: SandboxAuditInput): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,6 +207,7 @@ export function createRunAuditor(store: TaskStore, context: EngineRunContext | n
|
||||
git: async () => { /* no-op */ },
|
||||
database: async () => { /* no-op */ },
|
||||
filesystem: async () => { /* no-op */ },
|
||||
sandbox: async () => { /* no-op */ },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -206,6 +220,7 @@ export function createRunAuditor(store: TaskStore, context: EngineRunContext | n
|
||||
git: async () => { /* no-op */ },
|
||||
database: async () => { /* no-op */ },
|
||||
filesystem: async () => { /* no-op */ },
|
||||
sandbox: async () => { /* no-op */ },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -270,6 +285,24 @@ export function createRunAuditor(store: TaskStore, context: EngineRunContext | n
|
||||
};
|
||||
await store.recordRunAuditEvent(eventInput);
|
||||
},
|
||||
|
||||
sandbox: async (input: SandboxAuditInput) => {
|
||||
const eventInput: RunAuditEventInput = {
|
||||
taskId: context.taskId,
|
||||
agentId: context.agentId,
|
||||
runId: context.runId,
|
||||
domain: "sandbox",
|
||||
mutationType: input.type,
|
||||
target: input.target,
|
||||
metadata: {
|
||||
phase: context.phase,
|
||||
...(context.source ? { source: context.source } : {}),
|
||||
...(context.taskLineageId ? { taskLineageId: context.taskLineageId } : {}),
|
||||
...input.metadata,
|
||||
},
|
||||
};
|
||||
await store.recordRunAuditEvent(eventInput);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user