| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- const axios = require('axios');
- const colors = require('colors');
- // API格式验证测试脚本
- // 用于验证所有接口是否符合统一响应格式规范
- // 获取Token
- async function getToken() {
- try {
- const { getToken } = require('./quick-token-getter');
- return await getToken();
- } catch (error) {
- console.log('❌ 获取Token失败'.red);
- return null;
- }
- }
- // 验证响应格式
- function validateResponseFormat(response, apiName, expectedType) {
- const issues = [];
- // 检查基础字段
- if (typeof response.code !== 'number') {
- issues.push('❌ code字段缺失或类型错误(应为number)');
- }
- if (typeof response.msg !== 'string') {
- issues.push('❌ msg字段缺失或类型错误(应为string)');
- }
- if (!response.hasOwnProperty('data')) {
- issues.push('❌ data字段缺失');
- }
- // 检查成功状态码
- if (response.code === 200) {
- // 根据期望类型验证字段
- switch (expectedType) {
- case 'paginated':
- // 分页接口直接在顶层包含total和rows字段
- if (typeof response.total !== 'number') {
- issues.push('❌ 分页接口缺少total字段(应为number)');
- }
- if (!Array.isArray(response.rows)) {
- issues.push('❌ 分页接口缺少rows字段(应为array)');
- }
- break;
- case 'array':
- if (!Array.isArray(response.data)) {
- issues.push('❌ 列表接口data应为数组');
- }
- break;
- case 'object':
- if (
- response.data === null ||
- typeof response.data !== 'object' ||
- Array.isArray(response.data)
- ) {
- issues.push('❌ 单条数据接口data应为对象(非null)');
- }
- break;
- case 'null':
- if (response.data !== null) {
- issues.push('❌ 操作类接口data应为null');
- }
- break;
- }
- }
- return issues;
- }
- // API测试用例
- const apiTestCases = [
- {
- name: '报修工单列表(分页)',
- url: '/pm/repairOrder/list',
- method: 'GET',
- data: { pageNum: 1, pageSize: 10 },
- expectedType: 'paginated',
- permission: 'pm:repairOrder:list',
- },
- {
- name: '维修分类下拉选项',
- url: '/pm/repairCategory/options',
- method: 'GET',
- data: null,
- expectedType: 'array',
- permission: 'pm:category:options',
- },
- {
- name: '报修统计数据',
- url: '/pm/repairOrder/statistics',
- method: 'GET',
- data: null,
- expectedType: 'object',
- permission: 'pm:repairOrder:statistics',
- },
- {
- name: '系统用户信息',
- url: '/system/user/getInfo',
- method: 'GET',
- data: null,
- expectedType: 'object',
- permission: 'system:user:query',
- },
- {
- name: '系统字典数据',
- url: '/system/dict/data/list',
- method: 'GET',
- data: { pageNum: 1, pageSize: 10 },
- expectedType: 'paginated',
- permission: 'system:dict:list',
- },
- ];
- // 执行API格式验证测试
- async function runApiFormatValidation() {
- console.log('🔍 API格式验证测试'.rainbow.bold);
- console.log('='.repeat(80).rainbow);
- console.log('📋 验证规范:统一响应格式 {code, msg, data}');
- console.log('🎯 测试目标:确保所有接口符合新的格式规范');
- console.log('');
- // 获取Token
- console.log('🔐 获取认证Token...'.magenta);
- const token = await getToken();
- if (!token) {
- console.log('❌ 无法获取Token,测试终止'.red);
- return;
- }
- console.log('✅ Token获取成功'.green);
- console.log('');
- const results = [];
- let totalTests = 0;
- let passedTests = 0;
- // 逐个测试API
- for (const testCase of apiTestCases) {
- totalTests++;
- console.log(`📝 测试 ${totalTests}: ${testCase.name}`.cyan.bold);
- console.log(` 📍 接口: ${testCase.method} ${testCase.url}`);
- console.log(` 🔑 权限: ${testCase.permission}`);
- console.log(` 📊 期望格式: ${testCase.expectedType}`);
- try {
- // 构建请求配置
- const config = {
- method: testCase.method.toLowerCase(),
- url: `http://localhost/dev-api${testCase.url}`,
- headers: {
- 'Content-Type': 'application/json;charset=utf-8',
- clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
- 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
- referer: 'http://localhost/login?redirect=%252Findex',
- Authorization: `Bearer ${token}`,
- },
- timeout: 10000,
- };
- if (testCase.data) {
- if (testCase.method === 'GET') {
- config.params = testCase.data;
- } else {
- config.data = testCase.data;
- }
- }
- // 发送请求
- const response = await axios(config);
- // 验证响应格式
- const issues = validateResponseFormat(
- response.data,
- testCase.name,
- testCase.expectedType,
- );
- if (issues.length === 0) {
- console.log(' ✅ 格式验证通过'.green);
- passedTests++;
- // 显示响应数据统计
- if (response.data.code === 200) {
- if (testCase.expectedType === 'paginated') {
- console.log(
- ` 📊 数据统计: total=${response.data.total}, rows=${
- response.data.rows?.length || 0
- }`,
- );
- } else if (
- testCase.expectedType === 'array' &&
- Array.isArray(response.data.data)
- ) {
- console.log(` 📊 数据统计: 数组长度=${response.data.data.length}`);
- } else if (testCase.expectedType === 'object' && response.data.data) {
- const keys = Object.keys(response.data.data);
- console.log(` 📊 数据统计: 对象字段=${keys.length}个`);
- }
- }
- } else {
- console.log(' ❌ 格式验证失败'.red);
- issues.forEach((issue) => {
- console.log(` ${issue}`.red);
- });
- }
- results.push({
- name: testCase.name,
- url: testCase.url,
- method: testCase.method,
- success: issues.length === 0,
- issues: issues,
- response: {
- code: response.data.code,
- msg: response.data.msg,
- hasData: response.data.hasOwnProperty('data'),
- dataType:
- testCase.expectedType === 'paginated'
- ? 'paginated'
- : Array.isArray(response.data.data)
- ? 'array'
- : response.data.data === null
- ? 'null'
- : typeof response.data.data,
- total: response.data.total,
- rowsCount: response.data.rows?.length,
- },
- });
- } catch (error) {
- console.log(' ❌ 请求失败'.red);
- console.log(` 错误信息: ${error.response?.data?.msg || error.message}`.red);
- results.push({
- name: testCase.name,
- url: testCase.url,
- method: testCase.method,
- success: false,
- issues: [`请求失败: ${error.response?.data?.msg || error.message}`],
- response: null,
- });
- }
- console.log('');
- }
- // 输出测试总结
- console.log('📊 测试结果汇总'.rainbow.bold);
- console.log('='.repeat(80).rainbow);
- console.log(`🎯 总测试数: ${totalTests}`);
- console.log(`✅ 通过测试: ${passedTests}`.green);
- console.log(`❌ 失败测试: ${totalTests - passedTests}`.red);
- console.log(`📈 通过率: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
- console.log('');
- // 详细结果表格
- console.log('📋 详细测试结果'.blue.bold);
- console.log('─'.repeat(100));
- results.forEach((result, index) => {
- const status = result.success ? '✅ 通过'.green : '❌ 失败'.red;
- console.log(
- `${(index + 1).toString().padStart(2)}. ${result.name.padEnd(
- 20,
- )} ${result.method.padEnd(6)} ${status}`,
- );
- if (!result.success && result.issues.length > 0) {
- result.issues.forEach((issue) => {
- console.log(` ${issue}`.red);
- });
- }
- if (result.response) {
- console.log(
- ` 响应: code=${result.response.code}, msg="${result.response.msg}", data类型=${result.response.dataType}`,
- );
- }
- console.log('');
- });
- // 规范建议
- if (passedTests < totalTests) {
- console.log('💡 规范建议'.yellow.bold);
- console.log('─'.repeat(50));
- console.log('1. 检查请求头是否包含Bearer Token格式的Authorization');
- console.log('2. 分页接口必须直接返回TableDataInfo格式(total、rows、code、msg)');
- console.log('3. 非分页接口必须使用R<T>类型包装返回结果');
- console.log('4. 确保包含clientid、Content-Type等必要请求头');
- console.log('5. 验证响应格式是否符合新的统一规范');
- console.log('');
- }
- // 成功提示
- if (passedTests === totalTests) {
- console.log('🎉 恭喜!所有接口都符合统一响应格式规范!'.green.bold);
- }
- return {
- total: totalTests,
- passed: passedTests,
- failed: totalTests - passedTests,
- successRate: ((passedTests / totalTests) * 100).toFixed(1),
- };
- }
- // 主函数
- async function main() {
- try {
- const result = await runApiFormatValidation();
- console.log('\n🏁 API格式验证测试完成'.rainbow.bold);
- // 返回测试结果供其他脚本使用
- return result;
- } catch (error) {
- console.log('❌ 测试执行失败:'.red, error.message);
- process.exit(1);
- }
- }
- // 如果直接运行此脚本
- if (require.main === module) {
- main().then((result) => {
- if (result.failed > 0) {
- process.exit(1); // 有失败用例时退出码为1
- }
- });
- }
- module.exports = { runApiFormatValidation };
-
|