/**
* Unit tests for Code Mode API
*
* Tests PgApi creation, tool method generation, and sandbox bindings.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { createPgApi, PgApi } from "../api.js";
import type { PostgresAdapter } from "../../adapters/postgresql/PostgresAdapter.js";
import type { ToolDefinition, ToolGroup } from "../../types/index.js";
// Mock PostgresAdapter for testing
function createMockAdapter(): PostgresAdapter {
const mockToolDefs: ToolDefinition[] = [
{
name: "pg_read_query",
description: "Execute a read query",
group: "core" as ToolGroup,
inputSchema: {},
handler: vi.fn(async () => ({ rows: [{ id: 1 }] })),
},
{
name: "pg_list_tables",
description: "List all tables",
group: "core" as ToolGroup,
inputSchema: {},
handler: vi.fn(async () => [{ name: "users" }, { name: "products" }]),
},
{
name: "pg_jsonb_set",
description: "Set JSONB value",
group: "jsonb" as ToolGroup,
inputSchema: {},
handler: vi.fn(async () => ({ success: true })),
},
{
name: "pg_transaction_begin",
description: "Begin transaction",
group: "transactions" as ToolGroup,
inputSchema: {},
handler: vi.fn(async () => ({ transactionId: "tx-123" })),
},
];
return {
getToolDefinitions: vi.fn(() => mockToolDefs),
createContext: vi.fn(() => ({})),
} as unknown as PostgresAdapter;
}
describe("PgApi", () => {
let adapter: PostgresAdapter;
let pgApi: PgApi;
beforeEach(() => {
adapter = createMockAdapter();
pgApi = createPgApi(adapter);
});
describe("getAvailableGroups()", () => {
it("should return all tool groups with counts", () => {
const groups = pgApi.getAvailableGroups();
expect(groups["core"]).toBe(2);
expect(groups["jsonb"]).toBe(1);
expect(groups["transactions"]).toBe(1);
});
});
describe("getGroupMethods()", () => {
it("should return method names for a group", () => {
const methods = pgApi.getGroupMethods("core");
expect(methods.length).toBeGreaterThan(0);
});
it("should return empty array for unknown group", () => {
const methods = pgApi.getGroupMethods("nonexistent");
expect(methods).toEqual([]);
});
});
describe("createSandboxBindings()", () => {
it("should return object with group namespaces", () => {
const bindings = pgApi.createSandboxBindings();
expect(bindings).toHaveProperty("core");
expect(bindings).toHaveProperty("jsonb");
expect(bindings).toHaveProperty("transactions");
});
it("should have methods for each group", () => {
const bindings = pgApi.createSandboxBindings();
const core = bindings["core"] as Record<string, unknown>;
expect(Object.keys(core).length).toBeGreaterThan(0);
});
it("should execute underlying tool handler when method called", async () => {
const bindings = pgApi.createSandboxBindings();
const core = bindings["core"] as Record<
string,
(params: unknown) => Promise<unknown>
>;
// Find the listTables method (could have different name after transform)
const methodNames = Object.keys(core);
expect(methodNames.length).toBeGreaterThan(0);
// Call first method and verify it returns something
const firstMethod = core[methodNames[0]];
expect(typeof firstMethod).toBe("function");
});
});
});
describe("createPgApi", () => {
it("should create PgApi instance", () => {
const adapter = createMockAdapter();
const api = createPgApi(adapter);
expect(api).toBeInstanceOf(PgApi);
});
it("should call getToolDefinitions on adapter", () => {
const adapter = createMockAdapter();
createPgApi(adapter);
expect(adapter.getToolDefinitions).toHaveBeenCalled();
});
});
describe("Method execution", () => {
let mockAdapter: PostgresAdapter;
let pgApi: PgApi;
beforeEach(() => {
mockAdapter = createMockAdapter();
pgApi = createPgApi(mockAdapter);
});
it("should execute core methods via sandbox bindings", async () => {
const bindings = pgApi.createSandboxBindings();
const core = bindings["core"] as Record<
string,
(params: unknown) => Promise<unknown>
>;
// Call listTables method
if (core["listTables"]) {
const result = await core["listTables"]({});
expect(result).toBeDefined();
}
});
it("should normalize string arg to named parameter", async () => {
const bindings = pgApi.createSandboxBindings();
const core = bindings["core"] as Record<
string,
(...args: unknown[]) => Promise<unknown>
>;
// readQuery("SELECT...") should normalize to {sql: "SELECT..."}
if (core["readQuery"]) {
const result = await core["readQuery"]("SELECT 1");
expect(result).toBeDefined();
}
});
});
describe("Help output", () => {
let adapter: PostgresAdapter;
let pgApi: PgApi;
beforeEach(() => {
adapter = createMockAdapter();
pgApi = createPgApi(adapter);
});
it("should return help for a specific group when called with group name", () => {
const bindings = pgApi.createSandboxBindings();
const core = bindings["core"] as Record<string, unknown>;
// Check if help function exists
if (typeof core["help"] === "function") {
const helpFn = core["help"] as () => unknown;
const result = helpFn();
expect(result).toBeDefined();
}
});
});
describe("toolNameToMethodName conversion", () => {
let adapter: PostgresAdapter;
let pgApi: PgApi;
beforeEach(() => {
adapter = createMockAdapter();
pgApi = createPgApi(adapter);
});
it("should convert pg_read_query to readQuery for core group", () => {
const methods = pgApi.getGroupMethods("core");
// pg_read_query (core tool) → readQuery
expect(methods).toContain("readQuery");
});
it("should convert pg_jsonb_set to set for jsonb group", () => {
const methods = pgApi.getGroupMethods("jsonb");
// pg_jsonb_set → set (removes pg_ and jsonb_ prefix)
expect(methods).toContain("set");
});
});
describe("Method aliases", () => {
let adapter: PostgresAdapter;
let pgApi: PgApi;
beforeEach(() => {
adapter = createMockAdapter();
pgApi = createPgApi(adapter);
});
it("should include method aliases in sandbox bindings", () => {
const bindings = pgApi.createSandboxBindings();
const jsonb = bindings["jsonb"] as Record<string, unknown>;
// jsonbSet should be an alias for set
if (jsonb["set"]) {
expect(jsonb["jsonbSet"]).toBe(jsonb["set"]);
}
});
});