86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { validatePrpEvent, type PrpEvent } from "./replay-contract.js";
|
|
|
|
function workspaceEvent(eventType: "workspace.change.updated" | "workspace.diff.recorded"): PrpEvent {
|
|
return {
|
|
schema: "paperclip.prp.event.v1",
|
|
sourceEventId: `workspace:${eventType}`,
|
|
sourceSeq: eventType === "workspace.change.updated" ? 1 : 2,
|
|
sourceInstanceId: "runner-1",
|
|
sourceKind: "runner",
|
|
runId: "run-1",
|
|
turnId: "turn-1",
|
|
eventType,
|
|
schemaVersion: 1,
|
|
priority: 1,
|
|
emittedAt: "2026-08-21T12:00:00.000Z",
|
|
payload: {
|
|
schema: "paperclip.workspace.diff.v1",
|
|
changeSetId: "turn-1:workspace",
|
|
revision: 1,
|
|
source: "runner_verified",
|
|
complete: eventType === "workspace.diff.recorded",
|
|
files: [{
|
|
path: "src/index.ts",
|
|
operation: "modify",
|
|
previousPath: null,
|
|
additions: 1,
|
|
deletions: 1,
|
|
binary: false,
|
|
diff: "--- a/src/index.ts\n+++ b/src/index.ts\n-old\n+new\n",
|
|
}],
|
|
totals: { files: 1, additions: 1, deletions: 1 },
|
|
patchArtifactRef: null,
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("PRP workspace change events", () => {
|
|
it("accepts provisional and complete canonical workspace changes", () => {
|
|
expect(validatePrpEvent(workspaceEvent("workspace.change.updated")).ok).toBe(true);
|
|
expect(validatePrpEvent(workspaceEvent("workspace.diff.recorded")).ok).toBe(true);
|
|
});
|
|
|
|
it("rejects traversal paths and incomplete recorded diffs", () => {
|
|
const traversal = workspaceEvent("workspace.diff.recorded");
|
|
traversal.payload.files[0]!.path = "../secret";
|
|
expect(validatePrpEvent(traversal).ok).toBe(false);
|
|
const incomplete = workspaceEvent("workspace.diff.recorded");
|
|
incomplete.payload.complete = false;
|
|
expect(validatePrpEvent(incomplete).ok).toBe(false);
|
|
});
|
|
|
|
it("validates a canonical individual workspace-file reference", () => {
|
|
const event: PrpEvent = {
|
|
schema: "paperclip.prp.event.v1",
|
|
sourceEventId: "workspace:file:1",
|
|
sourceSeq: 1,
|
|
sourceInstanceId: "runner-1",
|
|
sourceKind: "runner",
|
|
runId: "run-1",
|
|
turnId: "turn-1",
|
|
eventType: "workspace.file.referenced",
|
|
schemaVersion: 1,
|
|
priority: 1,
|
|
emittedAt: "2026-08-21T12:00:00.000Z",
|
|
payload: {
|
|
schema: "paperclip.workspace.file_reference.v1",
|
|
referenceId: "turn-1:file:guide",
|
|
source: "runner_verified",
|
|
path: "docs/guide.md",
|
|
displayName: "guide.md",
|
|
mediaType: "text/markdown",
|
|
presentation: "document",
|
|
line: null,
|
|
preview: "# Guide\n",
|
|
previewTruncated: false,
|
|
contentDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
},
|
|
};
|
|
expect(validatePrpEvent(event).ok).toBe(true);
|
|
event.payload.path = "/private/guide.md";
|
|
expect(validatePrpEvent(event).ok).toBe(false);
|
|
});
|
|
});
|