/**
 * JSON Fallback Storage Tests
 */
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtemp, rm, readFile } from "fs/promises";
import { existsSync } from "fs";
import { join } from "path";
import { tmpdir } from "os";
import { JsonFallbackStorage } from "../json-fallback.js";
describe("JSON Fallback Storage", () => {
    let tempDir;
    let storage;
    beforeEach(async () => {
        tempDir = await mkdtemp(join(tmpdir(), "fallback-test-"));
        storage = await JsonFallbackStorage.create(tempDir);
    });
    afterEach(async () => {
        storage.close();
        await rm(tempDir, { recursive: true, force: true });
    });
    describe("Projects", () => {
        it("creates and retrieves a project", () => {
            const project = storage.findOrCreateProject("/test/project");
            expect(project.id).toBeDefined();
            expect(project.path).toBe("/test/project");
            expect(project.name).toBe("project");
        });
        it("returns existing project on second call", () => {
            const first = storage.findOrCreateProject("/test/project");
            const second = storage.findOrCreateProject("/test/project");
            expect(first.id).toBe(second.id);
        });
        it("finds project by path", () => {
            storage.findOrCreateProject("/test/project");
            const found = storage.findProjectByPath("/test/project");
            expect(found).toBeDefined();
            expect(found?.path).toBe("/test/project");
        });
    });
    describe("Findings", () => {
        it("creates and retrieves a finding", () => {
            const project = storage.findOrCreateProject("/test");
            const finding = storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
                status: "open",
            });
            expect(finding.id).toBeDefined();
            expect(finding.severity).toBe("high");
            expect(finding.firstSeenAt).toBeDefined();
        });
        it("upserts findings correctly", () => {
            const project = storage.findOrCreateProject("/test");
            const { finding: first, isNew: isNew1 } = storage.upsertFinding({
                projectId: project.id,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
            });
            const { finding: second, isNew: isNew2 } = storage.upsertFinding({
                projectId: project.id,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
            });
            expect(isNew1).toBe(true);
            expect(isNew2).toBe(false);
            expect(first.id).toBe(second.id);
        });
        it("filters findings correctly", () => {
            const project = storage.findOrCreateProject("/test");
            storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
                status: "open",
            });
            storage.createFinding({
                projectId: project.id,
                severity: "low",
                category: "style",
                file: "src/utils.ts",
                line: 10,
                description: "Style issue",
                scannerSource: "eslint",
                confidence: 100,
                status: "open",
            });
            const highSeverity = storage.findFindings({ severity: "high" });
            expect(highSeverity).toHaveLength(1);
            const allOpen = storage.findFindings({ status: "open" });
            expect(allOpen).toHaveLength(2);
        });
        it("marks findings as fixed", () => {
            const project = storage.findOrCreateProject("/test");
            const finding = storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "test",
                file: "test.ts",
                line: 1,
                description: "Test",
                scannerSource: "semgrep",
                confidence: 100,
                status: "open",
            });
            const count = storage.markFixed([finding.id], "user@test.com");
            expect(count).toBe(1);
            const updated = storage.findById(finding.id);
            expect(updated?.status).toBe("fixed");
            expect(updated?.fixedBy).toBe("user@test.com");
        });
        it("marks findings as false positive", () => {
            const project = storage.findOrCreateProject("/test");
            const finding = storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "test",
                file: "test.ts",
                line: 1,
                description: "Test",
                scannerSource: "semgrep",
                confidence: 100,
                status: "open",
            });
            const result = storage.markFalsePositive(finding.id, "Not a real issue");
            expect(result).toBe(true);
            const updated = storage.findById(finding.id);
            expect(updated?.status).toBe("false_positive");
            expect(updated?.falsePositive).toBe(true);
            expect(updated?.falsePositiveReason).toBe("Not a real issue");
        });
    });
    describe("Stats", () => {
        it("calculates project stats", () => {
            const project = storage.findOrCreateProject("/test");
            storage.createFinding({
                projectId: project.id,
                severity: "critical",
                category: "sql-injection",
                file: "src/api.ts",
                line: 42,
                description: "SQL injection",
                scannerSource: "semgrep",
                confidence: 95,
                status: "open",
            });
            storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "xss",
                file: "src/view.ts",
                line: 10,
                description: "XSS",
                scannerSource: "semgrep",
                confidence: 90,
                status: "open",
            });
            const stats = storage.getStats(project.id);
            expect(stats.totalFindings).toBe(2);
            expect(stats.openFindings).toBe(2);
            expect(stats.bySeverity.critical).toBe(1);
            expect(stats.bySeverity.high).toBe(1);
        });
    });
    describe("Persistence", () => {
        it("saves and loads data", async () => {
            const project = storage.findOrCreateProject("/test");
            storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "test",
                file: "test.ts",
                line: 1,
                description: "Test finding",
                scannerSource: "semgrep",
                confidence: 100,
                status: "open",
            });
            await storage.save();
            const filePath = join(tempDir, ".vaspera", "findings-fallback.json");
            expect(existsSync(filePath)).toBe(true);
            const content = await readFile(filePath, "utf-8");
            const data = JSON.parse(content);
            expect(data.version).toBe(1);
            expect(Object.keys(data.projects)).toHaveLength(1);
            expect(Object.keys(data.findings)).toHaveLength(1);
        });
        it("loads existing data on create", async () => {
            const project = storage.findOrCreateProject("/test");
            storage.createFinding({
                projectId: project.id,
                severity: "high",
                category: "test",
                file: "test.ts",
                line: 1,
                description: "Test finding",
                scannerSource: "semgrep",
                confidence: 100,
                status: "open",
            });
            await storage.save();
            storage.close();
            const newStorage = await JsonFallbackStorage.create(tempDir);
            const findings = newStorage.findFindings({ status: "open" });
            expect(findings).toHaveLength(1);
            expect(findings[0].description).toBe("Test finding");
            newStorage.close();
        });
    });
    describe("Scans", () => {
        it("creates and completes scans", () => {
            const project = storage.findOrCreateProject("/test");
            const scan = storage.createScan(project.id, "cert-001");
            expect(scan.id).toBeDefined();
            expect(scan.status).toBe("running");
            storage.completeScan(scan.id, {
                totalFindings: 5,
                newFindings: 3,
                fixedFindings: 0,
                bySeverity: { critical: 1, high: 2, medium: 2, low: 0, info: 0 },
                byScanner: { semgrep: 5 },
                duration: 1000,
            });
            const stats = storage.getStats(project.id);
            expect(stats.lastScanAt).toBeDefined();
        });
    });
});
//# sourceMappingURL=json-fallback.test.js.map