77 lines
4.4 KiB
TypeScript
77 lines
4.4 KiB
TypeScript
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" } });
|
|
});
|
|
});
|