import { describe, expect, it } from "vitest"; import { assertSafeSelect, buildWhereClause, quoteIdentifier, resolveDatabaseName, validateProcedureDefinition } from "../src/sql.js"; describe("SQL helper safety", () => { it("quotes SQL Server identifiers and escapes closing brackets", () => { expect(quoteIdentifier("dbo")).toBe("[dbo]"); expect(quoteIdentifier("stock]audit")).toBe("[stock]]audit]"); }); it("rejects empty identifiers", () => { expect(() => quoteIdentifier("")).toThrow(/identifier/i); }); it("requires safe single-statement SELECT queries", () => { expect(() => assertSafeSelect("select top 1 * from dbo.Users")).not.toThrow(); expect(() => assertSafeSelect("with cte as (select 1 as id) select * from cte")).not.toThrow(); expect(() => assertSafeSelect("update dbo.Users set name = 'x'")).toThrow(/select/i); expect(() => assertSafeSelect("select * from dbo.Users; drop table dbo.Users")).toThrow(/single/i); }); it("builds parameterized WHERE clauses from object criteria", () => { const where = buildWhereClause({ id: 1, status: "pending" }, "w"); expect(where.clause).toBe("[id] = @w0 AND [status] = @w1"); expect(where.params).toEqual([ { name: "w0", value: 1 }, { name: "w1", value: "pending" } ]); }); it("rejects empty WHERE criteria", () => { expect(() => buildWhereClause({}, "w")).toThrow(/where/i); }); it("restricts database selection to the configured allowlist", () => { expect(resolveDatabaseName(undefined, "MainDb", ["MainDb", "ReportDb"])).toBe("MainDb"); expect(resolveDatabaseName("ReportDb", "MainDb", ["MainDb", "ReportDb"])).toBe("ReportDb"); expect(() => resolveDatabaseName("OtherDb", "MainDb", ["MainDb", "ReportDb"])).toThrow(/allowed/i); }); it("validates stored procedure definitions before execution", () => { expect(() => validateProcedureDefinition( "CREATE OR ALTER PROCEDURE [dbo].[p_test] AS BEGIN SELECT 1 END", "dbo", "p_test" ) ).not.toThrow(); expect(() => validateProcedureDefinition("SELECT 1", "dbo", "p_test")).toThrow(/procedure/i); expect(() => validateProcedureDefinition( "CREATE OR ALTER PROCEDURE [dbo].[other_proc] AS BEGIN SELECT 1 END", "dbo", "p_test" ) ).toThrow(/name/i); }); });