import { describe, expect, test } from "bun:test"; import type { AxiosInstance } from "axios"; import { handleStorefrontTool, STOREFRONT_TOOL_DEFINITIONS } from "./storefront-toolkit"; function fakeApi(sections: any[] = []) { const calls: Array<{ method: string; path: string; body?: unknown }> = []; const api = { get: async (path: string) => { calls.push({ method: "GET", path }); return { data: path === "/settings/footer-layout" ? { version: "v1", layout: {} } : sections }; }, patch: async (path: string, body: unknown) => { calls.push({ method: "PATCH", path, body }); return { data: { success: true } }; }, post: async (path: string, body?: unknown) => { calls.push({ method: "POST", path, body }); return { data: { success: true } }; }, put: async (path: string, body: unknown) => { calls.push({ method: "PUT", path, body }); return { data: { success: true } }; }, delete: async (path: string) => { calls.push({ method: "DELETE", path }); return { data: { success: true } }; }, } as unknown as AxiosInstance; return { api, calls }; } const value = (response: any) => JSON.parse(response.content[0].text); describe("storefront MCP tools", () => { test("definitions expose the complete new workflow", () => { expect(STOREFRONT_TOOL_DEFINITIONS.map((tool) => tool.name)).toEqual([ "get_footer_layout", "patch_footer_layout", "get_category_sections", "seed_category_sections", "add_category_section", "update_category_section", "reorder_category_sections", "delete_category_section", ]); }); test("footer write requires version and forwards backend dry run", async () => { const { api, calls } = fakeApi(); const missing = await handleStorefrontTool(api, "patch_footer_layout", { columns: ["brand"] }); expect(value(missing).code).toBe("VERSION_REQUIRED"); expect(calls).toHaveLength(0); const preview = await handleStorefrontTool(api, "patch_footer_layout", { columns: ["brand"], dryRun: true }); expect(preview?.isError).toBeUndefined(); expect(calls[0]).toEqual({ method: "PATCH", path: "/settings/footer-layout", body: { columns: ["brand"], dryRun: true } }); await handleStorefrontTool(api, "patch_footer_layout", { finale: { enabled: true }, expectedVersion: "v1" }); expect(calls[1]?.body).toEqual({ finale: { enabled: true }, expectedVersion: "v1" }); }); test("footer validation returns field paths without a request", async () => { const { api, calls } = fakeApi(); const response = await handleStorefrontTool(api, "patch_footer_layout", { columns: ["brand", "brand"], dryRun: true }); expect(value(response).code).toBe("INVALID_INPUT"); expect(calls).toHaveLength(0); }); test("category dry run reads but never writes", async () => { const { api, calls } = fakeApi(); const response = await handleStorefrontTool(api, "seed_category_sections", { categoryId: 9, dryRun: true }); expect(value(response).dryRun).toBe(true); expect(calls).toEqual([{ method: "GET", path: "/categories/9/sections" }]); }); test("category update rejects wrong ownership and locked changes", async () => { const { api, calls } = fakeApi([{ id: 7, type: "CATEGORY_HEADER", isLocked: true }]); expect(value(await handleStorefrontTool(api, "update_category_section", { categoryId: 9, sectionId: 8, isActive: false })).code).toBe("NOT_FOUND"); expect(value(await handleStorefrontTool(api, "update_category_section", { categoryId: 9, sectionId: 7, isActive: false })).code).toBe("CONFLICT"); expect(calls.every((call) => call.method === "GET")).toBe(true); }); test("reorder rejects missing IDs before backend write", async () => { const { api, calls } = fakeApi([{ id: 1 }, { id: 2 }]); const response = await handleStorefrontTool(api, "reorder_category_sections", { categoryId: 9, sections: [{ id: 1, order: 1 }] }); expect(value(response).code).toBe("INVALID_INPUT"); expect(calls.every((call) => call.method === "GET")).toBe(true); }); test("backend conflict includes current version", async () => { const { api } = fakeApi(); api.patch = async () => { throw { response: { status: 409, data: { code: "VERSION_CONFLICT", error: "Changed", currentVersion: "v2" } } }; }; const response = await handleStorefrontTool(api, "patch_footer_layout", { columns: ["brand"], expectedVersion: "v1" }); expect(value(response)).toMatchObject({ code: "VERSION_CONFLICT", message: "Changed", details: { status: 409, currentVersion: "v2" } }); }); });