Files
qiming/qimingclaw/crates/agent-electron-client/src/main/services/digitalEmployee/artifactAccessService.test.ts
2026-06-10 10:00:34 +08:00

115 lines
4.1 KiB
TypeScript

import { mkdtemp, rm, writeFile } from "fs/promises";
import os from "os";
import path from "path";
import { beforeEach, describe, expect, it, vi } from "vitest";
const mockStateService = vi.hoisted(() => ({
records: { artifacts: [] as any[] },
recordDigitalEmployeeArtifactAccess: vi.fn((input: { status: string }) => ({ eventId: `event:${input.status}` })),
}));
const mockShell = vi.hoisted(() => ({
openPath: vi.fn(async () => ""),
showItemInFolder: vi.fn(),
}));
vi.mock("electron", () => ({
shell: mockShell,
}));
vi.mock("./stateService", () => ({
readDigitalEmployeeRuntimeRecords: () => mockStateService.records,
recordDigitalEmployeeArtifactAccess: mockStateService.recordDigitalEmployeeArtifactAccess,
}));
describe("digital employee artifact access service", () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
mockStateService.records = { artifacts: [] };
});
it("returns bounded text preview for a local artifact and audits allowed access", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "qiming-artifact-access-"));
try {
const filePath = path.join(tempDir, "report.txt");
await writeFile(filePath, "hello artifact", "utf8");
mockStateService.records = {
artifacts: [{
id: "artifact-1",
planId: "plan-1",
taskId: "task-1",
runId: "run-1",
kind: "report",
name: "report.txt",
uri: filePath,
payload: { delivery: { status: "completed", workspace_id: "workspace-1" } },
syncStatus: "pending",
createdAt: "2026-06-07T08:00:00.000Z",
}],
};
const { accessDigitalEmployeeArtifact } = await import("./artifactAccessService");
const result = await accessDigitalEmployeeArtifact({ artifactId: "artifact-1", action: "preview", actor: "operator" });
expect(result).toMatchObject({
ok: true,
artifact_id: "artifact-1",
action: "preview",
status: "allowed",
preview: { name: "report.txt", text: "hello artifact", mime: "text/plain" },
});
expect(mockStateService.recordDigitalEmployeeArtifactAccess).toHaveBeenCalledWith(expect.objectContaining({
artifactId: "artifact-1",
status: "allowed",
uriSummary: expect.objectContaining({ basename: "report.txt", extension: ".txt" }),
deliverySnapshot: expect.objectContaining({ delivery_status: "completed", workspace_id: "workspace-1" }),
}));
} finally {
await rm(tempDir, { recursive: true, force: true });
}
});
it("rejects non-local artifact URIs without invoking shell", async () => {
mockStateService.records = {
artifacts: [{
id: "artifact-remote",
kind: "report",
name: "remote.txt",
uri: "https://example.com/remote.txt",
payload: {},
syncStatus: "pending",
createdAt: "2026-06-07T08:00:00.000Z",
}],
};
const { accessDigitalEmployeeArtifact } = await import("./artifactAccessService");
const result = await accessDigitalEmployeeArtifact({ artifactId: "artifact-remote", action: "download" });
expect(result).toMatchObject({ ok: false, status: "rejected", error: "local_uri_required" });
expect(mockShell.openPath).not.toHaveBeenCalled();
expect(mockShell.showItemInFolder).not.toHaveBeenCalled();
});
it("rejects unknown access actions before opening anything", async () => {
mockStateService.records = {
artifacts: [{
id: "artifact-1",
kind: "report",
name: "report.txt",
uri: "/tmp/report.txt",
payload: {},
syncStatus: "pending",
createdAt: "2026-06-07T08:00:00.000Z",
}],
};
const { accessDigitalEmployeeArtifact } = await import("./artifactAccessService");
const result = await accessDigitalEmployeeArtifact({ artifactId: "artifact-1", action: "delete" });
expect(result).toMatchObject({ ok: false, status: "rejected", error: "dangerous_action" });
expect(mockShell.openPath).not.toHaveBeenCalled();
expect(mockShell.showItemInFolder).not.toHaveBeenCalled();
});
});