import { describe, expect, it } from "vitest"; import { buildSqlCompletionItems, getSqlCompletionContext } from "@/lib/sqlCompletion"; describe("sqlCompletion quoted schema qualifiers", () => { it("parses quoted PostgreSQL schema names before a dot", () => { const sql = 'SELECT *\nFROM "order-management".'; const context = getSqlCompletionContext(sql, sql.length); expect(context.qualifier).toBe("order-management"); expect(context.prefix).toBe(""); expect(context.suggestTables).toBe(true); expect(context.exclusiveColumnSuggestions).toBe(false); }); it("suggests tables after a quoted schema qualifier", () => { const sql = 'SELECT *\nFROM "order-management".'; const items = buildSqlCompletionItems(sql, sql.length, { dialect: "postgres", tables: [ { name: "orders", schema: "order-management", type: "table" }, { name: "shipments", schema: "order-management", type: "table" }, ], columnsByTable: new Map(), }); expect(items.some((item) => item.label === "orders" && item.type === "table")).toBe(true); expect(items.some((item) => item.label === "shipments" && item.type === "table")).toBe(true); }); }); describe("sqlCompletion table aliases", () => { it("applies generated aliases to table completions when enabled", () => { const sql = "SELECT * FROM ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), autoAliasTables: true, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items AS oi"); }); it("omits AS from Oracle table alias completions", () => { const sql = "SELECT * FROM ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), databaseType: "oracle", autoAliasTables: true, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items oi"); }); it("keeps plain table completions when generated aliases are disabled", () => { const sql = "SELECT * FROM ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), autoAliasTables: false, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items"); }); it("omits AS from Oracle alias suggestions", () => { const sql = "SELECT * FROM order_items "; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), databaseType: "oracle", }); const alias = items.find((item) => item.type === "snippet" && item.detail === "alias for order_items"); expect(alias?.apply).toBe("oi "); }); it("uses a numbered alias when the generated table alias already exists", () => { const sql = "SELECT * FROM order_items oi JOIN ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), autoAliasTables: true, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items AS oi2"); }); it("applies generated aliases in comma-separated FROM table lists", () => { const sql = "SELECT * FROM users u, ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), autoAliasTables: true, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items AS oi"); }); it("does not apply generated aliases to non-query table completions", () => { const sql = "INSERT INTO ord"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [{ name: "order_items", type: "table" }], columnsByTable: new Map(), autoAliasTables: true, }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items"); }); }); describe("sqlCompletion scoped context classification", () => { it("classifies JOIN table contexts", () => { const sql = "SELECT * FROM users u JOIN "; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("join"); expect(context.suggestTables).toBe(true); expect(context.exclusiveTableSuggestions).toBe(true); }); it("classifies alias-qualified column contexts", () => { const sql = "SELECT * FROM users u WHERE u."; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("alias_column"); expect(context.qualifier).toBe("u"); expect(context.suggestColumns).toBe(true); }); it("classifies unqualified WHERE field input as column context", () => { const sql = "SELECT * FROM A1User WHERE userc"; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("column"); expect(context.prefix).toBe("userc"); expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ name: "A1User" })])); expect(context.suggestColumns).toBe(true); expect(context.suggestRoutines).toBe(false); }); it("classifies CALL routine contexts", () => { const sql = "CALL usp_"; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("exec"); expect(context.suggestRoutines).toBe(true); expect(context.exclusiveRoutineSuggestions).toBe(true); }); it("classifies INSERT column-list contexts", () => { const sql = "INSERT INTO dbo.Users ("; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("column"); expect(context.insertSchema).toBe("dbo"); expect(context.insertTable).toBe("users"); expect(context.exclusiveColumnSuggestions).toBe(true); }); it("classifies UPDATE SET column contexts", () => { const sql = "UPDATE dbo.Users SET "; const context = getSqlCompletionContext(sql, sql.length); expect(context.contextKind).toBe("column"); expect(context.updateTarget).toEqual({ schema: "dbo", table: "Users" }); expect(context.suggestColumns).toBe(true); }); it("extracts statement-local table aliases", () => { const sql = "SELECT * FROM dbo.Users u JOIN Orders AS o ON o.user_id = u.id WHERE u."; const context = getSqlCompletionContext(sql, sql.length); expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ schema: "dbo", name: "Users", alias: "u" }), expect.objectContaining({ name: "Orders", alias: "o" })])); }); it("treats schema-qualified table prefixes in FROM as table completion input", () => { const sql = "SELECT * FROM dws_game_sdk_base.di"; const context = getSqlCompletionContext(sql, sql.length); expect(context.qualifier).toBe("dws_game_sdk_base"); expect(context.prefix).toBe("di"); expect(context.suggestTables).toBe(true); expect(context.exclusiveTableSuggestions).toBe(true); expect(context.suggestColumns).toBe(true); }); it("exposes CTEs as table-like referenced tables", () => { const sql = "WITH recent_orders(id, total) AS (SELECT id, total FROM orders) SELECT * FROM recent_orders ro WHERE ro."; const context = getSqlCompletionContext(sql, sql.length); expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ name: "recent_orders", columns: ["id", "total"] }), expect.objectContaining({ name: "recent_orders", alias: "ro" })])); }); it("extracts subquery aliases and projected columns", () => { const sql = "SELECT * FROM (SELECT id, name AS user_name FROM users) sq WHERE sq."; const context = getSqlCompletionContext(sql, sql.length); expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ name: "sq", alias: "sq", columns: ["id", "user_name"] })])); }); }); describe("sqlCompletion scoped metadata ranking", () => { it("ranks exact and prefix table matches ahead of contains/fuzzy matches", () => { const sql = "SELECT * FROM Temp"; const items = buildSqlCompletionItems(sql, sql.length, { dialect: "sqlserver", tables: [ { name: "ArchiveTempTable", schema: "dbo", type: "table" }, { name: "TempAudit", schema: "dbo", type: "table" }, { name: "Temp", schema: "dbo", type: "table" }, { name: "Template", schema: "dbo", type: "table" }, ], columnsByTable: new Map(), }).filter((item) => item.type === "table"); expect(items.map((item) => item.label).slice(0, 3)).toEqual(["Temp", "Template", "TempAudit"]); expect(items.some((item) => item.label === "ArchiveTempTable")).toBe(true); }); it("keeps large table catalogs bounded", () => { const tables = Array.from({ length: 500 }, (_, index) => ({ name: `TempTable_${String(index).padStart(3, "0")}`, schema: "dbo", type: "table" as const })); const sql = "SELECT * FROM Temp"; const items = buildSqlCompletionItems(sql, sql.length, { dialect: "sqlserver", tables, columnsByTable: new Map() }).filter((item) => item.type === "table"); expect(items.length).toBeLessThanOrEqual(200); expect(items[0]?.label).toBe("TempTable_000"); }); });