Skip to main content
Glama
testing.ts11.7 kB
import { ref, reactive } from "vue"; import { defineStore } from "pinia"; import type { TestCase, MCPTool, ToolResult } from "@/types"; import { useAppStore } from "./app"; export const useTestingStore = defineStore("testing", () => { const appStore = useAppStore(); // State const testCases = ref<TestCase[]>([]); const activeTestCase = ref<TestCase | null>(null); const testResults = ref<Map<string, ToolResult>>(new Map()); const loading = ref(false); const error = ref<string | null>(null); // 测试历史记录 const testHistory = ref< Array<{ id: string; testCaseId: string; toolId: string; parameters: Record<string, any>; result: ToolResult; timestamp: Date; }> >([]); // Actions const setLoading = (value: boolean) => { loading.value = value; }; const setError = (value: string | null) => { error.value = value; }; const clearError = () => { error.value = null; }; // 创建测试用例 const createTestCase = (data: { name: string; toolId: string; parameters: Record<string, any>; expectedResult?: any; tags?: string[]; }): TestCase => { const testCase: TestCase = { id: `test_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, name: data.name, toolId: data.toolId, parameters: data.parameters, expectedResult: data.expectedResult, tags: data.tags || [], createdAt: new Date(), updatedAt: new Date(), }; testCases.value.push(testCase); appStore.addNotification({ type: "success", title: "测试用例创建成功", message: `测试用例 "${data.name}" 已创建`, duration: 3000, }); return testCase; }; // 更新测试用例 const updateTestCase = (id: string, updates: Partial<TestCase>) => { const index = testCases.value.findIndex((tc) => tc.id === id); if (index !== -1) { testCases.value[index] = { ...testCases.value[index], ...updates, updatedAt: new Date(), }; appStore.addNotification({ type: "success", title: "测试用例更新成功", message: `测试用例已更新`, duration: 3000, }); } }; // 删除测试用例 const deleteTestCase = (id: string) => { const index = testCases.value.findIndex((tc) => tc.id === id); if (index !== -1) { const testCase = testCases.value[index]; testCases.value.splice(index, 1); // 清理相关的测试结果 testResults.value.delete(id); // 清理测试历史 testHistory.value = testHistory.value.filter((h) => h.testCaseId !== id); appStore.addNotification({ type: "success", title: "测试用例删除成功", message: `测试用例 "${testCase.name}" 已删除`, duration: 3000, }); } }; // 执行工具测试 const executeToolTest = async ( tool: MCPTool, parameters: Record<string, any>, ): Promise<ToolResult> => { setLoading(true); clearError(); try { // 模拟工具执行逻辑 const startTime = Date.now(); // 参数验证 const validationErrors = validateParameters(tool, parameters); if (validationErrors.length > 0) { throw new Error(`参数验证失败: ${validationErrors.join(", ")}`); } // 模拟API调用 await new Promise((resolve) => setTimeout(resolve, 500 + Math.random() * 1000), ); // 模拟结果 const success = Math.random() > 0.2; // 80%成功率 const executionTime = Date.now() - startTime; let result: ToolResult; if (success) { result = { success: true, data: generateMockResponse(tool, parameters), executionTime, timestamp: new Date(), }; } else { result = { success: false, error: generateMockError(), executionTime, timestamp: new Date(), }; } // 记录测试历史 testHistory.value.unshift({ id: `history_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, testCaseId: "", toolId: tool.id, parameters: { ...parameters }, result: { ...result }, timestamp: new Date(), }); // 限制历史记录数量 if (testHistory.value.length > 100) { testHistory.value = testHistory.value.slice(0, 100); } return result; } catch (err) { const errorMessage = err instanceof Error ? err.message : "工具执行失败"; setError(errorMessage); const result: ToolResult = { success: false, error: errorMessage, executionTime: Date.now(), timestamp: new Date(), }; return result; } finally { setLoading(false); } }; // 参数验证 const validateParameters = ( tool: MCPTool, parameters: Record<string, any>, ): string[] => { const errors: string[] = []; if (!tool.parameters || tool.parameters.type !== "object") { return errors; } const required = tool.parameters.required || []; const properties = tool.parameters.properties || {}; // 检查必需参数 for (const requiredParam of required) { if ( !(requiredParam in parameters) || parameters[requiredParam] === undefined || parameters[requiredParam] === "" ) { errors.push(`缺少必需参数: ${requiredParam}`); } } // 检查参数类型 for (const [paramName, paramValue] of Object.entries(parameters)) { if (paramValue === undefined || paramValue === "") continue; const paramSchema = properties[paramName]; if (!paramSchema) continue; const typeError = validateParameterType( paramName, paramValue, paramSchema, ); if (typeError) { errors.push(typeError); } } return errors; }; // 验证参数类型 const validateParameterType = ( name: string, value: any, schema: any, ): string | null => { const { type, format, minimum, maximum, minLength, maxLength, pattern, enum: enumValues, } = schema; switch (type) { case "string": if (typeof value !== "string") { return `参数 ${name} 应为字符串类型`; } if (minLength !== undefined && value.length < minLength) { return `参数 ${name} 长度不能少于 ${minLength} 个字符`; } if (maxLength !== undefined && value.length > maxLength) { return `参数 ${name} 长度不能超过 ${maxLength} 个字符`; } if (pattern && !new RegExp(pattern).test(value)) { return `参数 ${name} 格式不正确`; } if (enumValues && !enumValues.includes(value)) { return `参数 ${name} 必须为: ${enumValues.join(", ")} 中的一个`; } break; case "number": case "integer": const numValue = Number(value); if (isNaN(numValue)) { return `参数 ${name} 应为数字类型`; } if (type === "integer" && !Number.isInteger(numValue)) { return `参数 ${name} 应为整数`; } if (minimum !== undefined && numValue < minimum) { return `参数 ${name} 不能小于 ${minimum}`; } if (maximum !== undefined && numValue > maximum) { return `参数 ${name} 不能大于 ${maximum}`; } break; case "boolean": if (typeof value !== "boolean") { return `参数 ${name} 应为布尔类型`; } break; case "array": if (!Array.isArray(value)) { return `参数 ${name} 应为数组类型`; } break; case "object": if ( typeof value !== "object" || value === null || Array.isArray(value) ) { return `参数 ${name} 应为对象类型`; } break; } return null; }; // 生成模拟响应 const generateMockResponse = ( tool: MCPTool, parameters: Record<string, any>, ) => { const responses = { get: { id: Math.floor(Math.random() * 1000), name: "测试数据", status: "active", data: parameters, timestamp: new Date().toISOString(), }, post: { id: Math.floor(Math.random() * 1000), message: "创建成功", created: parameters, timestamp: new Date().toISOString(), }, put: { id: Math.floor(Math.random() * 1000), message: "更新成功", updated: parameters, timestamp: new Date().toISOString(), }, delete: { message: "删除成功", deleted_id: parameters.id || Math.floor(Math.random() * 1000), timestamp: new Date().toISOString(), }, }; const method = tool.method.toLowerCase(); return responses[method as keyof typeof responses] || responses.get; }; // 生成模拟错误 const generateMockError = (): string => { const errors = [ "网络连接超时", "服务器内部错误 (500)", "资源未找到 (404)", "访问被拒绝 (403)", "请求参数错误 (400)", "认证失败 (401)", "请求频率限制 (429)", ]; return errors[Math.floor(Math.random() * errors.length)]; }; // 从工具生成测试用例模板 const generateTestCaseTemplate = (tool: MCPTool): Partial<TestCase> => { const parameters: Record<string, any> = {}; if (tool.parameters && tool.parameters.properties) { Object.entries(tool.parameters.properties).forEach(([name, schema]) => { parameters[name] = getDefaultValue(schema); }); } return { name: `${tool.name} 测试用例`, toolId: tool.id, parameters, tags: [tool.method.toUpperCase(), "auto-generated"], }; }; // 获取参数默认值 const getDefaultValue = (schema: any): any => { if (schema.default !== undefined) { return schema.default; } switch (schema.type) { case "string": return schema.format === "email" ? "test@example.com" : schema.format === "date" ? new Date().toISOString().split("T")[0] : schema.format === "date-time" ? new Date().toISOString() : "test value"; case "number": return schema.minimum || 0; case "integer": return schema.minimum || 1; case "boolean": return false; case "array": return []; case "object": return {}; default: return ""; } }; // 按标签筛选测试用例 const getTestCasesByTag = (tag: string): TestCase[] => { return testCases.value.filter((tc) => tc.tags.includes(tag)); }; // 按工具ID筛选测试用例 const getTestCasesByTool = (toolId: string): TestCase[] => { return testCases.value.filter((tc) => tc.toolId === toolId); }; // 获取最近的测试历史 const getRecentTestHistory = (limit: number = 10) => { return testHistory.value.slice(0, limit); }; return { // State testCases, activeTestCase, testResults, testHistory, loading, error, // Actions setLoading, setError, clearError, createTestCase, updateTestCase, deleteTestCase, executeToolTest, validateParameters, generateTestCaseTemplate, getTestCasesByTag, getTestCasesByTool, getRecentTestHistory, }; });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zaizaizhao/mcp-swagger-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server