dbx/tests/sqlCompletion.test.ts

119 lines
3.5 KiB
TypeScript

import { strict as assert } from "node:assert";
import test from "node:test";
import {
buildSqlCompletionItems,
type SqlCompletionColumn,
type SqlCompletionTable,
} from "../src/lib/sqlCompletion.ts";
const tables: SqlCompletionTable[] = [
{ name: "users", schema: "public", type: "table" },
{ name: "user_profiles", schema: "public", type: "table" },
{ name: "orders", schema: "public", type: "table" },
];
const columnsByTable = new Map<string, SqlCompletionColumn[]>([
["public.users", [
{ name: "id", table: "users", schema: "public", dataType: "bigint" },
{ name: "name", table: "users", schema: "public", dataType: "varchar" },
{ name: "email", table: "users", schema: "public", dataType: "varchar" },
]],
["public.orders", [
{ name: "id", table: "orders", schema: "public", dataType: "bigint" },
{ name: "user_id", table: "orders", schema: "public", dataType: "bigint" },
{ name: "status", table: "orders", schema: "public", dataType: "varchar" },
]],
]);
test("suggests SQL keywords for generic keyword input", () => {
const items = buildSqlCompletionItems("sel", 3, {
tables,
columnsByTable,
});
assert.equal(items[0]?.label, "SELECT");
assert.equal(items[0]?.type, "keyword");
});
test("suggests matching table names after FROM", () => {
const sql = "select * from us";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.deepEqual(
items.slice(0, 2).map((item) => item.label),
["users", "user_profiles"],
);
});
test("suggests columns for an explicit alias qualifier", () => {
const sql = "select u. from public.users u";
const cursor = "select u.".length;
const items = buildSqlCompletionItems(sql, cursor, {
tables,
columnsByTable,
});
const columnItems = items.filter((item) => item.type === "column");
assert.deepEqual(
columnItems.map((item) => item.label),
["id", "name", "email"],
);
});
test("suggests columns from referenced tables in select list", () => {
const sql = "select na from public.users u join public.orders o on u.id = o.user_id";
const cursor = "select na".length;
const items = buildSqlCompletionItems(sql, cursor, {
tables,
columnsByTable,
});
assert.equal(items[0]?.label, "name");
assert.equal(items[0]?.type, "column");
});
test("suggests tables after LEFT JOIN", () => {
const sql = "select * from users left join us";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.ok(items.some((item) => item.label === "users" && item.type === "table"));
assert.ok(items.some((item) => item.label === "user_profiles" && item.type === "table"));
});
test("suggests tables after comma in FROM clause", () => {
const sql = "select * from users, or";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.ok(items.some((item) => item.label === "orders" && item.type === "table"));
});
test("suggests keywords when typing without context", () => {
const sql = "us";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.ok(items.some((item) => item.type === "keyword" && item.label === "USING"));
});
test("always includes keywords alongside table suggestions", () => {
const sql = "select * from us";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.ok(items.some((item) => item.type === "table"));
assert.ok(items.some((item) => item.type === "keyword" && item.label === "USING"));
});