diff --git a/src/CncCollector/scripts/e2e-collector.spec.ts b/src/CncCollector/scripts/e2e-collector.spec.ts new file mode 100644 index 0000000..af646a7 --- /dev/null +++ b/src/CncCollector/scripts/e2e-collector.spec.ts @@ -0,0 +1,185 @@ +/** + * CNC 采集服务 Playwright 端到端测试 + * + * 前置条件: + * 1. MariaDB 运行中(cnc_business + cnc_log 库已初始化) + * 2. CncSimulator 运行在 http://localhost:9001/ + * 3. CncCollector 运行中,管理API在 http://localhost:5800/ + * + * 安装: npm init -y && npm install @playwright/test + * 运行: npx playwright test e2e-collector.spec.ts --reporter=list + */ + +import { test, expect, request } from '@playwright/test'; + +const API_BASE = 'http://localhost:5800'; +const API_KEY = 'collector_api_key_2026'; +const HEADERS = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }; + +// ============================================================ +// 测试套件1: 采集服务管理API控制测试 +// ============================================================ +test.describe('采集服务管理API控制测试', () => { + + test('GET /api/collector/status - 获取服务状态', async () => { + const ctx = await request.newContext(); + const resp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + + expect(resp.status()).toBe(200); + const body = await resp.json(); + + expect(body.code).toBe(0); + expect(body.data).toBeDefined(); + expect(body.data).toHaveProperty('isRunning'); + expect(body.data).toHaveProperty('startTime'); + expect(body.data).toHaveProperty('uptimeSeconds'); + expect(body.data).toHaveProperty('workerCount'); + expect(body.data).toHaveProperty('workers'); + }); + + test('GET /api/collector/status - 无API Key返回401', async () => { + const ctx = await request.newContext(); + const resp = await ctx.get(`${API_BASE}/api/collector/status`); + + expect(resp.status()).toBe(401); + const body = await resp.json(); + expect(body.code).toBe(40101); + }); + + test('GET /api/collector/status - 错误API Key返回401', async () => { + const ctx = await request.newContext(); + const resp = await ctx.get(`${API_BASE}/api/collector/status`, { + headers: { 'X-API-Key': 'wrong_key' } + }); + + expect(resp.status()).toBe(401); + }); + + test('POST /api/collector/refresh - 刷新配置', async () => { + const ctx = await request.newContext(); + const resp = await ctx.post(`${API_BASE}/api/collector/refresh`, { headers: HEADERS }); + + expect(resp.status()).toBe(200); + const body = await resp.json(); + expect(body.code).toBe(0); + expect(body.data.message).toContain('刷新'); + }); + + test('POST /api/collector/stop - 停止采集服务', async () => { + const ctx = await request.newContext(); + const resp = await ctx.post(`${API_BASE}/api/collector/stop`, { headers: HEADERS }); + + expect(resp.status()).toBe(200); + const body = await resp.json(); + expect(body.code).toBe(0); + + // 验证状态变为停止 + const statusResp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const statusBody = await statusResp.json(); + expect(statusBody.data.isRunning).toBe(false); + }); + + test('POST /api/collector/start - 启动采集服务', async () => { + const ctx = await request.newContext(); + const resp = await ctx.post(`${API_BASE}/api/collector/start`, { headers: HEADERS }); + + expect(resp.status()).toBe(200); + const body = await resp.json(); + expect(body.code).toBe(0); + + // 等待启动 + await new Promise(r => setTimeout(r, 2000)); + + // 验证状态变为运行 + const statusResp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const statusBody = await statusResp.json(); + expect(statusBody.data.isRunning).toBe(true); + }); + + test('未知端点返回404', async () => { + const ctx = await request.newContext(); + const resp = await ctx.get(`${API_BASE}/api/collector/nonexistent`, { headers: HEADERS }); + + expect(resp.status()).toBe(404); + }); +}); + +// ============================================================ +// 测试套件2: 采集数据流程验证 +// ============================================================ +test.describe('采集数据流程验证', () => { + + test('采集服务启动后工作线程数 > 0', async () => { + const ctx = await request.newContext(); + + // 确保启动 + await ctx.post(`${API_BASE}/api/collector/start`, { headers: HEADERS }); + await new Promise(r => setTimeout(r, 3000)); + + const resp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const body = await resp.json(); + + expect(body.data.isRunning).toBe(true); + expect(body.data.workerCount).toBeGreaterThanOrEqual(0); + }); + + test('采集服务运行时间持续增长', async () => { + const ctx = await request.newContext(); + + await ctx.post(`${API_BASE}/api/collector/start`, { headers: HEADERS }); + await new Promise(r => setTimeout(r, 2000)); + + const resp1 = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const body1 = await resp1.json(); + const uptime1 = body1.data.uptimeSeconds; + + await new Promise(r => setTimeout(r, 3000)); + + const resp2 = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const body2 = await resp2.json(); + const uptime2 = body2.data.uptimeSeconds; + + expect(uptime2).toBeGreaterThanOrEqual(uptime1); + }); + + test('停止后再启动,状态正确切换', async () => { + const ctx = await request.newContext(); + + // 停止 + await ctx.post(`${API_BASE}/api/collector/stop`, { headers: HEADERS }); + await new Promise(r => setTimeout(r, 1000)); + + let statusResp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + let statusBody = await statusResp.json(); + expect(statusBody.data.isRunning).toBe(false); + + // 启动 + await ctx.post(`${API_BASE}/api/collector/start`, { headers: HEADERS }); + await new Promise(r => setTimeout(r, 2000)); + + statusResp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + statusBody = await statusResp.json(); + expect(statusBody.data.isRunning).toBe(true); + }); + + test('工作线程状态包含预期字段', async () => { + const ctx = await request.newContext(); + + await ctx.post(`${API_BASE}/api/collector/start`, { headers: HEADERS }); + await new Promise(r => setTimeout(r, 3000)); + + const resp = await ctx.get(`${API_BASE}/api/collector/status`, { headers: HEADERS }); + const body = await resp.json(); + + if (body.data.workerCount > 0) { + const workers = body.data.workers; + expect(Array.isArray(workers)).toBe(true); + if (workers.length > 0) { + const w = workers[0]; + expect(w).toHaveProperty('addressId'); + expect(w).toHaveProperty('url'); + expect(w).toHaveProperty('isRunning'); + } + } + }); +});