import { describe, it, expect } from "vitest"; import { createTSQLCompletion } from "~/components/code/tsql/tsqlCompletion"; import type { TableSchema } from "@internal/tsql"; // Helper to create a mock completion context function createMockContext(doc: string, pos: number, explicit = false) { return { state: { doc: { toString: () => doc, sliceString: (from: number, to: number) => doc.slice(from, to), }, }, pos, explicit, matchBefore: (regex: RegExp) => { const beforePos = doc.slice(0, pos); const match = beforePos.match(new RegExp(regex.source + "$")); if (match) { return { from: pos - match[0].length, to: pos, text: match[0], }; } return null; }, } as any; } // Test schema with enum columns const testSchema: TableSchema[] = [ { name: "runs", clickhouseName: "trigger_dev.task_runs_v2", tenantColumns: { organizationId: "organization_id", projectId: "project_id", environmentId: "environment_id", }, description: "Task runs table", columns: { id: { name: "id", type: "String", description: "Run ID" }, status: { name: "status", type: "LowCardinality(String)", description: "Run status", allowedValues: ["Completed", "Failed", "Queued", "Executing"], }, machine: { name: "machine", type: "LowCardinality(String)", description: "Machine preset", allowedValues: ["micro", "small-1x", "small-2x", "medium-1x"], }, environment_type: { name: "environment_type", type: "LowCardinality(String)", description: "Environment type", allowedValues: ["PRODUCTION", "STAGING", "DEVELOPMENT", "PREVIEW"], }, created_at: { name: "created_at", type: "DateTime64", description: "Creation time" }, organization_id: { name: "organization_id", type: "String" }, project_id: { name: "project_id", type: "String" }, environment_id: { name: "environment_id", type: "String" }, }, }, { name: "logs", clickhouseName: "trigger_dev.task_events_v2", tenantColumns: { organizationId: "organization_id", projectId: "project_id", environmentId: "environment_id", }, description: "Task logs table", columns: { id: { name: "id", type: "String" }, run_id: { name: "run_id", type: "String" }, message: { name: "message", type: "String" }, level: { name: "level", type: "String" }, timestamp: { name: "timestamp", type: "DateTime64" }, organization_id: { name: "organization_id", type: "String" }, project_id: { name: "project_id", type: "String" }, environment_id: { name: "environment_id", type: "String" }, }, }, ]; describe("createTSQLCompletion", () => { const completionSource = createTSQLCompletion(testSchema); it("should return null for empty input without explicit trigger", () => { const context = createMockContext("", 0, false); const result = completionSource(context); expect(result).toBeNull(); }); it("should return completions when explicitly triggered", () => { const context = createMockContext("", 0, true); const result = completionSource(context); expect(result).not.toBeNull(); expect(result?.options.length).toBeGreaterThan(0); }); it("should include tables in completions", () => { // When typing after FROM, tables should be available const doc = "SELECT * FROM r"; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const tableLabels = result?.options.map((o) => o.label); // Tables should always be available in completions expect(tableLabels).toContain("runs"); expect(tableLabels).toContain("logs"); }); it("should suggest columns after SELECT keyword", () => { const doc = "SELECT FROM runs"; // Position cursor right after SELECT const pos = 7; const context = createMockContext(doc, pos, true); const result = completionSource(context); expect(result).not.toBeNull(); // Should include functions const labels = result?.options.map((o) => o.label) || []; expect(labels.some((l) => l === "count")).toBe(true); expect(labels.some((l) => l === "sum")).toBe(true); }); it("should suggest columns with table prefix for qualified references", () => { const doc = "SELECT runs. FROM runs"; // Position cursor right after "runs." const pos = 12; const context = createMockContext(doc, pos, true); const result = completionSource(context); expect(result).not.toBeNull(); const columnLabels = result?.options.map((o) => o.label); expect(columnLabels).toContain("id"); expect(columnLabels).toContain("status"); expect(columnLabels).toContain("created_at"); }); it("should include SQL keywords in general context", () => { const doc = "S"; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label); expect(labels).toContain("SELECT"); }); it("should include aggregate functions", () => { const doc = "SELECT "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label); expect(labels).toContain("count"); expect(labels).toContain("sum"); expect(labels).toContain("avg"); expect(labels).toContain("min"); expect(labels).toContain("max"); }); it("should handle WHERE clause context", () => { const doc = "SELECT * FROM runs WHERE "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); // Should suggest columns const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("status"); // Should include conditional keywords expect(labels).toContain("AND"); expect(labels).toContain("OR"); }); describe("enum value completions", () => { it("should suggest enum values after = operator", () => { const doc = "SELECT * FROM runs WHERE status = "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Completed'"); expect(labels).toContain("'Failed'"); expect(labels).toContain("'Queued'"); expect(labels).toContain("'Executing'"); }); it("should suggest enum values after = with opening quote", () => { const doc = "SELECT * FROM runs WHERE status = '"; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Completed'"); expect(labels).toContain("'Failed'"); }); it("should suggest enum values with partial input", () => { const doc = "SELECT * FROM runs WHERE status = 'Comp"; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Completed'"); }); it("should suggest enum values for machine column", () => { const doc = "SELECT * FROM runs WHERE machine = "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'micro'"); expect(labels).toContain("'small-1x'"); expect(labels).toContain("'medium-1x'"); }); it("should suggest enum values for environment_type column", () => { const doc = "SELECT * FROM runs WHERE environment_type = "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'PRODUCTION'"); expect(labels).toContain("'STAGING'"); expect(labels).toContain("'DEVELOPMENT'"); }); it("should suggest enum values after != operator", () => { const doc = "SELECT * FROM runs WHERE status != "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Completed'"); expect(labels).toContain("'Failed'"); }); it("should suggest enum values after IN (", () => { const doc = "SELECT * FROM runs WHERE status IN ("; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Completed'"); expect(labels).toContain("'Failed'"); }); it("should suggest enum values after comma in IN clause", () => { const doc = "SELECT * FROM runs WHERE status IN ('Completed', "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); const labels = result?.options.map((o) => o.label) || []; expect(labels).toContain("'Failed'"); expect(labels).toContain("'Queued'"); }); it("should include closing quote in replacement range when auto-paired", () => { // Simulate cursor between auto-paired quotes: status = '|' const doc = "SELECT * FROM runs WHERE status = ''"; const pos = doc.length - 1; // Cursor is between the quotes const context = createMockContext(doc, pos, true); const result = completionSource(context); expect(result).not.toBeNull(); // The 'to' should extend past the cursor to include the closing quote expect(result?.to).toBe(pos + 1); }); it("should not extend 'to' when no closing quote present", () => { const doc = "SELECT * FROM runs WHERE status = '"; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); // 'to' should be undefined (or not set) since there's no closing quote expect(result?.to).toBeUndefined(); }); it("should not suggest enum values for columns without allowedValues", () => { const doc = "SELECT * FROM runs WHERE id = "; const context = createMockContext(doc, doc.length, true); const result = completionSource(context); expect(result).not.toBeNull(); // Should return empty options for value context with non-enum column const labels = result?.options.map((o) => o.label) || []; expect(labels).not.toContain("'Completed'"); expect(labels.length).toBe(0); }); }); });