/**
 * Unit tests for otel module
 */
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { isOtelEnabled, getOtlpEndpoint, initializeOtel, getMetrics, getTracer, getInMemoryStats, resetInMemoryStats, } from "./otel.js";
describe("isOtelEnabled", () => {
    const originalEnv = process.env.VASPERA_OTEL_ENABLED;
    afterEach(() => {
        if (originalEnv === undefined) {
            delete process.env.VASPERA_OTEL_ENABLED;
        }
        else {
            process.env.VASPERA_OTEL_ENABLED = originalEnv;
        }
    });
    it("returns false when env var is not set", () => {
        delete process.env.VASPERA_OTEL_ENABLED;
        expect(isOtelEnabled()).toBe(false);
    });
    it("returns false when env var is 'false'", () => {
        process.env.VASPERA_OTEL_ENABLED = "false";
        expect(isOtelEnabled()).toBe(false);
    });
    it("returns true when env var is 'true'", () => {
        process.env.VASPERA_OTEL_ENABLED = "true";
        expect(isOtelEnabled()).toBe(true);
    });
    it("returns false for any other value", () => {
        process.env.VASPERA_OTEL_ENABLED = "1";
        expect(isOtelEnabled()).toBe(false);
        process.env.VASPERA_OTEL_ENABLED = "yes";
        expect(isOtelEnabled()).toBe(false);
    });
});
describe("getOtlpEndpoint", () => {
    const originalEnv = process.env.VASPERA_OTEL_ENDPOINT;
    afterEach(() => {
        if (originalEnv === undefined) {
            delete process.env.VASPERA_OTEL_ENDPOINT;
        }
        else {
            process.env.VASPERA_OTEL_ENDPOINT = originalEnv;
        }
    });
    it("returns default endpoint when env var is not set", () => {
        delete process.env.VASPERA_OTEL_ENDPOINT;
        expect(getOtlpEndpoint()).toBe("http://localhost:4318");
    });
    it("returns custom endpoint when env var is set", () => {
        process.env.VASPERA_OTEL_ENDPOINT = "http://otel.example.com:4317";
        expect(getOtlpEndpoint()).toBe("http://otel.example.com:4317");
    });
});
describe("initializeOtel", () => {
    const originalEnabled = process.env.VASPERA_OTEL_ENABLED;
    const originalDebug = process.env.VASPERA_OTEL_DEBUG;
    afterEach(() => {
        if (originalEnabled === undefined) {
            delete process.env.VASPERA_OTEL_ENABLED;
        }
        else {
            process.env.VASPERA_OTEL_ENABLED = originalEnabled;
        }
        if (originalDebug === undefined) {
            delete process.env.VASPERA_OTEL_DEBUG;
        }
        else {
            process.env.VASPERA_OTEL_DEBUG = originalDebug;
        }
    });
    it("does nothing when OTEL is disabled", async () => {
        delete process.env.VASPERA_OTEL_ENABLED;
        // Should not throw
        await expect(initializeOtel()).resolves.toBeUndefined();
    });
    it("handles missing OTEL packages gracefully", async () => {
        process.env.VASPERA_OTEL_ENABLED = "true";
        // Should not throw even when packages are missing
        await expect(initializeOtel()).resolves.toBeUndefined();
    });
});
describe("getMetrics", () => {
    beforeEach(() => {
        resetInMemoryStats();
    });
    it("returns a VasperaMetrics object", () => {
        const metrics = getMetrics();
        expect(metrics).toHaveProperty("findingsTotal");
        expect(metrics).toHaveProperty("scanDuration");
        expect(metrics).toHaveProperty("certificationDuration");
        expect(metrics).toHaveProperty("agentRuns");
        expect(metrics).toHaveProperty("circuitBreakerState");
        expect(metrics).toHaveProperty("retryAttempts");
    });
    it("findingsTotal increments in-memory counter", () => {
        const metrics = getMetrics();
        metrics.findingsTotal("high", "semgrep");
        metrics.findingsTotal("high", "semgrep");
        metrics.findingsTotal("medium", "semgrep");
        const stats = getInMemoryStats();
        expect(stats.findings["semgrep:high"]).toBe(2);
        expect(stats.findings["semgrep:medium"]).toBe(1);
    });
    it("scanDuration records scan durations", () => {
        const metrics = getMetrics();
        metrics.scanDuration("semgrep", 1000);
        metrics.scanDuration("gitleaks", 500);
        const stats = getInMemoryStats();
        expect(stats.recentScans.length).toBe(2);
        expect(stats.recentScans[0].scanner).toBe("semgrep");
        expect(stats.recentScans[0].durationMs).toBe(1000);
        expect(stats.recentScans[1].scanner).toBe("gitleaks");
        expect(stats.recentScans[1].durationMs).toBe(500);
    });
    it("agentRuns tracks success/failure by agent type", () => {
        const metrics = getMetrics();
        metrics.agentRuns("security", "success");
        metrics.agentRuns("security", "success");
        metrics.agentRuns("security", "failure");
        metrics.agentRuns("reliability", "success");
        const stats = getInMemoryStats();
        expect(stats.agentRuns["security"]).toEqual({ success: 2, failure: 1 });
        expect(stats.agentRuns["reliability"]).toEqual({ success: 1, failure: 0 });
    });
    it("certificationDuration is callable (no-op)", () => {
        const metrics = getMetrics();
        // Should not throw
        expect(() => metrics.certificationDuration(5000)).not.toThrow();
    });
    it("circuitBreakerState is callable (no-op)", () => {
        const metrics = getMetrics();
        // Should not throw
        expect(() => metrics.circuitBreakerState("api-client", "open")).not.toThrow();
    });
    it("retryAttempts is callable (no-op)", () => {
        const metrics = getMetrics();
        // Should not throw
        expect(() => metrics.retryAttempts("fetch", 3)).not.toThrow();
    });
});
describe("getTracer", () => {
    it("returns a Tracer object", () => {
        const tracer = getTracer();
        expect(tracer).toHaveProperty("startSpan");
        expect(tracer).toHaveProperty("withSpan");
    });
    it("startSpan returns a Span object", () => {
        const tracer = getTracer();
        const span = tracer.startSpan("test-span");
        expect(span).toHaveProperty("setAttribute");
        expect(span).toHaveProperty("recordException");
        expect(span).toHaveProperty("end");
        expect(span).toHaveProperty("getContext");
    });
    it("span has valid context with traceId and spanId", () => {
        const tracer = getTracer();
        const span = tracer.startSpan("test-span");
        const context = span.getContext();
        expect(context.traceId).toBeDefined();
        expect(context.spanId).toBeDefined();
        expect(context.traceId).toHaveLength(32);
        expect(context.spanId).toHaveLength(16);
    });
    it("span methods do not throw", () => {
        const tracer = getTracer();
        const span = tracer.startSpan("test-span");
        expect(() => span.setAttribute("key", "value")).not.toThrow();
        expect(() => span.setAttribute("count", 42)).not.toThrow();
        expect(() => span.setAttribute("enabled", true)).not.toThrow();
        expect(() => span.recordException(new Error("test error"))).not.toThrow();
        expect(() => span.end()).not.toThrow();
    });
    it("withSpan executes the callback and returns result", async () => {
        const tracer = getTracer();
        const result = await tracer.withSpan("test-operation", async (span) => {
            span.setAttribute("step", "processing");
            return "success";
        });
        expect(result).toBe("success");
    });
    it("withSpan handles errors in callback", async () => {
        const tracer = getTracer();
        await expect(tracer.withSpan("failing-operation", async () => {
            throw new Error("operation failed");
        })).rejects.toThrow("operation failed");
    });
    it("withSpan provides span to callback", async () => {
        const tracer = getTracer();
        let capturedSpan;
        await tracer.withSpan("capture-span", async (span) => {
            capturedSpan = span;
        });
        expect(capturedSpan).toBeDefined();
        expect(capturedSpan).toHaveProperty("setAttribute");
        expect(capturedSpan).toHaveProperty("getContext");
    });
});
describe("getInMemoryStats", () => {
    beforeEach(() => {
        resetInMemoryStats();
    });
    it("returns empty stats initially", () => {
        const stats = getInMemoryStats();
        expect(stats.findings).toEqual({});
        expect(stats.recentScans).toEqual([]);
        expect(stats.agentRuns).toEqual({});
    });
    it("reflects metrics after recording", () => {
        const metrics = getMetrics();
        metrics.findingsTotal("critical", "trivy");
        metrics.scanDuration("trivy", 2000);
        metrics.agentRuns("redteam", "success");
        const stats = getInMemoryStats();
        expect(stats.findings["trivy:critical"]).toBe(1);
        expect(stats.recentScans.length).toBe(1);
        expect(stats.agentRuns["redteam"]).toEqual({ success: 1, failure: 0 });
    });
    it("returns only last 100 scans in recentScans", () => {
        const metrics = getMetrics();
        // Record 150 scans
        for (let i = 0; i < 150; i++) {
            metrics.scanDuration("scanner", i);
        }
        const stats = getInMemoryStats();
        // Should only return last 100
        expect(stats.recentScans.length).toBeLessThanOrEqual(100);
    });
});
describe("resetInMemoryStats", () => {
    it("clears all in-memory metrics", () => {
        const metrics = getMetrics();
        metrics.findingsTotal("high", "semgrep");
        metrics.scanDuration("semgrep", 1000);
        metrics.agentRuns("security", "success");
        resetInMemoryStats();
        const stats = getInMemoryStats();
        expect(stats.findings).toEqual({});
        expect(stats.recentScans).toEqual([]);
        expect(stats.agentRuns).toEqual({});
    });
    it("allows recording new metrics after reset", () => {
        const metrics = getMetrics();
        metrics.findingsTotal("high", "semgrep");
        resetInMemoryStats();
        metrics.findingsTotal("low", "gitleaks");
        const stats = getInMemoryStats();
        expect(stats.findings["gitleaks:low"]).toBe(1);
        expect(stats.findings["semgrep:high"]).toBeUndefined();
    });
});
describe("NoOpSpan", () => {
    it("generates unique trace and span IDs", () => {
        const tracer = getTracer();
        const span1 = tracer.startSpan("span1");
        const span2 = tracer.startSpan("span2");
        const ctx1 = span1.getContext();
        const ctx2 = span2.getContext();
        expect(ctx1.traceId).not.toBe(ctx2.traceId);
        expect(ctx1.spanId).not.toBe(ctx2.spanId);
    });
    it("generates hex format IDs", () => {
        const tracer = getTracer();
        const span = tracer.startSpan("test");
        const ctx = span.getContext();
        expect(ctx.traceId).toMatch(/^[0-9a-f]{32}$/);
        expect(ctx.spanId).toMatch(/^[0-9a-f]{16}$/);
    });
});
//# sourceMappingURL=otel.test.js.map