Files
company-celebration/packages/server/load-test/vote-processor.cjs
empty 75570af8bc test: add comprehensive test suite for server
- Add load testing with Artillery (smoke, standard, peak, stress)
- Add functional tests: voting, lottery, admin (18 tests)
- Add fault tolerance tests (14 tests)
- Add network tests for WebSocket (7 tests)
- Add compatibility tests (15 tests)
- Add security tests: anti-fraud, auth, data integrity (10 tests)
- Fix optional callback handling in socket handlers
- Total: 64 test cases, all passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:53:16 +08:00

132 lines
3.7 KiB
JavaScript

/**
* Artillery 压力测试处理器
* 用于生成测试数据和自定义逻辑
*
* 注意:使用 CommonJS 格式以兼容 Artillery
*/
// 节目ID列表
const programIds = [
'program_1',
'program_2',
'program_3',
'program_4',
'program_5',
'program_6',
'program_7',
];
// 奖项票种列表 (与 shared/constants 一致)
const ticketTypes = [
'best_creativity',
'best_visual',
'best_atmosphere',
'best_performance',
'best_teamwork',
'best_innovation',
'most_popular',
];
// 用户计数器 (确保每个虚拟用户有唯一ID)
let userCounter = 0;
/**
* 生成唯一用户ID
*/
function generateUserId(userContext, events, done) {
userCounter++;
const timestamp = Date.now();
userContext.vars.userId = `stress_user_${timestamp}_${userCounter}`;
userContext.vars.voteIndex = 0; // 初始化投票索引
userContext.vars.usedTickets = []; // 已使用的票种
return done();
}
/**
* 随机选择一个节目
*/
function selectRandomProgram(userContext, events, done) {
const randomIndex = Math.floor(Math.random() * programIds.length);
userContext.vars.selectedProgram = programIds[randomIndex];
return done();
}
/**
* 随机选择一个未使用的票种
* 符合业务逻辑:每种票只能用一次
*/
function selectRandomTicketType(userContext, events, done) {
const usedTickets = userContext.vars.usedTickets || [];
const availableTickets = ticketTypes.filter(t => !usedTickets.includes(t));
if (availableTickets.length === 0) {
// 所有票已用完,重置 (用于压测场景)
userContext.vars.usedTickets = [];
userContext.vars.selectedTicket = ticketTypes[0];
} else {
const randomIndex = Math.floor(Math.random() * availableTickets.length);
userContext.vars.selectedTicket = availableTickets[randomIndex];
userContext.vars.usedTickets.push(availableTickets[randomIndex]);
}
return done();
}
/**
* 顺序选择票种 (确保不重复)
*/
function selectNextTicketType(userContext, events, done) {
const voteIndex = userContext.vars.voteIndex || 0;
userContext.vars.selectedTicket = ticketTypes[voteIndex % ticketTypes.length];
userContext.vars.voteIndex = voteIndex + 1;
return done();
}
/**
* 记录投票结果 (用于调试)
*/
function logVoteResult(userContext, events, done) {
const { userId, selectedProgram, selectedTicket } = userContext.vars;
console.log(`[VOTE] User: ${userId} -> Program: ${selectedProgram} (${selectedTicket})`);
return done();
}
/**
* 生成随机延迟 (模拟真实用户行为)
*/
function randomDelay(userContext, events, done) {
const minDelay = 100; // 最小 100ms
const maxDelay = 500; // 最大 500ms
const delay = Math.floor(Math.random() * (maxDelay - minDelay)) + minDelay;
userContext.vars.thinkTime = delay / 1000; // 转换为秒
return done();
}
/**
* 在连接前打印信息 (调试用)
*/
function beforeConnect(requestParams, context, ee, next) {
console.log(`[CONNECT] Connecting user: ${context.vars.userId}`);
return next();
}
/**
* 处理响应 (调试用)
*/
function afterResponse(requestParams, response, context, ee, next) {
if (response.body && response.body.error) {
console.log(`[ERROR] ${context.vars.userId}: ${response.body.error}`);
}
return next();
}
// CommonJS exports
exports.generateUserId = generateUserId;
exports.selectRandomProgram = selectRandomProgram;
exports.selectRandomTicketType = selectRandomTicketType;
exports.selectNextTicketType = selectNextTicketType;
exports.logVoteResult = logVoteResult;
exports.randomDelay = randomDelay;
exports.beforeConnect = beforeConnect;
exports.afterResponse = afterResponse;