live-forum/server/k6/utils/helpers.js
2026-03-24 11:27:37 +08:00

265 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* k6 压测工具函数
* 直播论坛项目
*/
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
import { BASE_URL, HTTP_TIMEOUT, THINK_TIME } from '../config.js';
// ============================================
// Token 管理
// ============================================
// 使用 SharedArray 加载 Token所有 VU 共享,节省内存)
const tokens = new SharedArray('tokens', function() {
// 读取 tokens.txt 文件
const data = open('../tokens.txt');
// 按行分割,过滤空行和注释行
return data.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
});
/**
* 获取 Token 总数
*/
export function getTokenCount() {
return tokens.length;
}
/**
* 根据 VU 编号获取 Token
* 如果 VU 数量超过 Token 数量,会循环使用
* @param {number} vuId - VU 编号 (__VU)
* @returns {string} Token已添加 Bearer 前缀)
*/
export function getToken(vuId) {
if (tokens.length === 0) {
console.error('警告: tokens.txt 中没有有效的 Token');
return '';
}
const index = (vuId - 1) % tokens.length;
const token = tokens[index];
return `Bearer ${token}`;
}
/**
* 获取请求头(带认证)
* @param {number} vuId - VU 编号
* @returns {object} HTTP Headers
*/
export function getHeaders(vuId) {
return {
'Content-Type': 'application/json',
'Authorization': getToken(vuId),
};
}
// ============================================
// HTTP 请求封装
// ============================================
/**
* 发送 GET 请求
* @param {string} path - API 路径
* @param {object} params - URL 参数
* @param {number} vuId - VU 编号
* @returns {object} HTTP Response
*/
export function httpGet(path, params = {}, vuId) {
const url = buildUrl(path, params);
const response = http.get(url, {
headers: getHeaders(vuId),
timeout: HTTP_TIMEOUT,
tags: { api: path.split('?')[0] },
});
return response;
}
/**
* 发送 POST 请求
* @param {string} path - API 路径
* @param {object} body - 请求体
* @param {number} vuId - VU 编号
* @returns {object} HTTP Response
*/
export function httpPost(path, body = {}, vuId) {
const url = `${BASE_URL}${path}`;
const response = http.post(url, JSON.stringify(body), {
headers: getHeaders(vuId),
timeout: HTTP_TIMEOUT,
tags: { api: path },
});
return response;
}
/**
* 构建带参数的 URL
* @param {string} path - API 路径
* @param {object} params - URL 参数
* @returns {string} 完整 URL
*/
function buildUrl(path, params) {
let url = `${BASE_URL}${path}`;
const queryString = Object.entries(params)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
if (queryString) {
url += (path.includes('?') ? '&' : '?') + queryString;
}
return url;
}
// ============================================
// 响应检查
// ============================================
/**
* 检查响应是否成功
* @param {object} response - HTTP Response
* @param {string} name - 检查名称
* @returns {boolean} 是否成功
*/
export function checkResponse(response, name) {
const result = check(response, {
[`${name} - status is 200`]: (r) => r.status === 200,
[`${name} - code is 0`]: (r) => {
try {
const body = JSON.parse(r.body);
return body.code === 0;
} catch {
return false;
}
},
});
// 如果失败,打印详细信息(仅在调试时有用)
if (!result && response.status !== 200) {
console.log(`${name} 失败: status=${response.status}, body=${response.body.substring(0, 200)}`);
}
return result;
}
/**
* 解析响应体
* @param {object} response - HTTP Response
* @returns {object|null} 解析后的数据
*/
export function parseResponse(response) {
try {
const body = JSON.parse(response.body);
if (body.code === 0) {
return body.data;
}
return null;
} catch {
return null;
}
}
// ============================================
// 模拟用户行为
// ============================================
/**
* 模拟用户思考时间(随机等待)
*/
export function thinkTime() {
const duration = Math.random() * (THINK_TIME.max - THINK_TIME.min) + THINK_TIME.min;
sleep(duration);
}
/**
* 短暂等待API 调用间隔)
*/
export function shortWait() {
sleep(0.5 + Math.random() * 0.5);
}
// ============================================
// 数据生成
// ============================================
/**
* 生成随机评论内容
* @returns {string} 评论内容
*/
export function randomComment() {
const comments = [
'压测评论内容_' + Date.now(),
'这是一条测试评论_' + Math.random().toString(36).substring(7),
'好帖子!' + Date.now(),
'支持一下~' + Math.random().toString(36).substring(7),
'学习了!' + Date.now(),
];
return comments[Math.floor(Math.random() * comments.length)];
}
/**
* 生成随机整数
* @param {number} min - 最小值
* @param {number} max - 最大值
* @returns {number} 随机整数
*/
export function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// ============================================
// API 路径常量
// ============================================
export const API = {
// 认证
AUTH: {
LOGIN: '/api/Auth/WechatMpLogin',
REFRESH: '/api/Auth/RefreshToken',
},
// 首页
HOME: {
BANNERS: '/api/Banners/GetBanners',
CATEGORIES: '/api/Home/StreamerCategories',
RANKINGS: '/api/Home/Rankings',
RANKINGS_MORE: '/api/Home/RankingsMore',
LIVE_STREAMERS: '/api/Home/LiveStreamers',
},
// 帖子
POSTS: {
LIST: '/api/Posts/GetPosts',
DETAIL: '/api/Posts/GetPostDetail',
PUBLISH: '/api/Posts/PublishPosts',
LIKE: '/api/Posts/LikePost',
DELETE: '/api/Posts/DeletePosts',
MY_POSTS: '/api/Posts/GetMyPosts',
},
// 评论
COMMENTS: {
LIST: '/api/PostComments/GetPostComments',
PUBLISH: '/api/PostComments/PublishPostComments',
REPLIES: '/api/PostComments/GetCommentsReplies',
LIKE: '/api/PostComments/LikeComment',
},
// 送花
FLOWERS: {
SEND: '/api/Flowers/SendFlower',
},
// 用户
USER: {
INFO: '/api/UsersInfo/GetUserInfo',
UPDATE: '/api/UsersInfo/UpdateUserInfo',
},
// 消息
MESSAGES: {
LIST: '/api/Messages/GetMessages',
PARTICIPATE: '/api/Messages/ParticipateActivity',
},
// 配置
CONFIG: {
APP: '/api/Config/GetAppConfig',
AGREEMENT: '/api/Config/GetAgreement',
},
};