refactor: clean up project structure
- Remove old review reports (keep latest only) - Move docs/ to deploy/docs-backup/ - Move performance-testing/ to deploy/ - Clean up test output files - Organize root directory
This commit is contained in:
247
deploy/performance-testing/test-suites/admin.test.js
Normal file
247
deploy/performance-testing/test-suites/admin.test.js
Normal file
@@ -0,0 +1,247 @@
|
||||
// Sub2API Admin API Performance Test
|
||||
// 管理后台 API 性能测试
|
||||
|
||||
import http from 'k6/http';
|
||||
import { check, sleep, group } from 'k6';
|
||||
import { Rate, Trend, Counter } from 'k6/metrics';
|
||||
import { config, getBaseUrl, getAdminCredentials } from '../common/utils.js';
|
||||
import { httpGet, httpPost, randomSleep } from '../common/utils.js';
|
||||
|
||||
// ============= 自定义指标 =============
|
||||
|
||||
const adminUserListDuration = new Trend('admin_user_list_duration');
|
||||
const adminGroupStatsDuration = new Trend('admin_group_stats_duration');
|
||||
const adminDashboardDuration = new Trend('admin_dashboard_duration');
|
||||
const adminErrorRate = new Rate('admin_errors');
|
||||
|
||||
// ============= 测试配置 =============
|
||||
|
||||
export const options = {
|
||||
scenarios: {
|
||||
admin_load: {
|
||||
executor: 'ramping-vus',
|
||||
startVUs: 2,
|
||||
stages: [
|
||||
{ duration: '1m', target: 5 },
|
||||
{ duration: '3m', target: 20 },
|
||||
{ duration: '2m', target: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
thresholds: {
|
||||
'admin_user_list_duration': ['p(95)<1000', 'p(99)<2000'],
|
||||
'admin_group_stats_duration': ['p(95)<1500', 'p(99)<3000'],
|
||||
'admin_errors': ['rate<0.02'],
|
||||
},
|
||||
};
|
||||
|
||||
// ============= 辅助函数 =============
|
||||
|
||||
function getAdminHeaders() {
|
||||
return {
|
||||
'Authorization': `Bearer ${getAdminToken()}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
|
||||
let adminToken = null;
|
||||
let tokenExpiry = 0;
|
||||
|
||||
function getAdminToken() {
|
||||
const now = Date.now();
|
||||
if (adminToken && now < tokenExpiry) {
|
||||
return adminToken;
|
||||
}
|
||||
|
||||
const credentials = getAdminCredentials();
|
||||
const res = http.post(
|
||||
`${getBaseUrl()}/api/v1/auth/login`,
|
||||
JSON.stringify({
|
||||
email: credentials.email,
|
||||
password: credentials.password,
|
||||
}),
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
tags: { name: 'admin_login' },
|
||||
}
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
adminToken = res.json('token');
|
||||
tokenExpiry = now + (55 * 60 * 1000);
|
||||
return adminToken;
|
||||
}
|
||||
|
||||
throw new Error('Admin login failed');
|
||||
}
|
||||
|
||||
// ============= 测试场景 =============
|
||||
|
||||
/**
|
||||
* 测试用户列表
|
||||
*/
|
||||
function testUserList() {
|
||||
const res = http.get(`${getBaseUrl()}/api/v1/admin/users?page=1&limit=20`, {
|
||||
headers: getAdminHeaders(),
|
||||
tags: { name: 'admin_user_list' },
|
||||
});
|
||||
|
||||
adminUserListDuration.add(res.timings.duration);
|
||||
adminErrorRate.add(res.status !== 200);
|
||||
|
||||
check(res, {
|
||||
'User List: status is 200': (r) => r.status === 200,
|
||||
'User List: has data': (r) => r.json('data') !== undefined,
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试分组统计
|
||||
*/
|
||||
function testGroupStats(groupId = 1) {
|
||||
const res = http.get(`${getBaseUrl()}/api/v1/admin/groups/${groupId}/stats`, {
|
||||
headers: getAdminHeaders(),
|
||||
tags: { name: 'admin_group_stats' },
|
||||
});
|
||||
|
||||
adminGroupStatsDuration.add(res.timings.duration);
|
||||
adminErrorRate.add(res.status !== 200);
|
||||
|
||||
check(res, {
|
||||
'Group Stats: status is 200': (r) => r.status === 200,
|
||||
'Group Stats: has stats': (r) => r.json() !== undefined,
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试仪表板
|
||||
*/
|
||||
function testDashboard() {
|
||||
const res = http.get(`${getBaseUrl()}/api/v1/admin/dashboard`, {
|
||||
headers: getAdminHeaders(),
|
||||
tags: { name: 'admin_dashboard' },
|
||||
});
|
||||
|
||||
adminDashboardDuration.add(res.timings.duration);
|
||||
adminErrorRate.add(res.status !== 200);
|
||||
|
||||
check(res, {
|
||||
'Dashboard: status is 200': (r) => r.status === 200,
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试余额调整
|
||||
*/
|
||||
function testBalanceAdjustment() {
|
||||
// 首先获取一个用户 ID
|
||||
const userListRes = testUserList();
|
||||
if (userListRes.status !== 200 || !userListRes.json('data')?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = userListRes.json('data')[0].id;
|
||||
|
||||
const res = http.post(
|
||||
`${getBaseUrl()}/api/v1/admin/users/${userId}/balance`,
|
||||
JSON.stringify({
|
||||
balance: 10.00,
|
||||
operation: 'add',
|
||||
notes: 'Performance test adjustment',
|
||||
}),
|
||||
{
|
||||
headers: getAdminHeaders(),
|
||||
tags: { name: 'admin_balance_adjust' },
|
||||
}
|
||||
);
|
||||
|
||||
check(res, {
|
||||
'Balance Adjust: status is 200': (r) => r.status === 200,
|
||||
});
|
||||
|
||||
adminErrorRate.add(res.status !== 200);
|
||||
}
|
||||
|
||||
// ============= 主测试函数 =============
|
||||
|
||||
export default function () {
|
||||
group('Admin API', () => {
|
||||
const mode = __VU % 5;
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
testDashboard();
|
||||
break;
|
||||
case 1:
|
||||
testUserList();
|
||||
randomSleep(0.1, 0.5);
|
||||
testUserList();
|
||||
break;
|
||||
case 2:
|
||||
testGroupStats(1);
|
||||
testGroupStats(2);
|
||||
break;
|
||||
case 3:
|
||||
testBalanceAdjustment();
|
||||
break;
|
||||
case 4:
|
||||
// 综合测试
|
||||
testDashboard();
|
||||
testUserList();
|
||||
testGroupStats(1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
randomSleep(1, 3);
|
||||
}
|
||||
|
||||
export function handleSummary(data) {
|
||||
return {
|
||||
'admin-performance-report.json': JSON.stringify(data, null, 2),
|
||||
'admin-performance-summary.txt': `
|
||||
================================================================================
|
||||
Sub2API Admin API 性能测试报告
|
||||
================================================================================
|
||||
|
||||
测试时间: ${new Date().toISOString()}
|
||||
测试持续: ${(data.state.testRunDurationMs / 1000 / 60).toFixed(2)} 分钟
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
核心指标
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
总请求数: ${data.metrics?.requests_total?.values?.count || 0}
|
||||
错误率: ${((data.metrics?.admin_errors?.values?.rate || 0) * 100).toFixed(2)}%
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
操作类型性能
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
仪表板:
|
||||
平均响应: ${data.metrics?.admin_dashboard_duration?.values?.avg?.toFixed(2) || 0} ms
|
||||
P95 响应: ${data.metrics?.admin_dashboard_duration?.values?.['p(95)']?.toFixed(2) || 0} ms
|
||||
|
||||
用户列表:
|
||||
平均响应: ${data.metrics?.admin_user_list_duration?.values?.avg?.toFixed(2) || 0} ms
|
||||
P95 响应: ${data.metrics?.admin_user_list_duration?.values?.['p(95)']?.toFixed(2) || 0} ms
|
||||
P99 响应: ${data.metrics?.admin_user_list_duration?.values?.['p(99)']?.toFixed(2) || 0} ms
|
||||
|
||||
分组统计:
|
||||
平均响应: ${data.metrics?.admin_group_stats_duration?.values?.avg?.toFixed(2) || 0} ms
|
||||
P95 响应: ${data.metrics?.admin_group_stats_duration?.values?.['p(95)']?.toFixed(2) || 0} ms
|
||||
P99 响应: ${data.metrics?.admin_group_stats_duration?.values?.['p(99)']?.toFixed(2) || 0} ms
|
||||
|
||||
================================================================================
|
||||
测试完成
|
||||
================================================================================
|
||||
`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user