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) + }) })