/**
 * Unit tests for config/flags module
 */
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { tmpdir } from "os";
import { join } from "path";
import { mkdir, rm, writeFile, readFile } from "fs/promises";
import { loadConfig, clearConfigCache, isFeatureEnabled, isAgentEnabled, getAgentWeight, isScannerEnabled, getScannerTimeout, generateSampleConfig, saveConfig, DEFAULT_CONFIG, VasperaConfigSchema, } from "./flags.js";
describe("loadConfig", () => {
    let testDir;
    beforeEach(async () => {
        testDir = join(tmpdir(), `vaspera-flags-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
        await mkdir(testDir, { recursive: true });
        clearConfigCache();
    });
    afterEach(async () => {
        await rm(testDir, { recursive: true, force: true });
        clearConfigCache();
    });
    it("returns default config when no config file exists", async () => {
        const config = await loadConfig(testDir);
        expect(config.version).toBe(1);
        expect(config.features.costTracking).toBe(true);
        expect(config.defaultModel).toBe("claude-sonnet-4-20250514");
    });
    it("loads config from .vaspera/config.yaml", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.yaml"), `version: 1
features:
  costTracking: false
  autofix: true
defaultModel: custom-model`);
        const config = await loadConfig(testDir);
        expect(config.features.costTracking).toBe(false);
        expect(config.features.autofix).toBe(true);
        expect(config.defaultModel).toBe("custom-model");
    });
    it("loads config from .vaspera/config.yml", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.yml"), `version: 1
features:
  otel: true`);
        const config = await loadConfig(testDir);
        expect(config.features.otel).toBe(true);
    });
    it("loads config from .vaspera/config.json", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.json"), JSON.stringify({
            version: 1,
            features: { redteam: true },
            defaultTemperature: 0.5,
        }));
        const config = await loadConfig(testDir);
        expect(config.features.redteam).toBe(true);
        expect(config.defaultTemperature).toBe(0.5);
    });
    it("loads config from .vasperarc", async () => {
        await writeFile(join(testDir, ".vasperarc"), `version: 1
features:
  plugins: false`);
        const config = await loadConfig(testDir);
        expect(config.features.plugins).toBe(false);
    });
    it("loads config from .vasperarc.json", async () => {
        await writeFile(join(testDir, ".vasperarc.json"), JSON.stringify({
            version: 1,
            certification: {
                minConfidence: 80,
            },
        }));
        const config = await loadConfig(testDir);
        expect(config.certification.minConfidence).toBe(80);
    });
    it("prefers .vaspera/config.yaml over other locations", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        // Create multiple config files
        await writeFile(join(configDir, "config.yaml"), `version: 1
defaultModel: yaml-model`);
        await writeFile(join(testDir, ".vasperarc.json"), JSON.stringify({ version: 1, defaultModel: "json-model" }));
        const config = await loadConfig(testDir);
        expect(config.defaultModel).toBe("yaml-model");
    });
    it("caches config for same project path", async () => {
        const config1 = await loadConfig(testDir);
        const config2 = await loadConfig(testDir);
        expect(config1).toBe(config2); // Same object reference
    });
    it("reloads config for different project path", async () => {
        // Create config in testDir
        const configDir1 = join(testDir, ".vaspera");
        await mkdir(configDir1, { recursive: true });
        await writeFile(join(configDir1, "config.yaml"), `version: 1\ndefaultModel: model-a`);
        // Create different config in otherDir
        const otherDir = join(testDir, "other");
        const configDir2 = join(otherDir, ".vaspera");
        await mkdir(configDir2, { recursive: true });
        await writeFile(join(configDir2, "config.yaml"), `version: 1\ndefaultModel: model-b`);
        const config1 = await loadConfig(testDir);
        const config2 = await loadConfig(otherDir);
        expect(config1.defaultModel).toBe("model-a");
        expect(config2.defaultModel).toBe("model-b");
    });
    it("handles invalid YAML gracefully", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.yaml"), "invalid: : yaml");
        // Should fallback to default config
        const config = await loadConfig(testDir);
        expect(config.version).toBe(1);
    });
    it("handles invalid JSON gracefully", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.json"), "{ invalid json }");
        const config = await loadConfig(testDir);
        expect(config.version).toBe(1);
    });
    it("parses agent configurations", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.yaml"), `version: 1
agents:
  security:
    enabled: true
    weight: 0.9
  custom-agent:
    enabled: false
    weight: 0.5`);
        const config = await loadConfig(testDir);
        expect(config.agents.security.enabled).toBe(true);
        expect(config.agents.security.weight).toBe(0.9);
        expect(config.agents["custom-agent"].enabled).toBe(false);
    });
    it("parses scanner configurations", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        await writeFile(join(configDir, "config.yaml"), `version: 1
scanners:
  semgrep:
    enabled: true
    timeout: 180000
  custom-scanner:
    enabled: false`);
        const config = await loadConfig(testDir);
        expect(config.scanners.semgrep.enabled).toBe(true);
        expect(config.scanners.semgrep.timeout).toBe(180000);
        expect(config.scanners["custom-scanner"].enabled).toBe(false);
    });
});
describe("clearConfigCache", () => {
    let testDir;
    beforeEach(async () => {
        testDir = join(tmpdir(), `vaspera-cache-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
        await mkdir(testDir, { recursive: true });
        clearConfigCache();
    });
    afterEach(async () => {
        await rm(testDir, { recursive: true, force: true });
        clearConfigCache();
    });
    it("forces reload on next loadConfig call", async () => {
        const configDir = join(testDir, ".vaspera");
        await mkdir(configDir, { recursive: true });
        // Load initial config
        await writeFile(join(configDir, "config.yaml"), `version: 1\ndefaultModel: model-1`);
        const config1 = await loadConfig(testDir);
        expect(config1.defaultModel).toBe("model-1");
        // Update file and clear cache
        await writeFile(join(configDir, "config.yaml"), `version: 1\ndefaultModel: model-2`);
        clearConfigCache();
        const config2 = await loadConfig(testDir);
        expect(config2.defaultModel).toBe("model-2");
    });
});
describe("isFeatureEnabled", () => {
    it("returns true for enabled features", () => {
        const config = {
            ...DEFAULT_CONFIG,
            features: {
                ...DEFAULT_CONFIG.features,
                costTracking: true,
                autofix: true,
            },
        };
        expect(isFeatureEnabled(config, "costTracking")).toBe(true);
        expect(isFeatureEnabled(config, "autofix")).toBe(true);
    });
    it("returns false for disabled features", () => {
        const config = {
            ...DEFAULT_CONFIG,
            features: {
                ...DEFAULT_CONFIG.features,
                otel: false,
                redteam: false,
            },
        };
        expect(isFeatureEnabled(config, "otel")).toBe(false);
        expect(isFeatureEnabled(config, "redteam")).toBe(false);
    });
    it("uses default when feature is undefined", () => {
        // Create a config where feature is explicitly undefined in the lookup
        const config = {
            ...DEFAULT_CONFIG,
            features: {
                ...DEFAULT_CONFIG.features,
            },
        };
        // The isFeatureEnabled function should return the value from config.features
        // Since we're testing the nullish coalescing, we just verify it works
        expect(isFeatureEnabled(config, "costTracking")).toBe(true);
        expect(isFeatureEnabled(config, "otel")).toBe(false);
    });
});
describe("isAgentEnabled", () => {
    it("returns true when agent is explicitly enabled", () => {
        const config = {
            ...DEFAULT_CONFIG,
            agents: {
                security: { enabled: true, weight: 1.0 },
            },
        };
        expect(isAgentEnabled(config, "security")).toBe(true);
    });
    it("returns false when agent is explicitly disabled", () => {
        const config = {
            ...DEFAULT_CONFIG,
            agents: {
                typesafety: { enabled: false, weight: 0.7 },
            },
        };
        expect(isAgentEnabled(config, "typesafety")).toBe(false);
    });
    it("returns true when agent is not configured (default)", () => {
        const config = {
            ...DEFAULT_CONFIG,
            agents: {},
        };
        expect(isAgentEnabled(config, "unknown-agent")).toBe(true);
    });
});
describe("getAgentWeight", () => {
    it("returns configured weight", () => {
        const config = {
            ...DEFAULT_CONFIG,
            agents: {
                security: { enabled: true, weight: 0.9 },
                reliability: { enabled: true, weight: 0.5 },
            },
        };
        expect(getAgentWeight(config, "security")).toBe(0.9);
        expect(getAgentWeight(config, "reliability")).toBe(0.5);
    });
    it("returns default weight (1.0) for unconfigured agent", () => {
        const config = {
            ...DEFAULT_CONFIG,
            agents: {},
        };
        expect(getAgentWeight(config, "unknown")).toBe(1.0);
    });
});
describe("isScannerEnabled", () => {
    it("returns true when scanner is explicitly enabled", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {
                semgrep: { enabled: true },
            },
        };
        expect(isScannerEnabled(config, "semgrep")).toBe(true);
    });
    it("returns false when scanner is explicitly disabled", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {
                "npm-audit": { enabled: false },
            },
        };
        expect(isScannerEnabled(config, "npm-audit")).toBe(false);
    });
    it("returns true when scanner is not configured (default)", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {},
        };
        expect(isScannerEnabled(config, "unknown-scanner")).toBe(true);
    });
});
describe("getScannerTimeout", () => {
    it("returns configured timeout", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {
                semgrep: { enabled: true, timeout: 180000 },
            },
        };
        expect(getScannerTimeout(config, "semgrep")).toBe(180000);
    });
    it("returns default timeout for unconfigured scanner", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {},
        };
        expect(getScannerTimeout(config, "unknown")).toBe(120000);
    });
    it("accepts custom default timeout", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {},
        };
        expect(getScannerTimeout(config, "unknown", 60000)).toBe(60000);
    });
    it("returns configured timeout even when enabled is set", () => {
        const config = {
            ...DEFAULT_CONFIG,
            scanners: {
                gitleaks: { enabled: false, timeout: 30000 },
            },
        };
        expect(getScannerTimeout(config, "gitleaks")).toBe(30000);
    });
});
describe("generateSampleConfig", () => {
    it("generates valid YAML", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("version: 1");
        expect(sample).toContain("agents:");
        expect(sample).toContain("scanners:");
        expect(sample).toContain("features:");
    });
    it("includes all feature flags", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("multiModel:");
        expect(sample).toContain("costTracking:");
        expect(sample).toContain("autofix:");
        expect(sample).toContain("incrementalScan:");
        expect(sample).toContain("otel:");
        expect(sample).toContain("plugins:");
        expect(sample).toContain("redteam:");
    });
    it("includes agent configurations", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("security:");
        expect(sample).toContain("reliability:");
        expect(sample).toContain("typesafety:");
        expect(sample).toContain("weight:");
    });
    it("includes scanner configurations", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("semgrep:");
        expect(sample).toContain("npm-audit:");
        expect(sample).toContain("gitleaks:");
        expect(sample).toContain("timeout:");
    });
    it("includes certification settings", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("certification:");
        expect(sample).toContain("minConfidence:");
        expect(sample).toContain("requiredAgreement:");
    });
    it("includes cost tracking settings", () => {
        const sample = generateSampleConfig();
        expect(sample).toContain("cost:");
        expect(sample).toContain("maxBudget:");
        expect(sample).toContain("warningThreshold:");
    });
});
describe("saveConfig", () => {
    let testDir;
    beforeEach(async () => {
        testDir = join(tmpdir(), `vaspera-save-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
        await mkdir(join(testDir, ".vaspera"), { recursive: true });
        clearConfigCache();
    });
    afterEach(async () => {
        await rm(testDir, { recursive: true, force: true });
        clearConfigCache();
    });
    it("saves config file", async () => {
        await saveConfig(testDir, DEFAULT_CONFIG);
        const content = await readFile(join(testDir, ".vaspera", "config.yaml"), "utf-8");
        expect(content).toContain("version: 1");
    });
    it("clears config cache after save", async () => {
        // Load initial config to populate cache
        await loadConfig(testDir);
        // Save new config
        await saveConfig(testDir, DEFAULT_CONFIG);
        // Should not use cached config (cache was cleared)
        // This is verified by the fact that saveConfig doesn't throw
    });
});
describe("VasperaConfigSchema", () => {
    it("validates valid config", () => {
        const result = VasperaConfigSchema.safeParse({
            version: 1,
            features: { costTracking: true },
        });
        expect(result.success).toBe(true);
    });
    it("applies default values", () => {
        const result = VasperaConfigSchema.safeParse({});
        expect(result.success).toBe(true);
        if (result.success) {
            expect(result.data.version).toBe(1);
            expect(result.data.features.costTracking).toBe(true);
            expect(result.data.defaultModel).toBe("claude-sonnet-4-20250514");
        }
    });
    it("validates version must be 1", () => {
        const result = VasperaConfigSchema.safeParse({ version: 2 });
        expect(result.success).toBe(false);
    });
    it("validates agent weight range", () => {
        const valid = VasperaConfigSchema.safeParse({
            agents: { security: { weight: 0.5 } },
        });
        expect(valid.success).toBe(true);
        const tooLow = VasperaConfigSchema.safeParse({
            agents: { security: { weight: -0.1 } },
        });
        expect(tooLow.success).toBe(false);
        const tooHigh = VasperaConfigSchema.safeParse({
            agents: { security: { weight: 1.5 } },
        });
        expect(tooHigh.success).toBe(false);
    });
    it("validates temperature range", () => {
        const valid = VasperaConfigSchema.safeParse({
            defaultTemperature: 1.0,
        });
        expect(valid.success).toBe(true);
        const tooHigh = VasperaConfigSchema.safeParse({
            defaultTemperature: 3.0,
        });
        expect(tooHigh.success).toBe(false);
    });
    it("validates certification settings", () => {
        const valid = VasperaConfigSchema.safeParse({
            certification: {
                minConfidence: 85,
                requiredAgreement: 0.7,
            },
        });
        expect(valid.success).toBe(true);
        const invalidConfidence = VasperaConfigSchema.safeParse({
            certification: { minConfidence: 150 },
        });
        expect(invalidConfidence.success).toBe(false);
        const invalidAgreement = VasperaConfigSchema.safeParse({
            certification: { requiredAgreement: 1.5 },
        });
        expect(invalidAgreement.success).toBe(false);
    });
});
describe("DEFAULT_CONFIG", () => {
    it("has all required fields", () => {
        expect(DEFAULT_CONFIG.version).toBe(1);
        expect(DEFAULT_CONFIG.features).toBeDefined();
        expect(DEFAULT_CONFIG.agents).toBeDefined();
        expect(DEFAULT_CONFIG.scanners).toBeDefined();
        expect(DEFAULT_CONFIG.certification).toBeDefined();
        expect(DEFAULT_CONFIG.cost).toBeDefined();
        expect(DEFAULT_CONFIG.plugins).toBeDefined();
    });
    it("has sensible default values", () => {
        expect(DEFAULT_CONFIG.features.costTracking).toBe(true);
        expect(DEFAULT_CONFIG.features.autofix).toBe(true);
        expect(DEFAULT_CONFIG.features.multiModel).toBe(false);
        expect(DEFAULT_CONFIG.features.otel).toBe(false);
        expect(DEFAULT_CONFIG.defaultTemperature).toBe(0.3);
        expect(DEFAULT_CONFIG.certification.minConfidence).toBe(70);
        expect(DEFAULT_CONFIG.certification.requiredAgreement).toBe(0.6);
    });
    it("enables core agents by default", () => {
        expect(DEFAULT_CONFIG.agents.security?.enabled).toBe(true);
        expect(DEFAULT_CONFIG.agents.reliability?.enabled).toBe(true);
        expect(DEFAULT_CONFIG.agents.typesafety?.enabled).toBe(true);
    });
    it("enables core scanners by default", () => {
        expect(DEFAULT_CONFIG.scanners.semgrep?.enabled).toBe(true);
        expect(DEFAULT_CONFIG.scanners["npm-audit"]?.enabled).toBe(true);
        expect(DEFAULT_CONFIG.scanners.gitleaks?.enabled).toBe(true);
    });
});
//# sourceMappingURL=flags.test.js.map