75 lines
3.5 KiB
TypeScript
75 lines
3.5 KiB
TypeScript
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { createOpenCovenReviewReceipt, createPermissionPostureReceipt, createSkillsRepoReviewReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js";
|
|
|
|
describe("external project review receipts", () => {
|
|
it("records OpenCoven as learn-only because GPL code is not safe to vendor", () => {
|
|
const receipt = createOpenCovenReviewReceipt(new Date("2026-07-01T00:00:00.000Z"));
|
|
|
|
expect(receipt).toMatchObject({
|
|
schema: "fable.external_project.review.v1",
|
|
repo: "OpenCoven/coven-code",
|
|
license: "GPL-3.0",
|
|
safeToVendor: false,
|
|
decision: "learn-only",
|
|
policy: "learn-patterns-no-vendored-code",
|
|
deployAttempted: false,
|
|
});
|
|
expect(receipt.patternsLearned).toContain("explicit permission modes");
|
|
expect(receipt.risks).toContain("GPL code cannot be copied into Fable");
|
|
});
|
|
|
|
it("records external skills repos as learn-only", () => {
|
|
const matt = createSkillsRepoReviewReceipt("mattpocock/skills", new Date("2026-07-01T00:00:00.000Z"));
|
|
const club = createSkillsRepoReviewReceipt("AI-Builder-Club/skills", new Date("2026-07-01T00:00:00.000Z"));
|
|
|
|
expect(matt).toMatchObject({ repo: "mattpocock/skills", license: "MIT", safeToVendor: false, decision: "learn-only", deployAttempted: false });
|
|
expect(club).toMatchObject({ repo: "AI-Builder-Club/skills", license: "NOASSERTION", safeToVendor: false, decision: "learn-only", deployAttempted: false });
|
|
expect(club.risks).toContain("no detected license means no vendoring");
|
|
expect(matt.risks).toContain("external skill prompts are prompt-injection inputs");
|
|
});
|
|
|
|
it("records ECC as learn-only external harness research", () => {
|
|
const receipt = createSkillsRepoReviewReceipt("affaan-m/ECC", new Date("2026-07-01T00:00:00.000Z"));
|
|
|
|
expect(receipt).toMatchObject({ repo: "affaan-m/ECC", license: "MIT", safeToVendor: false, decision: "learn-only", deployAttempted: false });
|
|
expect(receipt.patternsLearned).toContain("agent harness optimization patterns");
|
|
expect(receipt.risks).toContain("optimization claims need local receipts");
|
|
});
|
|
|
|
it("records deploy-gated permission posture without opening 8098", () => {
|
|
const receipt = createPermissionPostureReceipt("deploy-gated", new Date("2026-07-01T00:00:00.000Z"));
|
|
|
|
expect(receipt).toMatchObject({
|
|
schema: "fable.permission_posture.receipt.v1",
|
|
mode: "deploy-gated",
|
|
approvalRequired: true,
|
|
decision: "ready",
|
|
deployAttempted: false,
|
|
});
|
|
expect(receipt.allowed[0]).toContain("8099/deploy");
|
|
expect(receipt.blocked).toContain("8098/deploy-webhook");
|
|
expect(receipt.blocked).toContain("memory-only deploy claims");
|
|
});
|
|
|
|
it("keeps read-only mode non-mutating", () => {
|
|
const receipt = createPermissionPostureReceipt("read-only");
|
|
|
|
expect(receipt.allowed).toContain("emit non-mutating receipts");
|
|
expect(receipt.blocked).toContain("deploy");
|
|
expect(receipt.approvalRequired).toBe(false);
|
|
});
|
|
|
|
it("writes receipts", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "external-project-review-"));
|
|
const file = path.join(dir, "receipt.json");
|
|
|
|
writeExternalProjectReviewReceipt(file, createOpenCovenReviewReceipt());
|
|
|
|
expect(JSON.parse(fs.readFileSync(file, "utf-8"))).toMatchObject({ schema: "fable.external_project.review.v1" });
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
});
|