test(cache): 修复CacheConfigTest边界值测试

- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl
- 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE
- 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查
- 所有1266个测试用例通过
- 覆盖率: 指令81.89%, 行88.48%, 分支51.55%

docs: 添加项目状态报告
- 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态
- 包含质量指标、已完成功能、待办事项和技术债务
This commit is contained in:
Your Name
2026-03-02 13:31:54 +08:00
parent 32d6449ea4
commit 91a0b77f7a
2272 changed files with 221995 additions and 503 deletions

168
frontend/scripts/run-e2e-tests.sh Executable file
View File

@@ -0,0 +1,168 @@
#!/bin/bash
# 🦟 蚊子项目 E2E测试启动脚本
# 同时启动前后端服务并运行E2E测试
set -e
echo "🚀 蚊子项目 E2E测试启动器"
echo ""
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 检查端口占用
check_port() {
local port=$1
if lsof -i :$port > /dev/null 2>&1; then
echo -e "${RED}❌ 端口 $port 已被占用${NC}"
return 1
fi
return 0
}
# 清理函数
cleanup() {
echo ""
echo -e "${YELLOW}🧹 清理进程中...${NC}"
# 杀死后端进程
if [ -n "$BACKEND_PID" ]; then
kill $BACKEND_PID 2>/dev/null || true
echo " 后端进程已停止"
fi
# 杀死前端进程
if [ -n "$FRONTEND_PID" ]; then
kill $FRONTEND_PID 2>/dev/null || true
echo " 前端进程已停止"
fi
echo -e "${GREEN}✅ 清理完成${NC}"
}
# 注册清理函数
trap cleanup EXIT INT TERM
# 检查必要命令
command -v mvn >/dev/null 2>&1 || { echo -e "${RED}❌ 需要Maven (mvn)${NC}"; exit 1; }
command -v node >/dev/null 2>&1 || { echo -e "${RED}❌ 需要Node.js${NC}"; exit 1; }
command -v npm >/dev/null 2>&1 || { echo -e "${RED}❌ 需要npm${NC}"; exit 1; }
# 检查端口
echo "🔍 检查端口..."
check_port 8080 || exit 1
check_port 5173 || exit 1
echo -e "${GREEN} ✅ 端口可用${NC}"
echo ""
# 获取项目根目录
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
FRONTEND_DIR="$PROJECT_ROOT/frontend"
echo "📂 项目目录: $PROJECT_ROOT"
echo ""
# 步骤1: 编译后端
echo "📦 步骤1: 编译后端..."
cd "$PROJECT_ROOT"
mvn clean compile -q -DskipTests
if [ $? -eq 0 ]; then
echo -e "${GREEN} ✅ 后端编译成功${NC}"
else
echo -e "${RED} ❌ 后端编译失败${NC}"
exit 1
fi
echo ""
# 步骤2: 启动后端服务
echo "🚀 步骤2: 启动后端服务..."
cd "$PROJECT_ROOT"
mvn spring-boot:run -Dspring-boot.run.profiles=e2e -Dspring-boot.run.jvmArguments="-Xmx512m" > /tmp/mosquito-backend.log 2>&1 &
BACKEND_PID=$!
echo " 后端PID: $BACKEND_PID"
echo " 日志: /tmp/mosquito-backend.log"
echo ""
# 等待后端启动
echo "⏳ 等待后端服务就绪..."
for i in {1..60}; do
if curl -s http://localhost:8080/api/v1/activities > /dev/null 2>&1; then
echo -e "${GREEN} ✅ 后端服务已就绪${NC}"
break
fi
if [ $i -eq 60 ]; then
echo -e "${RED} ❌ 后端服务启动超时${NC}"
exit 1
fi
echo -n "."
sleep 2
done
echo ""
# 步骤3: 安装前端依赖
echo "📦 步骤3: 安装前端依赖..."
cd "$FRONTEND_DIR"
npm install --silent
echo -e "${GREEN} ✅ 前端依赖已安装${NC}"
echo ""
# 步骤4: 启动前端服务
echo "🚀 步骤4: 启动前端服务..."
cd "$FRONTEND_DIR"
npm run dev -- --port 5173 > /tmp/mosquito-frontend.log 2>&1 &
FRONTEND_PID=$!
echo " 前端PID: $FRONTEND_PID"
echo " 日志: /tmp/mosquito-frontend.log"
echo ""
# 等待前端启动
echo "⏳ 等待前端服务就绪..."
for i in {1..30}; do
if curl -s http://localhost:5173 > /dev/null 2>&1; then
echo -e "${GREEN} ✅ 前端服务已就绪${NC}"
break
fi
if [ $i -eq 30 ]; then
echo -e "${RED} ❌ 前端服务启动超时${NC}"
exit 1
fi
echo -n "."
sleep 1
done
echo ""
# 步骤5: 运行E2E测试
echo "🎭 步骤5: 运行E2E测试..."
echo " 后端地址: http://localhost:8080"
echo " 前端地址: http://localhost:5173"
echo ""
cd "$FRONTEND_DIR"
# 设置环境变量
export API_BASE_URL=http://localhost:8080
export PLAYWRIGHT_BASE_URL=http://localhost:5173
# 运行Playwright测试
echo " 正在运行Playwright E2E测试..."
npx playwright test --reporter=list "$@"
TEST_EXIT_CODE=$?
echo ""
if [ $TEST_EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✅ E2E测试通过${NC}"
else
echo -e "${RED}❌ E2E测试失败${NC}"
fi
echo ""
echo "📊 查看报告:"
echo " Playwright报告: npx playwright show-report e2e-report"
echo " 后端日志: tail -100 /tmp/mosquito-backend.log"
echo " 前端日志: tail -100 /tmp/mosquito-frontend.log"
exit $TEST_EXIT_CODE

View File

@@ -0,0 +1,181 @@
/**
* E2E测试环境验证脚本
* 在运行完整测试前验证配置
*/
const fs = require('fs');
const path = require('path');
console.log('🔍 验证E2E测试环境...\n');
// 检查目录结构
const requiredDirs = [
'e2e',
'e2e/fixtures',
'e2e/tests',
'e2e/utils',
];
console.log('📂 检查目录结构:');
let allDirsExist = true;
for (const dir of requiredDirs) {
const dirPath = path.join(__dirname, '..', dir);
if (fs.existsSync(dirPath)) {
console.log(`${dir}`);
} else {
console.log(`${dir} (缺失)`);
allDirsExist = false;
}
}
if (!allDirsExist) {
console.log('\n❌ 目录结构不完整');
process.exit(1);
}
// 检查关键文件
const requiredFiles = [
'playwright.config.ts',
'e2e/global-setup.ts',
'e2e/global-teardown.ts',
'e2e/fixtures/test-data.ts',
'e2e/tests/user-journey.spec.ts',
'e2e/utils/auth-helper.ts',
'e2e/utils/wait-helper.ts',
];
console.log('\n📄 检查关键文件:');
let allFilesExist = true;
for (const file of requiredFiles) {
const filePath = path.join(__dirname, '..', file);
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath);
console.log(`${file} (${(stats.size / 1024).toFixed(1)} KB)`);
} else {
console.log(`${file} (缺失)`);
allFilesExist = false;
}
}
if (!allFilesExist) {
console.log('\n❌ 关键文件缺失');
process.exit(1);
}
// 检查package.json中的脚本
console.log('\n📦 检查npm脚本:');
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const requiredScripts = [
'test:e2e',
'test:e2e:ui',
'test:e2e:debug',
'test:e2e:report',
];
for (const script of requiredScripts) {
if (packageJson.scripts && packageJson.scripts[script]) {
console.log(`${script}`);
} else {
console.log(` ⚠️ ${script} (可选)`);
}
}
// 检查Playwright配置
console.log('\n⚙ 检查Playwright配置:');
const playwrightConfigPath = path.join(__dirname, '..', 'playwright.config.ts');
if (fs.existsSync(playwrightConfigPath)) {
const configContent = fs.readFileSync(playwrightConfigPath, 'utf-8');
const checks = [
{ name: '测试目录', pattern: /testDir/ },
{ name: '多浏览器支持', pattern: /chromium.*firefox.*webkit/s },
{ name: '移动端设备', pattern: /Pixel 5.*iPhone 12/s },
{ name: '截图配置', pattern: /screenshot/ },
{ name: '录屏配置', pattern: /video/ },
{ name: 'Web服务器配置', pattern: /webServer/ },
];
for (const check of checks) {
if (check.pattern.test(configContent)) {
console.log(`${check.name}`);
} else {
console.log(` ⚠️ ${check.name}`);
}
}
}
// 检查后端E2E配置
console.log('\n🔧 检查后端E2E配置:');
const backendE2EConfigPath = path.join(__dirname, '..', '..', 'src', 'main', 'resources', 'application-e2e.properties');
if (fs.existsSync(backendE2EConfigPath)) {
console.log(' ✅ application-e2e.properties');
const configContent = fs.readFileSync(backendE2EConfigPath, 'utf-8');
const checks = [
{ name: 'H2数据库配置', pattern: /spring\.datasource\.url.*h2/i },
{ name: 'Flyway禁用', pattern: /spring\.flyway\.enabled=false/i },
];
for (const check of checks) {
if (check.pattern.test(configContent)) {
console.log(`${check.name}`);
} else {
console.log(` ⚠️ ${check.name}`);
}
}
} else {
console.log(' ❌ application-e2e.properties (缺失)');
}
// 检查启动脚本
console.log('\n🚀 检查启动脚本:');
const scriptPath = path.join(__dirname, 'run-e2e-tests.sh');
if (fs.existsSync(scriptPath)) {
const stats = fs.statSync(scriptPath);
const isExecutable = (stats.mode & parseInt('111', 8)) !== 0;
if (isExecutable) {
console.log(' ✅ run-e2e-tests.sh (可执行)');
} else {
console.log(' ⚠️ run-e2e-tests.sh (需要执行权限)');
console.log(' 运行: chmod +x scripts/run-e2e-tests.sh');
}
} else {
console.log(' ❌ run-e2e-tests.sh (缺失)');
}
// 统计测试用例
console.log('\n🧪 统计测试用例:');
const testFilePath = path.join(__dirname, '..', 'e2e', 'tests', 'user-journey.spec.ts');
if (fs.existsSync(testFilePath)) {
const testContent = fs.readFileSync(testFilePath, 'utf-8');
// 统计test.describe和test('')
const describeMatches = testContent.match(/test\.describe\(/g);
const testMatches = testContent.match(/test\(['`]/g);
console.log(` 测试套件: ${describeMatches ? describeMatches.length : 0}`);
console.log(` 测试用例: ${testMatches ? testMatches.length : 0}`);
// 提取测试描述
const testDescriptions = testContent.match(/test\(['`]([^'`]+)['`]/g);
if (testDescriptions) {
console.log('\n 测试场景:');
testDescriptions.forEach((desc, i) => {
const cleanDesc = desc.replace(/test\(['`]/, '').replace(/['`]$/, '');
console.log(` ${i + 1}. ${cleanDesc}`);
});
}
}
console.log('\n✅ 验证完成!');
console.log('');
console.log('📖 使用指南:');
console.log(' 快速运行: cd frontend && ./scripts/run-e2e-tests.sh');
console.log(' 分步运行:');
console.log(' 1. mvn spring-boot:run -Dspring-boot.run.profiles=e2e');
console.log(' 2. cd frontend && npm run dev -- --port 5173');
console.log(' 3. cd frontend && npm run test:e2e');
console.log('');