Files
fusion/packages/engine/src/__tests__/external-integration-manifest.test.ts
Fusion (runfusion.ai) b06cf64cd4 feat(FN-5321): add evidence gap detector for external integration manifests
Implements external integration validation (FN-5321) with a manifest validator scaffold, worktrunk manifest wiring, and an evidence gap detector that runs during spec validation and triage; the reviewer also gates on external integration readiness. Includes tests for manifest, evidence gap, and tria

Fusion-Task-Id: FN-5321
2026-05-20 15:54:44 -07:00

122 lines
4.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { validateExternalIntegrationManifest } from "../external-integrations/manifest.js";
describe("validateExternalIntegrationManifest", () => {
it("accepts a valid upstream-verified manifest", () => {
const result = validateExternalIntegrationManifest({
id: "worktrunk",
binaryName: "wt",
upstreamRepo: "max-sixty/worktrunk",
docsUrl: "https://worktrunk.dev/",
source: "upstream-verified",
version: "0.4.2",
verifiedAt: "2026-05-20T00:00:00.000Z",
assets: {
"linux-x64": {
url: "https://github.com/max-sixty/worktrunk/releases/download/v0.4.2/wt-linux-x64.tar.gz",
sha256: "a".repeat(64),
},
},
});
expect(result).toEqual({ ok: true });
});
it("rejects pending manifests with non-empty assets", () => {
const result = validateExternalIntegrationManifest({
id: "worktrunk",
binaryName: "wt",
upstreamRepo: "max-sixty/worktrunk",
docsUrl: "https://worktrunk.dev/",
source: "upstream-pending-verification",
version: null,
verifiedAt: null,
assets: { linux: { url: "https://github.com/max-sixty/worktrunk/releases/download/v0.4.2/x", sha256: "a".repeat(64) } },
});
expect(result).toMatchObject({ ok: false });
if (result.ok) throw new Error("expected validation failure");
expect(result.missingFields).toContain("assets:must-be-empty-when-pending");
});
it("rejects upstream-verified manifests with empty sha256", () => {
const result = validateExternalIntegrationManifest({
id: "worktrunk",
binaryName: "wt",
upstreamRepo: "max-sixty/worktrunk",
docsUrl: "https://worktrunk.dev/",
source: "upstream-verified",
version: "0.4.2",
verifiedAt: "2026-05-20T00:00:00.000Z",
assets: {
linux: {
url: "https://github.com/max-sixty/worktrunk/releases/download/v0.4.2/wt-linux-x64.tar.gz",
sha256: "",
},
},
});
expect(result).toMatchObject({ ok: false });
if (result.ok) throw new Error("expected validation failure");
expect(result.missingFields).toContain("assets.linux.sha256");
});
it("rejects malformed upstreamRepo values", () => {
const result = validateExternalIntegrationManifest({
id: "cloudflared",
binaryName: "cloudflared",
upstreamRepo: "cloudflared",
docsUrl: "https://developers.cloudflare.com/",
source: "upstream-pending-verification",
version: null,
verifiedAt: null,
assets: {},
});
expect(result).toMatchObject({ ok: false });
if (result.ok) throw new Error("expected validation failure");
expect(result.missingFields).toContain("upstreamRepo");
});
it("rejects asset URLs outside upstream repo and docs host", () => {
const result = validateExternalIntegrationManifest({
id: "worktrunk",
binaryName: "wt",
upstreamRepo: "max-sixty/worktrunk",
docsUrl: "https://worktrunk.dev/",
source: "upstream-verified",
version: "0.4.2",
verifiedAt: "2026-05-20T00:00:00.000Z",
assets: {
linux: {
url: "https://example.com/download/wt-linux-x64.tar.gz",
sha256: "a".repeat(64),
},
},
});
expect(result).toMatchObject({ ok: false });
if (result.ok) throw new Error("expected validation failure");
expect(result.missingFields).toContain("assets.linux.url");
});
it("rejects missing required identifiers", () => {
const result = validateExternalIntegrationManifest({
source: "upstream-pending-verification",
version: null,
verifiedAt: null,
assets: {},
});
expect(result).toMatchObject({ ok: false });
if (result.ok) throw new Error("expected validation failure");
expect(result.missingFields).toEqual(expect.arrayContaining(["id", "binaryName", "docsUrl"]));
});
it.each([null, undefined, 42, "bad", true])("never throws for garbage input: %p", (input) => {
expect(() => validateExternalIntegrationManifest(input)).not.toThrow();
const result = validateExternalIntegrationManifest(input);
expect(result.ok).toBe(false);
});
});