/**
 * Tests for SLSA Provenance generation
 */
import { describe, it, expect, vi, beforeEach } from "vitest";
import { generateProvenance, generateProvenanceJson, generateProvenanceSummary, verifyProvenance, sha256Digest, } from "./provenance.js";
// Mock fs/promises
vi.mock("fs/promises", () => ({
    readFile: vi.fn(),
    access: vi.fn(),
}));
import { readFile, access } from "fs/promises";
describe("SLSA Provenance", () => {
    const mockCertification = {
        metadata: {
            id: "cert-test-123",
            project_name: "test-project",
            project_path: "/test/path",
            started_at: "2024-01-01T00:00:00Z",
            completed_at: "2024-01-01T01:00:00Z",
            status: "completed",
            agents_requested: ["security", "reliability"],
            agents_completed: ["security", "reliability"],
            certification_level: "CERTIFIED",
            final_score: 95,
        },
        agents: {
            security: {
                agent: "security",
                started_at: "2024-01-01T00:00:00Z",
                completed_at: "2024-01-01T00:30:00Z",
                status: "completed",
                findings: [],
                summary: {
                    total_findings: 0,
                    by_severity: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
                    confidence_score: 95,
                    coverage_areas: ["authentication", "authorization"],
                },
            },
        },
        cross_verifications: [],
        red_team_challenges: [],
        consensus: {
            overall_score: 95,
            certification_level: "CERTIFIED",
            agent_scores: { security: 95, reliability: 95, typesafety: 0, performance: 0, quality: 0, redteam: 0, "agent-redteam": 0, "agent-privacy": 0, "agent-integrity": 0, adversary: 0, antagonist: 0 },
            agent_confidences: { security: 95, reliability: 95, typesafety: 0, performance: 0, quality: 0, redteam: 0, "agent-redteam": 0, "agent-privacy": 0, "agent-integrity": 0, adversary: 0, antagonist: 0 },
            total_findings: 0,
            by_severity: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
            cross_verification_rate: 100,
            red_team_challenges_accepted: 0,
            red_team_challenges_total: 0,
            calculated_at: "2024-01-01T01:00:00Z",
        },
    };
    const mockArtifactContent = JSON.stringify(mockCertification, null, 2);
    beforeEach(() => {
        vi.clearAllMocks();
        // Default: no git repo
        vi.mocked(access).mockRejectedValue(new Error("Not found"));
    });
    describe("generateProvenance", () => {
        it("generates valid in-toto statement", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance._type).toBe("https://in-toto.io/Statement/v1");
            expect(provenance.predicateType).toBe("https://slsa.dev/provenance/v1");
            expect(provenance.subject).toBeDefined();
            expect(provenance.subject.length).toBe(1);
            expect(provenance.predicate).toBeDefined();
        });
        it("includes subject with SHA-256 digest", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.subject[0].digest.sha256).toBeDefined();
            expect(provenance.subject[0].digest.sha256).toBe(sha256Digest(mockArtifactContent));
        });
        it("includes build definition", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.predicate.buildDefinition).toBeDefined();
            expect(provenance.predicate.buildDefinition.buildType).toBe("https://vaspera.dev/certification/v1");
        });
        it("includes run details with builder", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.predicate.runDetails).toBeDefined();
            expect(provenance.predicate.runDetails.builder.id).toBe("https://github.com/RCOLKITT/vaspera-hardening-mcp");
        });
        it("includes metadata with certification timestamps", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.predicate.runDetails.metadata?.invocationId).toBe("cert-test-123");
            expect(provenance.predicate.runDetails.metadata?.startedOn).toBe("2024-01-01T00:00:00Z");
            expect(provenance.predicate.runDetails.metadata?.finishedOn).toBe("2024-01-01T01:00:00Z");
        });
        it("includes agents in external parameters", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.predicate.buildDefinition.externalParameters?.agents).toEqual([
                "security",
                "reliability",
            ]);
        });
        it("uses custom builder ID", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent, {
                builderId: "https://custom-builder.com",
            });
            expect(provenance.predicate.runDetails.builder.id).toBe("https://custom-builder.com");
        });
        it("uses custom build type", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent, {
                buildType: "https://custom.build/type",
            });
            expect(provenance.predicate.buildDefinition.buildType).toBe("https://custom.build/type");
        });
        it("includes byproducts with consensus info", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.predicate.runDetails.byproducts).toBeDefined();
            expect(provenance.predicate.runDetails.byproducts.length).toBe(1);
            expect(provenance.predicate.runDetails.byproducts[0].uri).toContain("consensus");
        });
        it("includes git commit when available", async () => {
            vi.mocked(access).mockResolvedValue(undefined);
            vi.mocked(readFile).mockImplementation(async (path) => {
                if (String(path).includes("HEAD")) {
                    return "abc123def456\n";
                }
                return "";
            });
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            expect(provenance.subject[0].digest.gitCommit).toBe("abc123def456");
        });
    });
    describe("generateProvenanceJson", () => {
        it("returns valid JSON string", async () => {
            const json = await generateProvenanceJson(mockCertification, mockArtifactContent);
            expect(() => JSON.parse(json)).not.toThrow();
            const parsed = JSON.parse(json);
            expect(parsed._type).toBe("https://in-toto.io/Statement/v1");
        });
    });
    describe("generateProvenanceSummary", () => {
        it("generates readable summary", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            const summary = generateProvenanceSummary(provenance);
            expect(summary).toContain("Subject:");
            expect(summary).toContain("Builder:");
            expect(summary).toContain("Build Type:");
        });
    });
    describe("verifyProvenance", () => {
        it("validates matching provenance", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            const result = verifyProvenance(provenance, mockCertification, mockArtifactContent);
            expect(result.valid).toBe(true);
            expect(result.errors).toHaveLength(0);
        });
        it("fails with wrong predicate type", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            provenance.predicateType = "wrong";
            const result = verifyProvenance(provenance, mockCertification, mockArtifactContent);
            expect(result.valid).toBe(false);
            expect(result.errors).toContain("Invalid predicate type: wrong");
        });
        it("fails with mismatched digest", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            const result = verifyProvenance(provenance, mockCertification, "different content");
            expect(result.valid).toBe(false);
            expect(result.errors.some((e) => e.includes("digest"))).toBe(true);
        });
        it("fails with wrong certification ID", async () => {
            const provenance = await generateProvenance(mockCertification, mockArtifactContent);
            const differentCert = {
                ...mockCertification,
                metadata: { ...mockCertification.metadata, id: "different-id" },
            };
            const result = verifyProvenance(provenance, differentCert, mockArtifactContent);
            expect(result.valid).toBe(false);
            expect(result.errors.some((e) => e.includes("Invocation ID"))).toBe(true);
        });
    });
    describe("sha256Digest", () => {
        it("computes correct SHA-256", () => {
            const digest = sha256Digest("test");
            expect(digest).toBe("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
        });
        it("produces different digests for different content", () => {
            const d1 = sha256Digest("content1");
            const d2 = sha256Digest("content2");
            expect(d1).not.toBe(d2);
        });
    });
});
//# sourceMappingURL=provenance.test.js.map