import { DOMParser } from "@xmldom/xmldom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { flattenExplainPlanNodes, parseExplainResult, sqlServerExplainResult, supportsExplainPlan } from "@/lib/diagram/explainPlan"; import type { QueryResult } from "@/types/database"; const SHOWPLAN_XML = ` `; function result(columns: string[], rows: unknown[][]): QueryResult { return { columns, rows, affected_rows: 0, execution_time_ms: 1 }; } describe("SQL Server explain plan", () => { beforeEach(() => { vi.stubGlobal("DOMParser", DOMParser); }); afterEach(() => { vi.unstubAllGlobals(); }); it("is enabled by the driver capability manifest", () => { expect(supportsExplainPlan("sqlserver")).toBe(true); }); it("parses SHOWPLAN XML into its operator hierarchy", () => { const planResult = result(["Microsoft SQL Server 2005 XML Showplan"], [[SHOWPLAN_XML]]); expect(sqlServerExplainResult([result([], []), planResult, result([], [])])).toEqual({ result: planResult }); const plan = parseExplainResult("sqlserver", planResult); const nodes = flattenExplainPlanNodes(plan.nodes); expect(plan.databaseType).toBe("sqlserver"); expect(plan.raw).toBe(SHOWPLAN_XML); expect(nodes).toHaveLength(2); expect(nodes[0]).toMatchObject({ id: "0", nodeType: "Sort", rows: "2", cost: "0.029466" }); expect(nodes[1]).toMatchObject({ id: "1", nodeType: "Index Seek", relation: "dbo.orders", index: "ix_orders_customer_status", rows: "1", cost: "0.0034412", }); expect(nodes[1].details).toContain("Estimated Rows Read: 4"); expect(nodes[1].details).toContain("Expression: [orders].[status]='paid'"); }); it("surfaces a SQL Server batch error instead of treating it as a plan", () => { expect(sqlServerExplainResult([{ ...result(["Error"], [["Invalid object name 'missing_table'"]]), execution_error: true }])).toEqual({ error: "Invalid object name 'missing_table'", }); }); it("does not mistake a successful Error column for a SQL Server batch failure", () => { expect(sqlServerExplainResult([result(["Error"], [["valid query value"]])])).toEqual({ error: "SQL Server did not return ShowPlan XML", }); }); it("rejects malformed SHOWPLAN XML", () => { const malformed = result(["Microsoft SQL Server 2005 XML Showplan"], [[""]]); expect(() => parseExplainResult("sqlserver", malformed)).toThrow("Invalid SQL Server ShowPlan XML"); }); it("rejects truncated SHOWPLAN XML", () => { const truncated = result(["Microsoft SQL Server 2005 XML Showplan"], [[""]]); expect(() => parseExplainResult("sqlserver", truncated)).toThrow("Invalid SQL Server ShowPlan XML"); }); it("rejects XML that is not a SHOWPLAN document", () => { const unrelated = result(["XML"], [[""]]); expect(() => parseExplainResult("sqlserver", unrelated)).toThrow("SQL Server did not return ShowPlan XML"); expect(sqlServerExplainResult([unrelated])).toEqual({ error: "SQL Server did not return ShowPlan XML" }); }); it("rejects SHOWPLAN XML without RelOp nodes", () => { const emptyPlan = result(["Microsoft SQL Server 2005 XML Showplan"], [[``]]); expect(() => parseExplainResult("sqlserver", emptyPlan)).toThrow("SQL Server ShowPlan XML contains no RelOp nodes"); }); });