/**
 * Persistence Layer Tests
 */
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtemp, rm } from "fs/promises";
import { join } from "path";
import { tmpdir } from "os";
import { PersistenceManager } from "../index.js";
describe("Persistence Layer", () => {
    let manager;
    let tempDir;
    beforeEach(async () => {
        tempDir = await mkdtemp(join(tmpdir(), `persistence-test-${Math.random().toString(36).slice(2, 8)}-`));
        manager = PersistenceManager.createInMemory();
    });
    afterEach(async () => {
        manager.close();
        await rm(tempDir, { recursive: true, force: true });
    });
    describe("Projects Repository", () => {
        it("creates a project", () => {
            const project = manager.projects.create("/test/project", "Test Project");
            expect(project.id).toBeDefined();
            expect(project.path).toBe("/test/project");
            expect(project.name).toBe("Test Project");
            expect(project.createdAt).toBeDefined();
        });
        it("finds project by path", () => {
            manager.projects.create("/test/project", "Test Project");
            const found = manager.projects.findByPath("/test/project");
            expect(found).toBeDefined();
            expect(found?.name).toBe("Test Project");
        });
        it("finds or creates project", () => {
            const first = manager.projects.findOrCreate("/test/project", "Test");
            const second = manager.projects.findOrCreate("/test/project", "Different");
            expect(first.id).toBe(second.id);
        });
        it("lists all projects", () => {
            manager.projects.create("/project1");
            manager.projects.create("/project2");
            const projects = manager.projects.list();
            expect(projects.length).toBe(2);
        });
    });
    describe("Findings Repository", () => {
        let projectId;
        beforeEach(() => {
            const project = manager.projects.create("/test/project");
            projectId = project.id;
        });
        it("creates a finding", () => {
            const finding = manager.findings.create({
                projectId,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection vulnerability",
                scannerSource: "semgrep",
                status: "open",
                confidence: 95,
            });
            expect(finding.id).toBeDefined();
            expect(finding.severity).toBe("high");
            expect(finding.firstSeenAt).toBeDefined();
        });
        it("upserts finding from scan", () => {
            const first = manager.findings.upsertFromScan({
                projectId,
                severity: "high",
                category: "xss",
                file: "src/ui.ts",
                line: 100,
                description: "XSS vulnerability",
                scannerSource: "semgrep",
                confidence: 90,
            });
            expect(first.isNew).toBe(true);
            const second = manager.findings.upsertFromScan({
                projectId,
                severity: "high",
                category: "xss",
                file: "src/ui.ts",
                line: 100,
                description: "XSS vulnerability",
                scannerSource: "semgrep",
                confidence: 90,
            });
            expect(second.isNew).toBe(false);
            expect(second.finding.id).toBe(first.finding.id);
        });
        it("filters findings", () => {
            manager.findings.create({
                projectId,
                severity: "critical",
                category: "sql-injection",
                file: "src/api.ts",
                line: 10,
                description: "Critical SQL injection",
                scannerSource: "semgrep",
                status: "open",
                confidence: 100,
            });
            manager.findings.create({
                projectId,
                severity: "low",
                category: "code-smell",
                file: "src/utils.ts",
                line: 20,
                description: "Minor issue",
                scannerSource: "eslint",
                status: "open",
                confidence: 80,
            });
            const critical = manager.findings.find({ projectId, severity: "critical" });
            expect(critical.length).toBe(1);
            const semgrep = manager.findings.find({ projectId, scannerSource: "semgrep" });
            expect(semgrep.length).toBe(1);
        });
        it("updates finding status", () => {
            const finding = manager.findings.create({
                projectId,
                severity: "high",
                category: "xss",
                file: "src/ui.ts",
                line: 50,
                description: "XSS",
                scannerSource: "semgrep",
                status: "open",
                confidence: 90,
            });
            const updated = manager.findings.updateStatus(finding.id, "fixed", "developer");
            expect(updated).toBe(true);
            const fetched = manager.findings.findById(finding.id);
            expect(fetched?.status).toBe("fixed");
            expect(fetched?.fixedAt).toBeDefined();
        });
        it("marks finding as false positive", () => {
            const finding = manager.findings.create({
                projectId,
                severity: "medium",
                category: "security",
                file: "src/test.ts",
                line: 1,
                description: "Test finding",
                scannerSource: "semgrep",
                status: "open",
                confidence: 70,
            });
            manager.findings.markFalsePositive(finding.id, "Not applicable in this context");
            const fetched = manager.findings.findById(finding.id);
            expect(fetched?.status).toBe("false_positive");
            expect(fetched?.falsePositive).toBe(true);
            expect(fetched?.falsePositiveReason).toBe("Not applicable in this context");
        });
        it("gets project stats", () => {
            manager.findings.create({
                projectId,
                severity: "critical",
                category: "sql-injection",
                file: "src/api.ts",
                line: 10,
                description: "Critical",
                scannerSource: "semgrep",
                status: "open",
                confidence: 100,
            });
            manager.findings.create({
                projectId,
                severity: "high",
                category: "xss",
                file: "src/ui.ts",
                line: 20,
                description: "High",
                scannerSource: "semgrep",
                status: "open",
                confidence: 90,
            });
            const stats = manager.findings.getStats(projectId);
            expect(stats.totalFindings).toBe(2);
            expect(stats.openFindings).toBe(2);
            expect(stats.bySeverity.critical).toBe(1);
            expect(stats.bySeverity.high).toBe(1);
        });
    });
    describe("Scans Repository", () => {
        let projectId;
        beforeEach(() => {
            const project = manager.projects.create("/test/project");
            projectId = project.id;
        });
        it("creates a scan", () => {
            const scan = manager.scans.create(projectId);
            expect(scan.id).toBeDefined();
            expect(scan.projectId).toBe(projectId);
            expect(scan.status).toBe("running");
        });
        it("completes a scan", () => {
            const scan = manager.scans.create(projectId);
            manager.scans.complete(scan.id, {
                totalFindings: 5,
                newFindings: 3,
                fixedFindings: 1,
                bySeverity: { critical: 1, high: 2, medium: 2, low: 0, info: 0 },
                byScanner: { semgrep: 4, gitleaks: 1 },
                duration: 5000,
            });
            const fetched = manager.scans.findById(scan.id);
            expect(fetched?.status).toBe("completed");
            expect(fetched?.totalFindings).toBe(5);
            expect(fetched?.completedAt).toBeDefined();
        });
        it("fails a scan", () => {
            const scan = manager.scans.create(projectId);
            manager.scans.fail(scan.id, "Scanner crashed");
            const fetched = manager.scans.findById(scan.id);
            expect(fetched?.status).toBe("failed");
            expect(fetched?.error).toBe("Scanner crashed");
        });
        it("lists project scans", () => {
            manager.scans.create(projectId);
            manager.scans.create(projectId);
            manager.scans.create(projectId);
            const scans = manager.scans.findByProject(projectId);
            expect(scans.length).toBe(3);
        });
    });
    describe("Trends Repository", () => {
        let projectId;
        beforeEach(() => {
            const project = manager.projects.create("/test/project");
            projectId = project.id;
            manager.findings.create({
                projectId,
                severity: "high",
                category: "xss",
                file: "src/ui.ts",
                line: 10,
                description: "XSS",
                scannerSource: "semgrep",
                status: "open",
                confidence: 90,
            });
        });
        it("calculates trends", () => {
            const trends = manager.trends.calculateTrends(projectId, "day", 7);
            expect(Array.isArray(trends)).toBe(true);
        });
        it("gets category breakdown", () => {
            const breakdown = manager.trends.getCategoryBreakdown(projectId);
            expect(breakdown.length).toBeGreaterThan(0);
            expect(breakdown[0]).toHaveProperty("category");
            expect(breakdown[0]).toHaveProperty("count");
            expect(breakdown[0]).toHaveProperty("percentage");
        });
        it("gets scanner effectiveness", () => {
            const effectiveness = manager.trends.getScannerEffectiveness(projectId);
            expect(effectiveness.length).toBeGreaterThan(0);
            expect(effectiveness[0]).toHaveProperty("scanner");
            expect(effectiveness[0]).toHaveProperty("found");
            expect(effectiveness[0]).toHaveProperty("accuracy");
        });
    });
    describe("End-to-End Workflow", () => {
        it("complete scan workflow with in-memory database", () => {
            const localManager = PersistenceManager.createInMemory();
            const project = localManager.projects.create("/test/project", "Test Project");
            const scan = localManager.scans.create(project.id);
            const { finding: finding1, isNew: isNew1 } = localManager.findings.upsertFromScan({
                projectId: project.id,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
            });
            const { finding: finding2, isNew: isNew2 } = localManager.findings.upsertFromScan({
                projectId: project.id,
                severity: "medium",
                category: "xss",
                file: "src/ui.ts",
                line: 100,
                description: "XSS",
                scannerSource: "semgrep",
                confidence: 85,
            });
            expect(isNew1).toBe(true);
            expect(isNew2).toBe(true);
            localManager.scans.complete(scan.id, {
                totalFindings: 2,
                newFindings: 2,
                fixedFindings: 0,
                bySeverity: { critical: 0, high: 1, medium: 1, low: 0, info: 0 },
                byScanner: { semgrep: 2 },
                duration: 1000,
            });
            const completedScan = localManager.scans.findById(scan.id);
            expect(completedScan?.status).toBe("completed");
            expect(completedScan?.totalFindings).toBe(2);
            const openFindings = localManager.findings.find({ projectId: project.id, status: "open" });
            expect(openFindings.length).toBe(2);
            localManager.findings.updateStatus(finding1.id, "fixed", "developer");
            const afterFix = localManager.findings.find({ projectId: project.id, status: "open" });
            expect(afterFix.length).toBe(1);
            const stats = localManager.findings.getStats(project.id);
            expect(stats.openFindings).toBe(1);
            expect(stats.fixedFindings).toBe(1);
            localManager.close();
        });
        it("tracks finding deduplication across scans", () => {
            const localManager = PersistenceManager.createInMemory();
            const project = localManager.projects.create("/test/project2");
            const first = localManager.findings.upsertFromScan({
                projectId: project.id,
                severity: "critical",
                category: "secrets",
                file: "src/config.ts",
                line: 5,
                description: "Hardcoded secret",
                scannerSource: "gitleaks",
                confidence: 100,
            });
            expect(first.isNew).toBe(true);
            const second = localManager.findings.upsertFromScan({
                projectId: project.id,
                severity: "critical",
                category: "secrets",
                file: "src/config.ts",
                line: 5,
                description: "Hardcoded secret",
                scannerSource: "gitleaks",
                confidence: 100,
            });
            expect(second.isNew).toBe(false);
            expect(second.finding.id).toBe(first.finding.id);
            const allFindings = localManager.findings.find({ projectId: project.id });
            expect(allFindings.length).toBe(1);
            localManager.close();
        });
        it("calculates project trends", () => {
            const localManager = PersistenceManager.createInMemory();
            const project = localManager.projects.create("/test/project3");
            for (let i = 0; i < 5; i++) {
                localManager.findings.create({
                    projectId: project.id,
                    severity: i < 2 ? "critical" : "high",
                    category: "security",
                    file: `src/file${i}.ts`,
                    line: i * 10,
                    description: `Finding ${i}`,
                    scannerSource: "semgrep",
                    status: "open",
                    confidence: 90,
                });
            }
            const trends = localManager.trends.calculateTrends(project.id, "day", 7);
            expect(Array.isArray(trends)).toBe(true);
            const breakdown = localManager.trends.getCategoryBreakdown(project.id);
            expect(breakdown.length).toBeGreaterThan(0);
            expect(breakdown[0].category).toBe("security");
            expect(breakdown[0].count).toBe(5);
            localManager.close();
        });
    });
});
//# sourceMappingURL=persistence.test.js.map