Files
FTC25009/tools/mcp/sqlserver-mcp-server/tests/sql.test.ts
2026-06-24 09:13:18 +08:00

66 lines
2.3 KiB
TypeScript

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);
});
});