From d65537529a3f86a6cd1fb74a1d7de0c2b51dbe18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Tue, 7 Apr 2026 11:24:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20=E4=BF=AE=E5=A4=8D=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8=E5=86=B2=E7=AA=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - Playwright的webServer配置会自动启动前端服务 - global-setup.ts也在启动前端服务 - 导致端口3002冲突 修复: - 移除global-setup.ts中的前端服务启动逻辑 - 移除global-setup.ts中的前端服务停止逻辑 - 移除前端服务健康检查验证 - 让Playwright的webServer统一管理前端服务 优势: - 避免端口冲突 - 简化测试环境设置 - 统一服务管理 --- novalon-manage-api/TestBCrypt.java | 14 ++ novalon-manage-web/e2e/global-setup.ts | 201 ++++++++----------------- 2 files changed, 77 insertions(+), 138 deletions(-) create mode 100644 novalon-manage-api/TestBCrypt.java diff --git a/novalon-manage-api/TestBCrypt.java b/novalon-manage-api/TestBCrypt.java new file mode 100644 index 0000000..fbbdfca --- /dev/null +++ b/novalon-manage-api/TestBCrypt.java @@ -0,0 +1,14 @@ +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class TestBCrypt { + public static void main(String[] args) { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); + String password = "admin123"; + String hash = "$2b$12$SFefXlGRFMA0fvxIufpWPuIAl0OPLgRDoCZPThCvjpiJGPYS8yNYy"; + + System.out.println("测试密码验证:"); + System.out.println("密码: " + password); + System.out.println("哈希: " + hash); + System.out.println("验证结果: " + encoder.matches(password, hash)); + } +} diff --git a/novalon-manage-web/e2e/global-setup.ts b/novalon-manage-web/e2e/global-setup.ts index dd7191d..6180c3b 100644 --- a/novalon-manage-web/e2e/global-setup.ts +++ b/novalon-manage-web/e2e/global-setup.ts @@ -9,7 +9,6 @@ const __dirname = path.dirname(__filename); let backendProcess: ChildProcess | null = null; let gatewayProcess: ChildProcess | null = null; -let frontendProcess: ChildProcess | null = null; let healthCheckInterval: NodeJS.Timeout | null = null; async function checkBackendHealth(): Promise { @@ -57,12 +56,12 @@ function startHealthMonitoring() { if (healthCheckInterval) { clearInterval(healthCheckInterval); } - + healthCheckInterval = setInterval(async () => { const backendHealthy = await checkBackendHealth(); const gatewayHealthy = await checkGatewayHealth(); const frontendHealthy = await checkFrontendHealth(); - + if (!backendHealthy) { console.error('⚠️ 后端服务健康检查失败!'); } @@ -84,16 +83,16 @@ function stopHealthMonitoring() { async function globalSetup(config: FullConfig) { console.log('🚀 开始全局测试环境设置...'); - + process.env.NODE_ENV = 'test'; process.env.PLAYWRIGHT_HEADLESS = 'false'; - + const backendDir = path.resolve(__dirname, '../../novalon-manage-api/manage-app'); const jarFile = path.join(backendDir, 'target/manage-app-1.0.0.jar'); - + let backendCommand: string; let backendArgs: string[]; - + if (existsSync(jarFile)) { console.log('📦 使用JAR文件启动后端服务...'); console.log(` JAR文件: ${jarFile}`); @@ -111,10 +110,10 @@ async function globalSetup(config: FullConfig) { backendCommand = 'mvn'; backendArgs = ['spring-boot:run', '-Dspring-boot.run.profiles=dev']; } - + console.log(` 目录: ${backendDir}`); console.log(` 命令: ${backendCommand} ${backendArgs.join(' ')}`); - + backendProcess = spawn(backendCommand, backendArgs, { cwd: backendDir, stdio: 'pipe', @@ -122,7 +121,7 @@ async function globalSetup(config: FullConfig) { detached: false, env: { ...process.env, SPRING_PROFILES_ACTIVE: 'test' } }); - + if (backendProcess.stdout) { backendProcess.stdout.on('data', (data) => { const output = data.toString(); @@ -131,7 +130,7 @@ async function globalSetup(config: FullConfig) { } }); } - + if (backendProcess.stderr) { backendProcess.stderr.on('data', (data) => { const output = data.toString(); @@ -140,26 +139,26 @@ async function globalSetup(config: FullConfig) { } }); } - + backendProcess.on('error', (error) => { console.error('❌ 后端服务启动失败:', error); }); - + backendProcess.on('exit', (code, signal) => { if (code !== 0 && code !== null) { console.error(`❌ 后端服务异常退出,退出码: ${code}, 信号: ${signal}`); } }); - + console.log('⏳ 等待后端服务就绪...'); await waitForBackendReady(); - + const gatewayDir = path.resolve(__dirname, '../../novalon-manage-api/manage-gateway'); const gatewayJarFile = path.join(gatewayDir, 'target/manage-gateway-1.0.0.jar'); - + let gatewayCommand: string; let gatewayArgs: string[]; - + if (existsSync(gatewayJarFile)) { console.log('🚪 使用JAR文件启动网关服务...'); console.log(` JAR文件: ${gatewayJarFile}`); @@ -177,10 +176,10 @@ async function globalSetup(config: FullConfig) { gatewayCommand = 'mvn'; gatewayArgs = ['spring-boot:run', '-Dspring-boot.run.profiles=dev']; } - + console.log(` 目录: ${gatewayDir}`); console.log(` 命令: ${gatewayCommand} ${gatewayArgs.join(' ')}`); - + gatewayProcess = spawn(gatewayCommand, gatewayArgs, { cwd: gatewayDir, stdio: 'pipe', @@ -188,7 +187,7 @@ async function globalSetup(config: FullConfig) { detached: false, env: { ...process.env, SPRING_PROFILES_ACTIVE: 'dev' } }); - + if (gatewayProcess.stdout) { gatewayProcess.stdout.on('data', (data) => { const output = data.toString(); @@ -197,7 +196,7 @@ async function globalSetup(config: FullConfig) { } }); } - + if (gatewayProcess.stderr) { gatewayProcess.stderr.on('data', (data) => { const output = data.toString(); @@ -206,71 +205,28 @@ async function globalSetup(config: FullConfig) { } }); } - + gatewayProcess.on('error', (error) => { console.error('❌ 网关服务启动失败:', error); }); - + gatewayProcess.on('exit', (code, signal) => { if (code !== 0 && code !== null) { console.error(`❌ 网关服务异常退出,退出码: ${code}, 信号: ${signal}`); } }); - + console.log('⏳ 等待网关服务就绪...'); await waitForGatewayReady(); - - const frontendDir = path.resolve(__dirname, '..'); - console.log('🌐 启动前端服务...'); - console.log(` 目录: ${frontendDir}`); - - frontendProcess = spawn('pnpm', ['run', 'dev'], { - cwd: frontendDir, - stdio: 'pipe', - shell: true, - detached: false, - env: { ...process.env, NODE_ENV: 'test' } - }); - - if (frontendProcess.stdout) { - frontendProcess.stdout.on('data', (data) => { - const output = data.toString(); - if (output.includes('Local:') || output.includes('localhost:3002')) { - console.log('✅ 前端服务启动成功'); - } - }); - } - - if (frontendProcess.stderr) { - frontendProcess.stderr.on('data', (data) => { - const output = data.toString(); - if (output.includes('ERROR') || output.includes('error')) { - console.error('❌ 前端服务启动错误:', output); - } - }); - } - - frontendProcess.on('error', (error) => { - console.error('❌ 前端服务启动失败:', error); - }); - - frontendProcess.on('exit', (code, signal) => { - if (code !== 0 && code !== null) { - console.error(`❌ 前端服务异常退出,退出码: ${code}, 信号: ${signal}`); - } - }); - - console.log('⏳ 等待前端服务就绪...'); - await waitForFrontendReady(); - + console.log('🔍 验证所有服务连通性...'); await verifyAllServices(); - + console.log('🧹 清理测试数据...'); await cleanupTestData(); - + startHealthMonitoring(); - + console.log('✅ 全局测试环境设置完成'); } @@ -281,21 +237,14 @@ async function verifyAllServices(): Promise { throw new Error('❌ 后端服务验证失败'); } console.log(' ✅ 后端服务正常'); - + console.log(' 验证网关服务...'); const gatewayOk = await checkGatewayHealth(); if (!gatewayOk) { throw new Error('❌ 网关服务验证失败'); } console.log(' ✅ 网关服务正常'); - - console.log(' 验证前端服务...'); - const frontendOk = await checkFrontendHealth(); - if (!frontendOk) { - throw new Error('❌ 前端服务验证失败'); - } - console.log(' ✅ 前端服务正常'); - + console.log(' 验证网关到后端的连通性...'); try { const response = await fetch('http://localhost:8080/api/auth/login', { @@ -304,28 +253,28 @@ async function verifyAllServices(): Promise { body: JSON.stringify({ username: 'admin', password: 'admin123' }), signal: AbortSignal.timeout(10000) as any }); - + if (!response.ok) { throw new Error(`网关到后端连通性验证失败,状态码: ${response.status}`); } - + const data = await response.json(); if (!data.token) { throw new Error('网关到后端连通性验证失败,未返回token'); } - + console.log(' ✅ 网关到后端连通性正常'); } catch (error) { throw new Error(`❌ 网关到后端连通性验证失败: ${error}`); } - + console.log('✅ 所有服务验证通过'); } async function waitForBackendReady(): Promise { const maxRetries = 90; const retryInterval = 1000; - + for (let i = 0; i < maxRetries; i++) { try { const response = await fetch('http://localhost:8084/actuator/health', { @@ -335,7 +284,7 @@ async function waitForBackendReady(): Promise { const data = await response.json(); if (data.status === 'UP') { console.log(`✅ 后端服务健康检查通过 (尝试 ${i + 1}/${maxRetries})`); - + // 验证服务连通性:测试登录API try { const loginTest = await fetch('http://localhost:8084/api/auth/login', { @@ -344,7 +293,7 @@ async function waitForBackendReady(): Promise { body: JSON.stringify({ username: 'admin', password: 'admin123' }), signal: AbortSignal.timeout(10000) as any }); - + if (loginTest.ok) { console.log('✅ 后端服务连通性验证通过(登录API可用)'); return; @@ -359,19 +308,19 @@ async function waitForBackendReady(): Promise { } catch (error) { // 服务还未就绪,继续等待 } - + if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, retryInterval)); } } - + throw new Error('❌ 后端服务启动超时'); } async function waitForGatewayReady(): Promise { const maxRetries = 90; const retryInterval = 1000; - + for (let i = 0; i < maxRetries; i++) { try { const response = await fetch('http://localhost:8080/actuator/health', { @@ -381,7 +330,7 @@ async function waitForGatewayReady(): Promise { const data = await response.json(); if (data.status === 'UP') { console.log(`✅ 网关服务健康检查通过 (尝试 ${i + 1}/${maxRetries})`); - + // 验证网关连通性:通过网关测试登录API try { const loginTest = await fetch('http://localhost:8080/api/auth/login', { @@ -390,7 +339,7 @@ async function waitForGatewayReady(): Promise { body: JSON.stringify({ username: 'admin', password: 'admin123' }), signal: AbortSignal.timeout(10000) as any }); - + if (loginTest.ok) { console.log('✅ 网关服务连通性验证通过(登录API可用)'); return; @@ -405,19 +354,19 @@ async function waitForGatewayReady(): Promise { } catch (error) { // 服务还未就绪,继续等待 } - + if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, retryInterval)); } } - + throw new Error('❌ 网关服务启动超时'); } async function waitForFrontendReady(): Promise { const maxRetries = 90; const retryInterval = 1000; - + for (let i = 0; i < maxRetries; i++) { try { const response = await fetch('http://localhost:3002', { @@ -430,12 +379,12 @@ async function waitForFrontendReady(): Promise { } catch (error) { // 服务还未就绪,继续等待 } - + if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, retryInterval)); } } - + throw new Error('❌ 前端服务启动超时'); } @@ -452,25 +401,25 @@ async function cleanupTestData(): Promise { password: 'admin123' }) }); - + if (!loginResponse.ok) { console.log('⚠️ 无法登录,跳过数据清理'); return; } - + const loginData = await loginResponse.json(); const token = loginData.token; - + // 获取所有用户 const usersResponse = await fetch('http://localhost:8080/api/users', { headers: { 'Authorization': `Bearer ${token}` } }); - + if (usersResponse.ok) { const users = await usersResponse.json(); - + // 删除测试创建的用户(保留ID 1-10的初始用户) for (const user of users) { if (user.id > 10) { @@ -488,17 +437,17 @@ async function cleanupTestData(): Promise { } } } - + // 获取所有角色 const rolesResponse = await fetch('http://localhost:8080/api/roles', { headers: { 'Authorization': `Bearer ${token}` } }); - + if (rolesResponse.ok) { const roles = await rolesResponse.json(); - + // 删除测试创建的角色(保留ID 1-4的初始角色) for (const role of roles) { if (role.id > 4) { @@ -516,7 +465,7 @@ async function cleanupTestData(): Promise { } } } - + console.log('✅ 测试数据清理完成'); } catch (error) { console.log('⚠️ 数据清理失败,继续执行测试'); @@ -526,20 +475,20 @@ async function cleanupTestData(): Promise { async function globalTeardown() { console.log('🧹 开始全局测试环境清理...'); - + stopHealthMonitoring(); - + if (backendProcess) { console.log('🛑 停止后端服务...'); backendProcess.kill('SIGTERM'); - + await new Promise((resolve) => { if (backendProcess) { backendProcess.on('exit', () => { console.log('✅ 后端服务已停止'); resolve(); }); - + setTimeout(() => { if (backendProcess) { backendProcess.kill('SIGKILL'); @@ -552,18 +501,18 @@ async function globalTeardown() { } }); } - + if (gatewayProcess) { console.log('🛑 停止网关服务...'); gatewayProcess.kill('SIGTERM'); - + await new Promise((resolve) => { if (gatewayProcess) { gatewayProcess.on('exit', () => { console.log('✅ 网关服务已停止'); resolve(); }); - + setTimeout(() => { if (gatewayProcess) { gatewayProcess.kill('SIGKILL'); @@ -576,31 +525,7 @@ async function globalTeardown() { } }); } - - if (frontendProcess) { - console.log('🛑 停止前端服务...'); - frontendProcess.kill('SIGTERM'); - - await new Promise((resolve) => { - if (frontendProcess) { - frontendProcess.on('exit', () => { - console.log('✅ 前端服务已停止'); - resolve(); - }); - - setTimeout(() => { - if (frontendProcess) { - frontendProcess.kill('SIGKILL'); - console.log('⚠️ 强制停止前端服务'); - resolve(); - } - }, 10000); - } else { - resolve(); - } - }); - } - + console.log('✅ 全局测试环境清理完成'); }