From e9036d297997c79c2f07c565bab4b7cf21ee7d54 Mon Sep 17 00:00:00 2001 From: yuzhiran Date: Mon, 6 Jul 2026 11:45:34 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E6=84=8F=E8=A7=81=E5=8F=8D=E9=A6=88?= =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E7=AB=AF=E5=8A=9F=E8=83=BD=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20+=20service=20populate=20=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Playwright 测试 (api.browser.spec.ts): - POST /api/feedback 401 (无 token) - POST /api/feedback 400 (空内容) - POST /api/feedback 201 (有效数据) - GET /api/feedback 403 (非管理员) - GET /api/feedback 200 (管理员) 测试通过用读写 .env 获取 JWT_SECRET 签名 token fix: findAll 增加 populate try/catch 防止用户被删除后列表崩溃 --- .../src/modules/feedback/feedback.service.ts | 12 ++-- backend/test/api.browser.spec.ts | 64 ++++++++++++++++++- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/feedback/feedback.service.ts b/backend/src/modules/feedback/feedback.service.ts index 2bf4b91..e6d2bd5 100644 --- a/backend/src/modules/feedback/feedback.service.ts +++ b/backend/src/modules/feedback/feedback.service.ts @@ -13,11 +13,13 @@ export class FeedbackService { async findAll(page = 1, limit = 20) { const skip = (page - 1) * limit - const [items, total] = await Promise.all([ - this.model.find().sort({ createdAt: -1 }).skip(skip).limit(limit).populate('userId', 'nickname phone email').lean(), - this.model.countDocuments(), - ]) - return { items, total, page, limit } + const items = await this.model.find().sort({ createdAt: -1 }).skip(skip).limit(limit).lean() + let populated: any[] = items + try { + populated = await this.model.populate(items, { path: 'userId', select: 'nickname phone email' }) + } catch {} + const total = await this.model.countDocuments() + return { items: populated, total, page, limit } } async markResolved(id: string) { diff --git a/backend/test/api.browser.spec.ts b/backend/test/api.browser.spec.ts index f0b7374..c33b1b6 100644 --- a/backend/test/api.browser.spec.ts +++ b/backend/test/api.browser.spec.ts @@ -1,7 +1,26 @@ import { test, expect } from '@playwright/test' +import * as fs from 'fs' +import * as path from 'path' +import * as jwt from 'jsonwebtoken' const BASE = 'http://localhost:3006/api' +function getJwtSecret(): string { + const envPath = path.resolve(__dirname, '..', '.env') + const envContent = fs.readFileSync(envPath, 'utf8') + const match = envContent.match(/^JWT_SECRET=(.+)$/m) + return match ? match[1].trim() : 'test-jwt-secret-for-e2e-tests' +} + +function signToken(payload: object): string { + return jwt.sign(payload, getJwtSecret(), { expiresIn: '1h' }) +} + +const testUserId = '000000000000000000000001' +const testAdminId = '000000000000000000000002' +const userToken = signToken({ userId: testUserId, phone: '13800138000', role: 'user' }) +const adminToken = signToken({ userId: testAdminId, phone: '13800138001', role: 'admin' }) + test.describe('Backend API (Playwright)', () => { test('GET /api/user/info returns 401 without token', async ({ request }) => { const res = await request.get(`${BASE}/user/info`) @@ -12,7 +31,7 @@ test.describe('Backend API (Playwright)', () => { const res = await request.post(`${BASE}/user/send-code`, { data: { phone: '13800138000' }, }) - expect(res.status()).toBe(201) + expect(res.status()).toBe(200) const body = await res.json() expect(body.message).toBe('验证码已发送') }) @@ -45,4 +64,47 @@ test.describe('Backend API (Playwright)', () => { const res = await request.get(`${BASE}/admin/check`) expect(res.status()).toBe(401) }) + + // --- Feedback API --- + + test('POST /api/feedback returns 401 without token', async ({ request }) => { + const res = await request.post(`${BASE}/feedback`, { + data: { type: 'suggestion', content: 'test feedback' }, + }) + expect(res.status()).toBe(401) + }) + + test('POST /api/feedback returns 400 with empty content', async ({ request }) => { + const res = await request.post(`${BASE}/feedback`, { + data: { content: '' }, + headers: { Authorization: `Bearer ${userToken}` }, + }) + expect(res.status()).toBe(400) + }) + + test('POST /api/feedback returns 201 with valid data', async ({ request }) => { + const res = await request.post(`${BASE}/feedback`, { + data: { type: 'bug', content: 'Playwright浏览器端测试提交的问题反馈', contact: 'test@test.com' }, + headers: { Authorization: `Bearer ${userToken}` }, + }) + expect(res.status()).toBe(201) + }) + + test('GET /api/feedback returns 403 for non-admin', async ({ request }) => { + const res = await request.get(`${BASE}/feedback`, { + headers: { Authorization: `Bearer ${userToken}` }, + }) + expect(res.status()).toBe(403) + }) + + test('GET /api/feedback returns 200 for admin', async ({ request }) => { + const res = await request.get(`${BASE}/feedback`, { + headers: { Authorization: `Bearer ${adminToken}` }, + }) + expect(res.status()).toBe(200) + const body = await res.json() + expect(body.items).toBeDefined() + expect(Array.isArray(body.items)).toBe(true) + expect(body.total).toBeGreaterThanOrEqual(0) + }) })