/**
 * Unit tests for dependency vulnerability scanner
 */
import { describe, it, expect, beforeEach, afterEach, afterAll } from "vitest";
import { mkdir, rm, writeFile } from "fs/promises";
import { join } from "path";
import { tmpdir } from "os";
import { runDependencyAudit, checkNpmAvailable } from "./dependencies.js";
const TEST_BASE = join(tmpdir(), "vaspera-deps-test-" + Date.now() + "-" + Math.random().toString(36).slice(2));
let testProjectPath;
beforeEach(async () => {
    testProjectPath = join(TEST_BASE, "project-" + Math.random().toString(36).slice(2));
    await mkdir(testProjectPath, { recursive: true });
});
afterEach(async () => {
    try {
        await rm(testProjectPath, { recursive: true, force: true });
    }
    catch {
        // Ignore cleanup errors
    }
});
afterAll(async () => {
    try {
        await rm(TEST_BASE, { recursive: true, force: true });
    }
    catch {
        // Ignore cleanup errors
    }
});
describe("checkNpmAvailable", () => {
    it("returns npm availability status", async () => {
        const result = await checkNpmAvailable();
        expect(result).toHaveProperty("available");
        expect(typeof result.available).toBe("boolean");
        if (result.available) {
            expect(result.version).toBeDefined();
            expect(result.version).toMatch(/^\d+\.\d+\.\d+/);
        }
    });
    it("returns version when npm is available", async () => {
        const result = await checkNpmAvailable();
        // npm should be available in the test environment
        expect(result.available).toBe(true);
        expect(result.version).toBeDefined();
    });
});
describe("runDependencyAudit", () => {
    it("returns scanner result structure", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({ name: "test", version: "1.0.0" }));
        const result = await runDependencyAudit(testProjectPath);
        expect(result).toHaveProperty("scanner", "npm-audit");
        expect(result).toHaveProperty("findings");
        expect(result).toHaveProperty("duration");
        expect(result).toHaveProperty("success");
        expect(Array.isArray(result.findings)).toBe(true);
        expect(typeof result.duration).toBe("number");
    });
    it("skips when no package.json exists", async () => {
        // Don't create package.json
        const result = await runDependencyAudit(testProjectPath);
        expect(result.scanner).toBe("npm-audit");
        expect(result.success).toBe(true);
        expect(result.findings).toHaveLength(0);
        expect(result.error).toContain("No package.json found");
    });
    it("handles project with no vulnerabilities", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "clean-project",
            version: "1.0.0",
            dependencies: {},
        }));
        const result = await runDependencyAudit(testProjectPath);
        expect(result.scanner).toBe("npm-audit");
        // May succeed or fail depending on npm audit requirements
        // but should not throw
    });
    it("all findings have confidence 100", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "test",
            version: "1.0.0",
            dependencies: {},
        }));
        const result = await runDependencyAudit(testProjectPath);
        for (const finding of result.findings) {
            expect(finding.confidence).toBe(100);
        }
    });
    it("findings include package metadata", async () => {
        // Create a minimal package.json
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "test",
            version: "1.0.0",
        }));
        const result = await runDependencyAudit(testProjectPath);
        // If there are findings, they should have proper structure
        for (const finding of result.findings) {
            expect(finding.scanner).toBe("npm-audit");
            expect(finding.ruleId).toMatch(/^npm-audit:/);
            expect(finding.file).toBe("package.json");
            expect(finding.line).toBe(1);
            expect(finding.message).toBeDefined();
            expect(finding.severity).toMatch(/^(critical|high|medium|low|info)$/);
        }
    });
    it("tracks scan duration", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({ name: "test", version: "1.0.0" }));
        const result = await runDependencyAudit(testProjectPath);
        expect(result.duration).toBeGreaterThanOrEqual(0);
    });
});
describe("runDependencyAudit - severity mapping", () => {
    it("maps npm severities correctly in findings", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "test",
            version: "1.0.0",
        }));
        const result = await runDependencyAudit(testProjectPath);
        // All findings should have valid vaspera severity
        for (const finding of result.findings) {
            expect(["critical", "high", "medium", "low", "info"]).toContain(finding.severity);
        }
    });
});
describe("runDependencyAudit - fix availability", () => {
    it("reports fix availability when present", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "test",
            version: "1.0.0",
        }));
        const result = await runDependencyAudit(testProjectPath);
        // Check that fixAvailable is a boolean if present
        for (const finding of result.findings) {
            if (finding.fixAvailable !== undefined) {
                expect(typeof finding.fixAvailable).toBe("boolean");
            }
            if (finding.fix !== undefined) {
                expect(typeof finding.fix).toBe("string");
            }
        }
    });
});
describe("runDependencyAudit - CVE/CWE extraction", () => {
    it("extracts CVE and CWE IDs when available", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({
            name: "test",
            version: "1.0.0",
        }));
        const result = await runDependencyAudit(testProjectPath);
        // Check proper format of IDs if present
        for (const finding of result.findings) {
            if (finding.cveIds) {
                expect(Array.isArray(finding.cveIds)).toBe(true);
                for (const cve of finding.cveIds) {
                    expect(cve).toMatch(/^CVE-\d{4}-\d+$/);
                }
            }
            if (finding.cweIds) {
                expect(Array.isArray(finding.cweIds)).toBe(true);
                for (const cwe of finding.cweIds) {
                    expect(cwe).toMatch(/^CWE-\d+$/);
                }
            }
        }
    });
});
describe("runDependencyAudit - error handling", () => {
    it("handles npm audit errors gracefully", async () => {
        // Create an invalid package.json
        await writeFile(join(testProjectPath, "package.json"), "not valid json{");
        const result = await runDependencyAudit(testProjectPath);
        // Should not throw, but may have success: false
        expect(result).toHaveProperty("scanner", "npm-audit");
        expect(result).toHaveProperty("findings");
    });
    it("includes error message on failure", async () => {
        await writeFile(join(testProjectPath, "package.json"), JSON.stringify({ name: "test" }));
        const result = await runDependencyAudit(testProjectPath);
        // If it fails, there should be an error message
        if (!result.success) {
            expect(result.error).toBeDefined();
        }
    });
});
//# sourceMappingURL=dependencies.test.js.map