This commit is contained in:
Your Name committed 2026-06-29 17:57:54 -04:00
1 parent 70877882b0
commit ce2f4cf0c8
93 files changed
+4200 -67

No files matched your search

+386
View File
@@ -0,0 +1,386 @@
import prisma from "./src/lib/prisma";
import axios from "axios";
import { handleMenuTool } from "../mcp-server/menu-toolkit";
const API_BASE = "http://localhost:3051/api";
const TEST_MENU_NAME_API = "challenger-test-menu-api";
const TEST_MENU_NAME_MCP = "challenger-test-menu-mcp";
async function loginAndGetToken() {
const loginRes = await fetch(`${API_BASE}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "admin", password: "homaroma123" })
});
if (!loginRes.ok) {
throw new Error(`Failed to log in: ${loginRes.statusText}`);
}
const data = await loginRes.json() as { success: boolean; token: string };
if (!data.success || !data.token) {
throw new Error(`Invalid login response: ${JSON.stringify(data)}`);
}
return data.token;
}
async function cleanDatabase() {
console.log("Cleaning up previous test data in the database...");
await prisma.menu.deleteMany({
where: {
name: {
in: [TEST_MENU_NAME_API, TEST_MENU_NAME_MCP]
}
}
});
console.log("Cleanup finished.");
}
async function run() {
const token = await loginAndGetToken();
console.log("Obtained Admin Authorization Token successfully.");
await cleanDatabase();
console.log("\n=============================================");
console.log("PART 1: Direct Elysia API Routes Verification");
console.log("=============================================");
// 1. Post a new menu via API
console.log("1.1 POST /api/menus - Creating a menu with rootImage and items with columnImage & hoverImage...");
const createPayload = {
name: TEST_MENU_NAME_API,
rootImage: "https://example.com/api-root-original.png",
items: [
{
label: "Link 1",
url: "/link1",
order: 1,
columnImage: "https://example.com/api-col1-original.png",
hoverImage: "https://example.com/api-hover1-original.png"
},
{
label: "Link 2",
url: "/link2",
order: 2,
columnImage: null,
hoverImage: null
}
]
};
const postRes = await fetch(`${API_BASE}/menus`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(createPayload)
});
if (!postRes.ok) {
throw new Error(`POST /menus failed: ${postRes.status} ${await postRes.text()}`);
}
const createdMenu = await postRes.json() as any;
console.log("Successfully created menu via API:", JSON.stringify(createdMenu, null, 2));
// 2. Query the backend API and verify stored fields
console.log("\n1.2 GET /api/menus/:name - Querying the backend API to verify fields...");
const getRes = await fetch(`${API_BASE}/menus/${TEST_MENU_NAME_API}`);
if (!getRes.ok) {
throw new Error(`GET /menus/${TEST_MENU_NAME_API} failed: ${getRes.status}`);
}
const retrievedMenu = await getRes.json() as any;
console.log("Retrieved menu from API:", JSON.stringify(retrievedMenu, null, 2));
if (retrievedMenu.rootImage !== createPayload.rootImage) {
throw new Error(`API Verification Error: Menu rootImage mismatch. Expected '${createPayload.rootImage}', got '${retrievedMenu.rootImage}'`);
}
const items = retrievedMenu.items || [];
if (items.length !== 2) {
throw new Error(`API Verification Error: Expected 2 items, got ${items.length}`);
}
const item1 = items.find((i: any) => i.label === "Link 1");
const item2 = items.find((i: any) => i.label === "Link 2");
if (!item1 || item1.columnImage !== "https://example.com/api-col1-original.png" || item1.hoverImage !== "https://example.com/api-hover1-original.png") {
throw new Error(`API Verification Error: Item 1 image fields mismatch. Got: col='${item1?.columnImage}', hover='${item1?.hoverImage}'`);
}
if (!item2 || item2.columnImage !== null || item2.hoverImage !== null) {
throw new Error(`API Verification Error: Item 2 image fields mismatch. Got: col='${item2?.columnImage}', hover='${item2?.hoverImage}'`);
}
console.log("✅ GET API fields are stored and retrieved correctly!");
// 3. Update the menu via PUT /api/menus/:name
console.log("\n1.3 PUT /api/menus/:name - Updating the menu and items...");
const updatePayload = {
rootImage: "https://example.com/api-root-updated.png",
items: [
{
id: item1.id,
label: "Link 1 Updated",
url: "/link1-updated",
order: 10,
columnImage: "https://example.com/api-col1-updated.png",
hoverImage: "https://example.com/api-hover1-updated.png"
},
{
label: "Link 3 Added",
url: "/link3",
order: 3,
columnImage: "https://example.com/api-col3-new.png",
hoverImage: null
}
]
};
const putRes = await fetch(`${API_BASE}/menus/${TEST_MENU_NAME_API}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(updatePayload)
});
if (!putRes.ok) {
throw new Error(`PUT /menus failed: ${putRes.status} ${await putRes.text()}`);
}
const updatedMenu = await putRes.json() as any;
console.log("Successfully updated menu via PUT:", JSON.stringify(updatedMenu, null, 2));
if (updatedMenu.rootImage !== updatePayload.rootImage) {
throw new Error(`API Verification Error: Updated Menu rootImage mismatch. Expected '${updatePayload.rootImage}', got '${updatedMenu.rootImage}'`);
}
const updatedItems = updatedMenu.items || [];
const updatedItem1 = updatedItems.find((i: any) => i.id === item1.id);
const addedItem3 = updatedItems.find((i: any) => i.label === "Link 3 Added");
if (!updatedItem1 || updatedItem1.columnImage !== "https://example.com/api-col1-updated.png" || updatedItem1.hoverImage !== "https://example.com/api-hover1-updated.png") {
throw new Error(`API Verification Error: Updated Item 1 image fields mismatch.`);
}
if (!addedItem3 || addedItem3.columnImage !== "https://example.com/api-col3-new.png" || addedItem3.hoverImage !== null) {
throw new Error(`API Verification Error: Added Item 3 image fields mismatch.`);
}
console.log("✅ PUT API fields update verified successfully!");
// 4. Verify validation errors for incorrect types in Elysia routes
console.log("\n1.4 Verify Elysia Route Validation Errors for Incorrect Types...");
const invalidPayloads = [
{
description: "Invalid rootImage type (number instead of string/null)",
payload: {
name: "challenger-invalid-1",
rootImage: 12345,
items: []
}
},
{
description: "Invalid columnImage type (boolean instead of string/null)",
payload: {
name: "challenger-invalid-2",
rootImage: null,
items: [
{
label: "Link",
url: "/url",
order: 1,
columnImage: true
}
]
}
},
{
description: "Invalid order type (string instead of number)",
payload: {
name: "challenger-invalid-3",
rootImage: null,
items: [
{
label: "Link",
url: "/url",
order: "first",
columnImage: null
}
]
}
}
];
for (const { description, payload } of invalidPayloads) {
const invalidRes = await fetch(`${API_BASE}/menus`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(payload)
});
console.log(`- Testing: ${description}`);
console.log(` Response Status: ${invalidRes.status} (Expected: 422)`);
if (invalidRes.status !== 422) {
throw new Error(`API Validation Verification Error: Expected 422 status, got ${invalidRes.status}`);
}
const errorBody = await invalidRes.json();
console.log(` Validation Message: ${JSON.stringify((errorBody as any).summary || (errorBody as any).message || errorBody)}`);
}
console.log("✅ Elysia Route Validation Errors verification passed!");
console.log("\n=============================================");
console.log("PART 2: Direct Database Schema Verification");
console.log("=============================================");
console.log("Querying database directly using Prisma client...");
const dbMenu = await prisma.menu.findUnique({
where: { name: TEST_MENU_NAME_API },
include: { items: true }
});
if (!dbMenu) {
throw new Error("DB Verification Error: Menu not found in database.");
}
console.log("Direct DB Menu Record:", JSON.stringify(dbMenu, null, 2));
if (dbMenu.rootImage !== "https://example.com/api-root-updated.png") {
throw new Error(`DB Verification Error: Menu rootImage column mismatch. Expected 'https://example.com/api-root-updated.png', got '${dbMenu.rootImage}'`);
}
const dbItem1 = dbMenu.items.find(i => i.url === "/link1-updated");
if (!dbItem1 || dbItem1.columnImage !== "https://example.com/api-col1-updated.png" || dbItem1.hoverImage !== "https://example.com/api-hover1-updated.png") {
throw new Error("DB Verification Error: Menu item 1 columns mismatch in PostgreSQL.");
}
const dbItem3 = dbMenu.items.find(i => i.url === "/link3");
if (!dbItem3 || dbItem3.columnImage !== "https://example.com/api-col3-new.png" || dbItem3.hoverImage !== null) {
throw new Error("DB Verification Error: Menu item 3 columns mismatch in PostgreSQL.");
}
console.log("✅ PostgreSQL schema verification passed!");
console.log("\n=============================================");
console.log("PART 3: MCP Tools Layer Verification");
console.log("=============================================");
// Create an Axios instance mimicking the MCP server environment
const apiAxios = axios.create({
baseURL: API_BASE,
headers: { Authorization: `Bearer ${token}` }
});
// 1. Test upsert_menu MCP Tool
console.log("3.1 Testing upsert_menu MCP Tool...");
const mcpPayload = {
name: TEST_MENU_NAME_MCP,
rootImage: "https://example.com/mcp-root.png",
items: [
{
label: "MCP Link 1",
url: "/mcp-link1",
order: 1,
columnImage: "https://example.com/mcp-col1.png",
hoverImage: "https://example.com/mcp-hover1.png"
}
]
};
const upsertToolRes = await handleMenuTool(apiAxios, "upsert_menu", mcpPayload);
console.log("upsert_menu MCP response:", JSON.stringify(upsertToolRes, null, 2));
if (upsertToolRes?.isError) {
throw new Error(`MCP Tool Error: upsert_menu failed: ${JSON.stringify(upsertToolRes)}`);
}
// Verify stored fields via direct API / DB
const mcpDbMenu = await prisma.menu.findUnique({
where: { name: TEST_MENU_NAME_MCP },
include: { items: true }
});
if (!mcpDbMenu) {
throw new Error("MCP DB Verification Error: Menu created by MCP not found.");
}
if (mcpDbMenu.rootImage !== mcpPayload.rootImage) {
throw new Error(`MCP DB Verification Error: rootImage mismatch. Got '${mcpDbMenu.rootImage}'`);
}
const mcpItem = mcpDbMenu.items[0];
if (!mcpItem || mcpItem.columnImage !== "https://example.com/mcp-col1.png" || mcpItem.hoverImage !== "https://example.com/mcp-hover1.png") {
throw new Error(`MCP DB Verification Error: Item columns mismatch. Got col='${mcpItem?.columnImage}', hover='${mcpItem?.hoverImage}'`);
}
console.log("✅ MCP upsert_menu successfully created & stored correct fields!");
// 2. Test get_menu MCP Tool
console.log("\n3.2 Testing get_menu MCP Tool...");
const getToolRes = await handleMenuTool(apiAxios, "get_menu", { name: TEST_MENU_NAME_MCP });
console.log("get_menu MCP response:", JSON.stringify(getToolRes, null, 2));
if (getToolRes?.isError) {
throw new Error(`MCP Tool Error: get_menu failed: ${JSON.stringify(getToolRes)}`);
}
const mcpContentText = getToolRes?.content?.[0]?.text;
if (!mcpContentText) {
throw new Error("MCP Tool Error: No content returned by get_menu.");
}
const mcpMenuData = JSON.parse(mcpContentText);
if (mcpMenuData.rootImage !== mcpPayload.rootImage) {
throw new Error(`MCP Tool Verification Error: rootImage mismatch in get_menu response.`);
}
if (mcpMenuData.items?.[0]?.columnImage !== "https://example.com/mcp-col1.png") {
throw new Error(`MCP Tool Verification Error: columnImage mismatch in get_menu response.`);
}
console.log("✅ MCP get_menu successfully retrieved correct fields!");
// 3. Verify validation errors in MCP Tools Layer
console.log("\n3.3 Verify MCP Tool Validation Errors for Incorrect Types...");
const invalidMcpPayloads = [
{
description: "Invalid items parameter type (not an array)",
payload: {
name: TEST_MENU_NAME_MCP,
items: "not-an-array"
}
},
{
description: "Invalid columnImage parameter type (number instead of string/null)",
payload: {
name: TEST_MENU_NAME_MCP,
items: [
{
label: "Invalid Item",
url: "/invalid",
order: 1,
columnImage: 999
}
]
}
}
];
for (const { description, payload } of invalidMcpPayloads) {
console.log(`- Testing MCP Tool: ${description}`);
try {
await handleMenuTool(apiAxios, "upsert_menu", payload);
throw new Error(`Expected MCP Tool to throw validation error, but it succeeded.`);
} catch (e: any) {
console.log(` Caught expected validation error: ${e.message}`);
}
}
console.log("✅ MCP Tool validation error checking passed!");
console.log("\n=============================================");
console.log("ALL TESTS COMPLETED SUCCESSFULLY!");
console.log("=============================================");
}
run()
.catch((err) => {
console.error("\n❌ VERIFICATION TEST FAILED!");
console.error(err);
process.exit(1);
})
.finally(() => {
prisma.$disconnect();
});