dbx/packages/app-tests/sqlSemanticDiagnostics.test.ts

96 lines
3.0 KiB
TypeScript

import { strict as assert } from "node:assert";
import test from "node:test";
import {
buildSqlParserErrorDiagnostic,
buildSqlSemanticDiagnostics,
areSqlSemanticDiagnosticsEqual,
shouldRunSqlSemanticDiagnostics,
} from "../../apps/desktop/src/lib/sqlSemanticDiagnostics.ts";
import type { SqlReferenceAnalysis } from "../../apps/desktop/src/types/database.ts";
const span = (startColumn: number, endColumn: number) => ({
start_line: 1,
start_column: startColumn,
end_line: 1,
end_column: endColumn,
});
test("flags missing qualified columns against the referenced table", () => {
const analysis: SqlReferenceAnalysis = {
tables: [{ name: "users", alias: "u", span: span(23, 27) }],
columns: [
{ name: "missing", qualifier: "u", span: span(10, 16) },
{ name: "id", qualifier: "u", span: span(34, 35) },
],
};
const diagnostics = buildSqlSemanticDiagnostics(analysis, {
tables: [{ name: "users", type: "table" }],
columnsByTable: new Map([["users", [{ name: "id", table: "users" }]]]),
});
assert.deepEqual(
diagnostics.map((diagnostic) => diagnostic.message),
["Unknown column u.missing"],
);
});
test("does not flag unqualified columns when multiple tables make ownership ambiguous", () => {
const analysis: SqlReferenceAnalysis = {
tables: [
{ name: "users", alias: "u", span: span(25, 29) },
{ name: "orders", alias: "o", span: span(35, 40) },
],
columns: [{ name: "id", span: span(8, 9) }],
};
const diagnostics = buildSqlSemanticDiagnostics(analysis, {
tables: [
{ name: "users", type: "table" },
{ name: "orders", type: "table" },
],
columnsByTable: new Map(),
});
assert.deepEqual(diagnostics, []);
});
test("builds a syntax diagnostic from parser errors with line and column", () => {
const diagnostic = buildSqlParserErrorDiagnostic(
"Expected: end of statement, found: FOM at Line: 1, Column: 10",
"SELECT * FOM",
);
assert.equal(diagnostic?.message, "Expected: end of statement, found: FOM at Line: 1, Column: 10");
assert.deepEqual(diagnostic?.span, span(10, 12));
});
test("compares diagnostics by severity message and span", () => {
const diagnostics = [
{
message: "Unknown column u.missing",
severity: "warning" as const,
span: span(10, 16),
},
];
assert.equal(
areSqlSemanticDiagnosticsEqual(
diagnostics,
diagnostics.map((item) => ({ ...item })),
),
true,
);
assert.equal(
areSqlSemanticDiagnosticsEqual(diagnostics, [{ ...diagnostics[0], message: "Unknown column u.name" }]),
false,
);
});
test("defers diagnostics while the cursor is in table completion context", () => {
assert.equal(shouldRunSqlSemanticDiagnostics("select * from ", "select * from ".length), false);
assert.equal(shouldRunSqlSemanticDiagnostics("select * from us", "select * from us".length), false);
assert.equal(shouldRunSqlSemanticDiagnostics("select u.", "select u.".length), false);
assert.equal(shouldRunSqlSemanticDiagnostics("select * from users where missing = 1", 42), true);
});