/**
 * Tests for Ticketing Integrations
 */
import { describe, it, expect, beforeEach, vi } from "vitest";
import { JiraClient, ServiceNowClient, LinearClient, TicketingClient, createJiraClient, createServiceNowClient, createLinearClient, createTicketingClient, } from "./ticketing.js";
// Mock fetch
const mockFetch = vi.fn();
global.fetch = mockFetch;
describe("JiraClient", () => {
    let client;
    let config;
    beforeEach(() => {
        vi.clearAllMocks();
        config = {
            platform: "jira",
            baseUrl: "https://company.atlassian.net",
            apiToken: "api-token-xxx",
            email: "user@company.com",
            defaultProject: "SEC",
            isCloud: true,
        };
        client = new JiraClient(config);
    });
    describe("createTicket", () => {
        it("creates issue successfully", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    id: "10001",
                    key: "SEC-123",
                }),
            });
            const request = {
                title: "Security Finding: SQL Injection",
                description: "Critical SQL injection vulnerability found",
            };
            const result = await client.createTicket(request);
            expect(result.id).toBe("10001");
            expect(result.key).toBe("SEC-123");
            expect(result.url).toContain("SEC-123");
            expect(result.platform).toBe("jira");
            expect(mockFetch).toHaveBeenCalledWith("https://company.atlassian.net/rest/api/3/issue", expect.objectContaining({
                method: "POST",
                headers: expect.objectContaining({
                    Authorization: expect.stringContaining("Basic"),
                    "Content-Type": "application/json",
                }),
            }));
        });
        it("maps severity from findings to priority", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-124" }),
            });
            await client.createTicket({
                title: "Critical Finding",
                description: "test",
                findings: [
                    {
                        id: "f1",
                        severity: "critical",
                        category: "vuln",
                        description: "Critical issue",
                        evidence: "",
                        confidence: 95,
                        verifications: [],
                        created_at: new Date().toISOString(),
                    },
                ],
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.fields.priority.name).toBe("Highest");
        });
        it("uses specified priority over finding severity", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-125" }),
            });
            await client.createTicket({
                title: "Test",
                description: "test",
                priority: "Low",
                findings: [
                    {
                        id: "f1",
                        severity: "critical",
                        category: "vuln",
                        description: "Critical",
                        evidence: "",
                        confidence: 95,
                        verifications: [],
                        created_at: new Date().toISOString(),
                    },
                ],
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.fields.priority.name).toBe("Low");
        });
        it("uses custom issue type", async () => {
            const customConfig = {
                ...config,
                defaultIssueType: "Security Bug",
            };
            const customClient = new JiraClient(customConfig);
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-126" }),
            });
            await customClient.createTicket({
                title: "Test",
                description: "test",
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.fields.issuetype.name).toBe("Security Bug");
        });
        it("includes labels", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-127" }),
            });
            await client.createTicket({
                title: "Test",
                description: "test",
                labels: ["security", "automated"],
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.fields.labels).toContain("security");
            expect(body.fields.labels).toContain("automated");
        });
        it("handles API errors", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: false,
                status: 400,
                text: async () => "Project not found",
            });
            await expect(client.createTicket({
                title: "Test",
                description: "test",
            })).rejects.toThrow("Jira API error");
        });
    });
    describe("addComment", () => {
        it("adds comment to issue", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({ id: "comment-1" }),
            });
            await client.addComment("SEC-123", "New information about this finding");
            expect(mockFetch).toHaveBeenCalledWith("https://company.atlassian.net/rest/api/3/issue/SEC-123/comment", expect.objectContaining({
                method: "POST",
            }));
        });
    });
    describe("attachFile", () => {
        it("attaches file to issue", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => [{ id: "attachment-1" }],
            });
            await client.attachFile("SEC-123", "report.json", '{"findings": []}');
            expect(mockFetch).toHaveBeenCalledWith("https://company.atlassian.net/rest/api/3/issue/SEC-123/attachments", expect.objectContaining({
                method: "POST",
                headers: expect.objectContaining({
                    "X-Atlassian-Token": "no-check",
                }),
            }));
        });
    });
    describe("searchIssues", () => {
        it("searches issues with JQL", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    issues: [
                        { key: "SEC-1", fields: { summary: "Issue 1" } },
                        { key: "SEC-2", fields: { summary: "Issue 2" } },
                    ],
                }),
            });
            const results = await client.searchIssues('project = SEC AND labels = "vaspera"');
            expect(results).toHaveLength(2);
            expect(results[0].key).toBe("SEC-1");
        });
    });
});
describe("ServiceNowClient", () => {
    let client;
    let config;
    beforeEach(() => {
        vi.clearAllMocks();
        config = {
            platform: "servicenow",
            baseUrl: "https://company.service-now.com",
            apiToken: "oauth-token",
        };
        client = new ServiceNowClient(config);
    });
    describe("createTicket", () => {
        it("creates incident successfully", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    result: {
                        sys_id: "abc123",
                        number: "INC0001234",
                    },
                }),
            });
            const request = {
                title: "Security Incident",
                description: "Vulnerability detected",
            };
            const result = await client.createTicket(request);
            expect(result.id).toBe("abc123");
            expect(result.key).toBe("INC0001234");
            expect(result.url).toContain("abc123");
            expect(result.platform).toBe("servicenow");
            expect(mockFetch).toHaveBeenCalledWith("https://company.service-now.com/api/now/table/incident", expect.objectContaining({
                method: "POST",
                headers: expect.objectContaining({
                    Authorization: expect.stringContaining("Bearer"),
                }),
            }));
        });
        it("maps severity to priority", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({
                    result: { sys_id: "abc", number: "INC001" },
                }),
            });
            await client.createTicket({
                title: "Critical",
                description: "test",
                findings: [
                    {
                        id: "f1",
                        severity: "critical",
                        category: "vuln",
                        description: "Critical",
                        evidence: "",
                        confidence: 95,
                        verifications: [],
                        created_at: new Date().toISOString(),
                    },
                ],
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.priority).toBe("1");
        });
        it("sets assignment group", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    result: { sys_id: "abc", number: "INC001" },
                }),
            });
            await client.createTicket({
                title: "Test",
                description: "test",
                project: "Security Team",
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.assignment_group).toBe("Security Team");
        });
        it("handles API errors", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: false,
                status: 401,
                text: async () => "Invalid credentials",
            });
            await expect(client.createTicket({
                title: "Test",
                description: "test",
            })).rejects.toThrow("ServiceNow API error");
        });
    });
    describe("addWorkNote", () => {
        it("adds work note to incident", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({ result: {} }),
            });
            await client.addWorkNote("abc123", "Investigation update");
            expect(mockFetch).toHaveBeenCalledWith("https://company.service-now.com/api/now/table/incident/abc123", expect.objectContaining({
                method: "PATCH",
            }));
        });
    });
});
describe("LinearClient", () => {
    let client;
    let config;
    beforeEach(() => {
        vi.clearAllMocks();
        config = {
            platform: "linear",
            baseUrl: "https://api.linear.app",
            apiToken: "lin_api_xxxxxxxxxxxxx",
            teamId: "TEAM-123",
        };
        client = new LinearClient(config);
    });
    describe("createTicket", () => {
        it("creates issue via GraphQL", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    data: {
                        issueCreate: {
                            success: true,
                            issue: {
                                id: "issue-abc",
                                identifier: "SEC-42",
                                url: "https://linear.app/company/issue/SEC-42",
                            },
                        },
                    },
                }),
            });
            const request = {
                title: "Security Finding",
                description: "Vulnerability detected",
            };
            const result = await client.createTicket(request);
            expect(result.id).toBe("issue-abc");
            expect(result.key).toBe("SEC-42");
            expect(result.url).toContain("SEC-42");
            expect(result.platform).toBe("linear");
            expect(mockFetch).toHaveBeenCalledWith("https://api.linear.app/graphql", expect.objectContaining({
                method: "POST",
                headers: expect.objectContaining({
                    Authorization: "lin_api_xxxxxxxxxxxxx",
                }),
            }));
        });
        it("maps severity to priority number", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({
                    data: {
                        issueCreate: {
                            success: true,
                            issue: { id: "i", identifier: "SEC-1", url: "url" },
                        },
                    },
                }),
            });
            // Critical -> 0 (urgent)
            await client.createTicket({
                title: "Critical",
                description: "test",
                findings: [
                    {
                        id: "f1",
                        severity: "critical",
                        category: "vuln",
                        description: "Critical",
                        evidence: "",
                        confidence: 95,
                        verifications: [],
                        created_at: new Date().toISOString(),
                    },
                ],
            });
            let body = JSON.parse(mockFetch.mock.calls[0][1].body);
            expect(body.variables.input.priority).toBe(0);
            // Low -> 3
            await client.createTicket({
                title: "Low",
                description: "test",
                findings: [
                    {
                        id: "f2",
                        severity: "low",
                        category: "style",
                        description: "Low",
                        evidence: "",
                        confidence: 80,
                        verifications: [],
                        created_at: new Date().toISOString(),
                    },
                ],
            });
            body = JSON.parse(mockFetch.mock.calls[1][1].body);
            expect(body.variables.input.priority).toBe(3);
        });
        it("handles GraphQL errors", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    errors: [{ message: "Team not found" }],
                }),
            });
            await expect(client.createTicket({
                title: "Test",
                description: "test",
            })).rejects.toThrow("Team not found");
        });
        it("handles unsuccessful creation", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    data: {
                        issueCreate: {
                            success: false,
                        },
                    },
                }),
            });
            await expect(client.createTicket({
                title: "Test",
                description: "test",
            })).rejects.toThrow("Failed to create Linear issue");
        });
    });
    describe("addComment", () => {
        it("adds comment via GraphQL", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    data: {
                        commentCreate: {
                            success: true,
                        },
                    },
                }),
            });
            await client.addComment("issue-abc", "New comment");
            expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
                body: expect.stringContaining("commentCreate"),
            }));
        });
    });
    describe("getTeamId", () => {
        it("finds team by name", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    data: {
                        teams: {
                            nodes: [
                                { id: "team-1", name: "Security" },
                                { id: "team-2", name: "Engineering" },
                            ],
                        },
                    },
                }),
            });
            const teamId = await client.getTeamId("Security");
            expect(teamId).toBe("team-1");
        });
        it("returns null for unknown team", async () => {
            mockFetch.mockResolvedValueOnce({
                ok: true,
                json: async () => ({
                    data: {
                        teams: {
                            nodes: [],
                        },
                    },
                }),
            });
            const teamId = await client.getTeamId("NonExistent");
            expect(teamId).toBeNull();
        });
    });
});
describe("TicketingClient", () => {
    beforeEach(() => {
        vi.clearAllMocks();
        mockFetch.mockResolvedValue({
            ok: true,
            json: async () => ({
                id: "10001",
                key: "SEC-123",
            }),
        });
    });
    describe("constructor", () => {
        it("creates Jira client", () => {
            const client = new TicketingClient({
                platform: "jira",
                baseUrl: "https://company.atlassian.net",
                apiToken: "token",
                isCloud: true,
            });
            expect(client.getPlatform()).toBe("jira");
        });
        it("creates ServiceNow client", () => {
            const client = new TicketingClient({
                platform: "servicenow",
                baseUrl: "https://company.service-now.com",
                apiToken: "token",
            });
            expect(client.getPlatform()).toBe("servicenow");
        });
        it("creates Linear client", () => {
            const client = new TicketingClient({
                platform: "linear",
                baseUrl: "https://api.linear.app",
                apiToken: "lin_api_xxx",
                teamId: "TEAM-1",
            });
            expect(client.getPlatform()).toBe("linear");
        });
        it("throws for unsupported platform", () => {
            expect(() => new TicketingClient({
                platform: "github",
                baseUrl: "https://github.com",
                apiToken: "token",
            })).toThrow("Unsupported ticketing platform");
        });
    });
    describe("createTicket", () => {
        it("delegates to underlying client", async () => {
            const client = new TicketingClient({
                platform: "jira",
                baseUrl: "https://company.atlassian.net",
                apiToken: "token",
                isCloud: true,
            });
            const result = await client.createTicket({
                title: "Test",
                description: "test",
            });
            expect(result.key).toBe("SEC-123");
        });
    });
    describe("createTicketsForFindings", () => {
        it("creates ticket for each finding", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-123" }),
            });
            const client = new TicketingClient({
                platform: "jira",
                baseUrl: "https://company.atlassian.net",
                apiToken: "token",
                isCloud: true,
            });
            const findings = [
                {
                    id: "f1",
                    severity: "critical",
                    category: "vulnerability",
                    description: "SQL injection",
                    evidence: "",
                    confidence: 95,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
                {
                    id: "f2",
                    severity: "high",
                    category: "code-quality",
                    description: "Hardcoded secret",
                    evidence: "",
                    confidence: 90,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
            ];
            const results = await client.createTicketsForFindings(findings);
            expect(results.created).toHaveLength(2);
            expect(mockFetch).toHaveBeenCalledTimes(2);
        });
        it("respects severity filter", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-123" }),
            });
            const client = new TicketingClient({
                platform: "jira",
                baseUrl: "https://company.atlassian.net",
                apiToken: "token",
                isCloud: true,
            });
            const findings = [
                {
                    id: "f1",
                    severity: "critical",
                    category: "vulnerability",
                    description: "Critical issue",
                    evidence: "",
                    confidence: 95,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
                {
                    id: "f2",
                    severity: "low",
                    category: "style",
                    description: "Style issue",
                    evidence: "",
                    confidence: 80,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
            ];
            const results = await client.createTicketsForFindings(findings, {
                minSeverity: "high",
            });
            // Only critical should be created (high and above)
            expect(results.created).toHaveLength(1);
            expect(mockFetch).toHaveBeenCalledTimes(1);
        });
        it("groups findings by category when requested", async () => {
            mockFetch.mockResolvedValue({
                ok: true,
                json: async () => ({ id: "10001", key: "SEC-123" }),
            });
            const client = new TicketingClient({
                platform: "jira",
                baseUrl: "https://company.atlassian.net",
                apiToken: "token",
                isCloud: true,
            });
            const findings = [
                {
                    id: "f1",
                    severity: "high",
                    category: "vulnerability",
                    description: "Issue 1",
                    evidence: "",
                    confidence: 90,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
                {
                    id: "f2",
                    severity: "high",
                    category: "vulnerability",
                    description: "Issue 2",
                    evidence: "",
                    confidence: 85,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
                {
                    id: "f3",
                    severity: "medium",
                    category: "code-quality",
                    description: "Issue 3",
                    evidence: "",
                    confidence: 80,
                    verifications: [],
                    created_at: new Date().toISOString(),
                },
            ];
            const results = await client.createTicketsForFindings(findings, {
                groupByCategory: true,
            });
            // 2 categories: vulnerability and code-quality
            expect(results.created).toHaveLength(2);
            expect(mockFetch).toHaveBeenCalledTimes(2);
        });
    });
});
describe("Factory Functions", () => {
    it("createJiraClient creates Jira client", () => {
        const client = createJiraClient({
            baseUrl: "https://company.atlassian.net",
            apiToken: "token",
            isCloud: true,
        });
        expect(client).toBeInstanceOf(JiraClient);
    });
    it("createServiceNowClient creates ServiceNow client", () => {
        const client = createServiceNowClient({
            baseUrl: "https://company.service-now.com",
            apiToken: "token",
        });
        expect(client).toBeInstanceOf(ServiceNowClient);
    });
    it("createLinearClient creates Linear client", () => {
        const client = createLinearClient({
            baseUrl: "https://api.linear.app",
            apiToken: "lin_api_xxx",
            teamId: "TEAM-1",
        });
        expect(client).toBeInstanceOf(LinearClient);
    });
    it("createTicketingClient creates unified client", () => {
        const client = createTicketingClient({
            platform: "jira",
            baseUrl: "https://company.atlassian.net",
            apiToken: "token",
        });
        expect(client).toBeInstanceOf(TicketingClient);
    });
});
//# sourceMappingURL=ticketing.test.js.map