integration-test-mcp
Version:
MCP服务器用于集成测试
248 lines (247 loc) • 8.42 kB
JavaScript
/**
* 集成测试 MCP 服务器工具函数
*/
import os from 'os';
import axios from 'axios';
import puppeteer from 'puppeteer-core';
// 全局配置
export const CONFIG = {
// 登录页面URL
loginUrl: process.env.MCP_API_BASE_URL || '',
// API配置
api: {
baseUrl: process.env.MCP_API_BASE_URL || '',
featureList: "/brapi/feature/list",
submitTest: "/brapi/feature/submit_test",
pipelineBranches: "/plapi/branch/get_feature_list",
pipelineIntegrate: "/plapi/stage/integrate",
headers: {
"content-type": "application/json",
"group-id": "3a079c60-d2ec-9e36-0135-6112ed2fe6ed",
},
pipeline: {
taskId: "3a1277fd-8c79-6c90-b9a7-ae75a80b472a",
pipelineId: "3a1277f8-a6c1-1c75-7428-4ecd9f1617e7",
stageId: "3a1277fd-8c5f-3fca-12f1-81799faf345d",
},
},
// 登录凭证
credentials: {
selectors: {
usernameInput: ".login-user",
passwordInput: ".login-pwd",
submitButton: ".login-button",
},
},
// 需要获取的cookie字段
cookieFields: ["token"],
// 登录成功后的判断条件
loginSuccessCondition: {
urlIncludes: "/feature",
elementExists: ".ant-avatar",
},
};
/**
* 获取登录凭证
* @returns 返回用户名和密码
* @throws 如果环境变量未设置则抛出错误
*/
export function getCredentials() {
const username = process.env.MCP_USERNAME;
const password = process.env.MCP_PASSWORD;
const baseUrl = process.env.MCP_API_BASE_URL;
if (!username || !password) {
throw new Error('环境变量 MCP_USERNAME 或 MCP_PASSWORD 未设置,请先设置环境变量');
}
if (!baseUrl) {
throw new Error('环境变量 MCP_API_BASE_URL 未设置,请先设置环境变量');
}
return { username, password };
}
export function createApiClient(token) {
const client = axios.create({
baseURL: CONFIG.api.baseUrl,
headers: {
...CONFIG.api.headers,
Cookie: `token=${token}`,
},
});
client.interceptors.response.use((response) => response.data, (error) => {
throw new Error(`API请求失败: ${error.response?.data?.msg || error.message}`);
});
return client;
}
export async function getFeatureList(apiClient, branchName) {
const params = {
page_size: 100,
page: 1,
show_myself: 1,
name: branchName,
};
const response = await apiClient.get(CONFIG.api.featureList, { params });
if (response.code !== 0) {
throw new Error(response.msg || "获取特性列表失败");
}
if (!response.data.items?.length) {
throw new Error(`未找到分支 ${branchName} 对应的特性数据`);
}
return response;
}
export async function submitFeatureToTest(apiClient, featureId) {
const data = {
id: featureId,
action: "submit_test",
info: "1",
};
const response = await apiClient.post(CONFIG.api.submitTest, data);
if (response.code === 10000) {
throw new Error(response.msg || "提测失败");
}
if (response.code !== 0) {
throw new Error(response.msg || "提交特性到测试失败");
}
return response;
}
export async function getPipelineBranches(apiClient, branchName) {
const params = {
task_id: CONFIG.api.pipeline.taskId,
pipeline_id: CONFIG.api.pipeline.pipelineId,
page: 1,
page_size: 10,
name: branchName,
};
const response = await apiClient.get(CONFIG.api.pipelineBranches, {
params,
});
if (response.code !== 0) {
throw new Error(response.msg || "获取流水线分支列表失败");
}
if (!response.data.items?.length) {
throw new Error(`未找到分支 ${branchName} 对应的流水线数据`);
}
return response;
}
export async function integratePipeline(apiClient, featureId) {
const data = {
pipeline_id: CONFIG.api.pipeline.pipelineId,
stage_id: CONFIG.api.pipeline.stageId,
task_id: CONFIG.api.pipeline.taskId,
feature_ids: featureId,
};
const response = await apiClient.post(CONFIG.api.pipelineIntegrate, data);
if (response.code !== 0) {
throw new Error(response.msg || "执行流水线集成失败");
}
return response;
}
export function getEdgePath() {
const platform = os.platform();
switch (platform) {
case "win32":
return "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
case "darwin":
return "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
case "linux":
return "/usr/bin/microsoft-edge";
default:
throw new Error(`不支持的操作系统: ${platform}`);
}
}
export async function loginAndGetCookies() {
let browserInstance = null;
try {
const launchOptions = {
headless: true,
defaultViewport: {
width: 1920,
height: 1080,
},
args: [
"--disable-gpu",
"--disable-dev-shm-usage",
"--disable-setuid-sandbox",
"--no-sandbox",
],
executablePath: getEdgePath(),
};
// 获取登录凭证
const { username, password } = getCredentials();
browserInstance = await puppeteer.launch(launchOptions);
const page = await browserInstance.newPage();
await page.goto(CONFIG.loginUrl, { waitUntil: "networkidle2" });
await page.type(CONFIG.credentials.selectors.usernameInput, username);
await page.type(CONFIG.credentials.selectors.passwordInput, password);
await page.click(CONFIG.credentials.selectors.submitButton);
if (CONFIG.loginSuccessCondition.urlIncludes) {
await page.waitForFunction((expectedUrl) => window.location.href.includes(expectedUrl), {}, CONFIG.loginSuccessCondition.urlIncludes);
}
if (CONFIG.loginSuccessCondition.elementExists) {
await page.waitForSelector(CONFIG.loginSuccessCondition.elementExists);
}
const cookies = await page.cookies();
const result = {};
for (const cookie of cookies) {
if (CONFIG.cookieFields.includes(cookie.name)) {
result[cookie.name] = cookie.value;
}
}
return result;
}
catch (error) {
throw new Error(`登录过程中发生错误: ${error.message}`);
}
finally {
if (browserInstance) {
try {
await browserInstance.close();
}
catch (closeError) {
console.error("关闭浏览器时发生错误:", closeError.message);
}
}
}
}
// 主要业务逻辑
export async function runIntegrationTest(branchName) {
try {
const cookies = await loginAndGetCookies();
const apiClient = createApiClient(cookies.token);
const featureList = await getFeatureList(apiClient, branchName);
const feature = featureList.data.items[0];
const submitResult = await submitFeatureToTest(apiClient, feature.id);
const pipelineBranches = await getPipelineBranches(apiClient, branchName);
const branch = pipelineBranches.data.items[0];
const integrateResult = await integratePipeline(apiClient, branch.id);
return {
success: true,
branch: branchName,
feature: {
id: feature.id,
name: feature.name,
branch_name: feature.branch_name,
status: feature.status,
},
submit: {
code: submitResult.code,
msg: submitResult.msg,
success: submitResult.data,
},
pipeline: {
id: branch.id,
name: branch.name,
status: branch.status,
},
integrate: {
code: integrateResult.code,
msg: integrateResult.msg,
success: integrateResult.data,
},
};
}
catch (error) {
return {
success: false,
error: error.message,
};
}
}