119 lines
3.5 KiB
TypeScript
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"));
|
|
});
|