/**
 * Tests for Transcript Verifier
 */
import { describe, it, expect, beforeEach } from "vitest";
import { createHash } from "crypto";
import { verifyMerkleChain, verifyTimestampOrdering, verifyModelConsistency, verifyProvenance, verifyTranscriptBundle, quickIntegrityCheck, generateVerificationReport, } from "./verifier.js";
import { TranscriptLogger } from "./logger.js";
import { generateTranscriptProvenance } from "./signing.js";
describe("verifyMerkleChain", () => {
    let logger;
    beforeEach(() => {
        logger = new TranscriptLogger("test-session");
    });
    it("returns valid for empty entries", () => {
        const result = verifyMerkleChain([], "genesis", "genesis");
        expect(result.valid).toBe(true);
        expect(result.entryCount).toBe(0);
    });
    it("verifies valid chain", () => {
        logger.logPrompt("First");
        logger.logToolCall("tool", "input");
        logger.logModelResponse("Response");
        const entries = logger.getChain();
        const transcript = logger.generateSignedTranscript();
        const result = verifyMerkleChain(entries, transcript.rootHash, transcript.tipHash);
        expect(result.valid).toBe(true);
        expect(result.entryCount).toBe(3);
    });
    it("detects broken previous hash", () => {
        logger.logPrompt("First");
        logger.logPrompt("Second");
        const entries = logger.getChain();
        // Tamper with previous hash
        entries[1].previousHash = "tampered-hash";
        const transcript = logger.generateSignedTranscript();
        const result = verifyMerkleChain(entries, transcript.rootHash, transcript.tipHash);
        expect(result.valid).toBe(false);
        expect(result.brokenAt).toBe(1);
        expect(result.message).toContain("Chain broken");
    });
    it("detects tampered content", () => {
        logger.logPrompt("Original content");
        const entries = logger.getChain();
        const transcript = logger.generateSignedTranscript();
        // Tamper with content
        entries[0].content = "Tampered content";
        const result = verifyMerkleChain(entries, transcript.rootHash, transcript.tipHash);
        expect(result.valid).toBe(false);
        expect(result.message).toContain("Content hash mismatch");
    });
    it("detects root hash mismatch", () => {
        logger.logPrompt("Entry");
        const entries = logger.getChain();
        const result = verifyMerkleChain(entries, "wrong-root-hash", entries[0].entryHash);
        expect(result.valid).toBe(false);
        expect(result.message).toContain("Root hash mismatch");
    });
    it("detects tip hash mismatch", () => {
        logger.logPrompt("First");
        logger.logPrompt("Last");
        const entries = logger.getChain();
        const result = verifyMerkleChain(entries, entries[0].entryHash, "wrong-tip-hash");
        expect(result.valid).toBe(false);
        expect(result.message).toContain("Tip hash mismatch");
    });
});
describe("verifyTimestampOrdering", () => {
    it("returns passed for single entry", () => {
        const entries = [
            createMockEntry("1", new Date().toISOString()),
        ];
        const result = verifyTimestampOrdering(entries);
        expect(result.passed).toBe(true);
    });
    it("returns passed for correctly ordered timestamps", () => {
        const now = Date.now();
        const entries = [
            createMockEntry("1", new Date(now).toISOString()),
            createMockEntry("2", new Date(now + 1000).toISOString()),
            createMockEntry("3", new Date(now + 2000).toISOString()),
        ];
        const result = verifyTimestampOrdering(entries);
        expect(result.passed).toBe(true);
    });
    it("detects backwards timestamps", () => {
        const now = Date.now();
        const entries = [
            createMockEntry("1", new Date(now + 1000).toISOString()),
            createMockEntry("2", new Date(now).toISOString()), // Earlier!
        ];
        const result = verifyTimestampOrdering(entries);
        expect(result.passed).toBe(false);
        expect(result.details?.issues).toBeDefined();
    });
    it("detects excessive gaps when specified", () => {
        const now = Date.now();
        const entries = [
            createMockEntry("1", new Date(now).toISOString()),
            createMockEntry("2", new Date(now + 60000).toISOString()), // 60 seconds later
        ];
        const result = verifyTimestampOrdering(entries, 10000); // Max 10 seconds
        expect(result.passed).toBe(false);
        expect(result.details?.issues).toBeDefined();
    });
});
describe("verifyModelConsistency", () => {
    it("returns passed for no model IDs", () => {
        const entries = [
            createMockEntry("1"),
            createMockEntry("2"),
        ];
        const result = verifyModelConsistency(entries);
        expect(result.passed).toBe(true);
        expect(result.message).toContain("No model IDs");
    });
    it("returns passed for consistent model ID", () => {
        const entries = [
            createMockEntry("1", undefined, "claude-3"),
            createMockEntry("2", undefined, "claude-3"),
        ];
        const result = verifyModelConsistency(entries);
        expect(result.passed).toBe(true);
        expect(result.message).toContain("claude-3");
    });
    it("detects multiple model IDs", () => {
        const entries = [
            createMockEntry("1", undefined, "claude-3"),
            createMockEntry("2", undefined, "gpt-4"),
        ];
        const result = verifyModelConsistency(entries);
        expect(result.passed).toBe(false);
        expect(result.message).toContain("Multiple model IDs");
        expect(result.details?.modelIds).toContain("claude-3");
        expect(result.details?.modelIds).toContain("gpt-4");
    });
});
describe("verifyProvenance", () => {
    it("validates consistent provenance", () => {
        const logger = new TranscriptLogger("prov-test");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = verifyProvenance(bundle);
        expect(result.passed).toBe(true);
    });
    it("detects session ID mismatch", () => {
        const logger = new TranscriptLogger("session-1");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        // Tamper with provenance
        provenance.predicate.sessionId = "session-2";
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = verifyProvenance(bundle);
        expect(result.passed).toBe(false);
        expect(result.details?.issues).toBeDefined();
    });
    it("detects entry count mismatch", () => {
        const logger = new TranscriptLogger("test");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        // Tamper with entry count
        provenance.predicate.entryCount = 999;
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = verifyProvenance(bundle);
        expect(result.passed).toBe(false);
    });
});
describe("verifyTranscriptBundle", () => {
    it("verifies valid bundle", async () => {
        const logger = new TranscriptLogger("bundle-test");
        logger.logPrompt("First");
        logger.logToolCall("tool", "input");
        logger.logModelResponse("Response");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = await verifyTranscriptBundle(bundle, logger.getChain());
        expect(result.valid).toBe(true);
        expect(result.summary.failed).toBe(0);
    });
    it("validates expected session ID", async () => {
        const logger = new TranscriptLogger("expected-session");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = await verifyTranscriptBundle(bundle, logger.getChain(), {
            expectedSessionId: "different-session",
        });
        expect(result.valid).toBe(false);
        const sessionCheck = result.checks.find((c) => c.name === "session-id");
        expect(sessionCheck?.passed).toBe(false);
    });
    it("validates bundle version", async () => {
        const logger = new TranscriptLogger("version-test");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "2.0.0", // Unsupported version
        };
        const result = await verifyTranscriptBundle(bundle);
        expect(result.valid).toBe(false);
        const versionCheck = result.checks.find((c) => c.name === "bundle-version");
        expect(versionCheck?.passed).toBe(false);
    });
    it("notes redactions in checks", async () => {
        const logger = new TranscriptLogger("redaction-test");
        logger.logPrompt("Test");
        logger.logRedaction("hash", "email");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const result = await verifyTranscriptBundle(bundle);
        const redactionCheck = result.checks.find((c) => c.name === "redactions-declared");
        expect(redactionCheck).toBeDefined();
        expect(redactionCheck?.passed).toBe(true);
    });
});
describe("quickIntegrityCheck", () => {
    it("returns true for valid chain", () => {
        const logger = new TranscriptLogger("quick-test");
        logger.logPrompt("First");
        logger.logPrompt("Second");
        const entries = logger.getChain();
        const transcript = logger.generateSignedTranscript();
        const result = quickIntegrityCheck(entries, transcript.rootHash, transcript.tipHash);
        expect(result).toBe(true);
    });
    it("returns false for invalid chain", () => {
        const logger = new TranscriptLogger("quick-test");
        logger.logPrompt("First");
        const entries = logger.getChain();
        entries[0].contentHash = "tampered";
        const transcript = logger.generateSignedTranscript();
        const result = quickIntegrityCheck(entries, transcript.rootHash, transcript.tipHash);
        expect(result).toBe(false);
    });
});
describe("generateVerificationReport", () => {
    it("generates markdown report for valid verification", async () => {
        const logger = new TranscriptLogger("report-test");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "1.0.0",
        };
        const verification = await verifyTranscriptBundle(bundle, logger.getChain());
        const report = generateVerificationReport(verification);
        expect(report).toContain("## Transcript Verification Report");
        expect(report).toContain("VALID");
        expect(report).toContain("report-test");
    });
    it("shows failed checks in report", async () => {
        const logger = new TranscriptLogger("fail-report");
        logger.logPrompt("Test");
        const transcript = logger.generateSignedTranscript();
        const provenance = generateTranscriptProvenance(transcript);
        const bundle = {
            transcript,
            provenance,
            createdAt: new Date().toISOString(),
            version: "2.0.0", // Invalid version
        };
        const verification = await verifyTranscriptBundle(bundle);
        const report = generateVerificationReport(verification);
        expect(report).toContain("INVALID");
        expect(report).toContain("bundle-version");
    });
});
// Helper function to create mock entries
function createMockEntry(id, timestamp, modelId) {
    const content = `Content for ${id}`;
    const ts = timestamp || new Date().toISOString();
    const contentHash = createHash("sha256").update(content).digest("hex");
    return {
        id,
        timestamp: ts,
        type: "prompt",
        sessionId: "test-session",
        modelId,
        content,
        contentHash,
        previousHash: "genesis",
        entryHash: createHash("sha256")
            .update(JSON.stringify({ id, timestamp: ts, contentHash }))
            .digest("hex"),
    };
}
//# sourceMappingURL=verifier.test.js.map