From 72745456d2be1c23caef4d246be1d98eb41143d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Fri, 13 Mar 2026 15:18:46 +0800 Subject: [PATCH] test: add integration tests for configuration API and database operations --- e2e/src/tests/integration/config-api.spec.ts | 224 ++++++++++++++++++ .../tests/integration/config-database.spec.ts | 215 +++++++++++++++++ .../integration/config-permission.spec.ts | 127 ++++++++++ 3 files changed, 566 insertions(+) create mode 100644 e2e/src/tests/integration/config-api.spec.ts create mode 100644 e2e/src/tests/integration/config-database.spec.ts create mode 100644 e2e/src/tests/integration/config-permission.spec.ts diff --git a/e2e/src/tests/integration/config-api.spec.ts b/e2e/src/tests/integration/config-api.spec.ts new file mode 100644 index 0000000..ab87177 --- /dev/null +++ b/e2e/src/tests/integration/config-api.spec.ts @@ -0,0 +1,224 @@ +import { test, expect } from '@playwright/test'; + +test.describe('配置API端点测试', () => { + test('GET /api/admin/config - 获取所有配置', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.get('/api/admin/config'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + expect(Array.isArray(body.configs)).toBe(true); + }); + + test('GET /api/admin/config?category=feature - 按分类过滤', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.get('/api/admin/config?category=feature'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + expect(Array.isArray(body.configs)).toBe(true); + + body.configs.forEach(config => { + expect(config.category).toBe('feature'); + }); + }); + + test('GET /api/admin/config?key=xxx - 按key获取单个配置', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.get('/api/admin/config?key=feature_services'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + expect(Array.isArray(body.configs)).toBe(true); + expect(body.configs.length).toBe(1); + expect(body.configs[0].key).toBe('feature_services'); + }); + + test('POST /api/admin/config - 创建新配置', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const newConfig = { + key: 'test_config_' + Date.now(), + value: { enabled: true, testValue: 'test' }, + category: 'feature', + description: '测试配置' + }; + + const response = await request.post('/api/admin/config', { + data: newConfig + }); + + expect(response.status()).toBe(201); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + expect(body.configs.length).toBe(1); + expect(body.configs[0].key).toBe(newConfig.key); + expect(body.configs[0].value).toEqual(newConfig.value); + }); + + test('POST /api/admin/config - 缺少必要字段', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const invalidConfig = { + key: 'test_config', + value: { enabled: true } + }; + + const response = await request.post('/api/admin/config', { + data: invalidConfig + }); + + expect(response.status()).toBe(400); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('PUT /api/admin/config - 批量更新配置', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const updates = [ + { key: 'feature_services', value: { enabled: false } }, + { key: 'feature_products', value: { enabled: true } } + ]; + + const response = await request.put('/api/admin/config', { + data: { configs: updates } + }); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + expect(Array.isArray(body.configs)).toBe(true); + expect(body.configs.length).toBe(2); + }); + + test('PUT /api/admin/config - 无效的数据格式', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const invalidData = { + configs: 'not an array' + }; + + const response = await request.put('/api/admin/config', { + data: invalidData + }); + + expect(response.status()).toBe(400); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('DELETE /api/admin/config?key=xxx - 删除配置', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.delete('/api/admin/config?key=feature_services'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.data).toBeDefined(); + expect(body.data.success).toBe(true); + }); + + test('DELETE /api/admin/config - 缺少key参数', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.delete('/api/admin/config'); + + expect(response.status()).toBe(400); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('GET /api/config - 公开配置API', async ({ request }) => { + const response = await request.get('/api/config'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.data).toBeDefined(); + expect(typeof body.data).toBe('object'); + }); + + test('API响应格式验证 - success响应', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.get('/api/admin/config'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body).toHaveProperty('success'); + expect(body).toHaveProperty('configs'); + expect(body.success).toBe(true); + }); + + test('API响应格式验证 - error响应', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.delete('/api/admin/config'); + + expect(response.status()).toBe(400); + const body = await response.json(); + expect(body).toHaveProperty('success'); + expect(body).toHaveProperty('error'); + expect(body.success).toBe(false); + }); +}); diff --git a/e2e/src/tests/integration/config-database.spec.ts b/e2e/src/tests/integration/config-database.spec.ts new file mode 100644 index 0000000..1bfb994 --- /dev/null +++ b/e2e/src/tests/integration/config-database.spec.ts @@ -0,0 +1,215 @@ +import { test, expect } from '@playwright/test'; + +test.describe('配置数据库操作测试', () => { + test('创建配置 - 数据库插入', async ({ page, context }) => { + const adminPage = await context.newPage(); + + await adminPage.goto('/admin/login'); + await adminPage.fill('input[type="email"]', 'admin@novalon.cn'); + await adminPage.fill('input[type="password"]', 'admin123456'); + await adminPage.click('button[type="submit"]'); + await adminPage.waitForURL('/admin'); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const servicesConfig = adminPage.locator('text=功能配置').locator('..').locator('..'); + const textarea = servicesConfig.locator('textarea').first(); + + await textarea.fill('test_service'); + await servicesConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + const serviceCards = page.locator('#services .card'); + const count = await serviceCards.count(); + expect(count).toBeGreaterThan(0); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const configExists = adminPage.locator('textarea').first(); + expect(configExists).toBeVisible(); + }); + + test('更新配置 - 数据库更新', async ({ page, context }) => { + const adminPage = await context.newPage(); + + await adminPage.goto('/admin/login'); + await adminPage.fill('input[type="email"]', 'admin@novalon.cn'); + await adminPage.fill('input[type="password"]', 'admin123456'); + await adminPage.click('button[type="submit"]'); + await adminPage.waitForURL('/admin'); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const newsConfig = adminPage.locator('text=功能配置').locator('..').locator('..'); + const displayCountInput = newsConfig.locator('input[type="number"]').nth(1); + + await displayCountInput.fill('5'); + await newsConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + const newsCards = page.locator('#news .card'); + const count = await newsCards.count(); + expect(count).toBe(5); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + await displayCountInput.fill('10'); + await newsConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + const newCount = await newsCards.count(); + expect(newCount).toBe(10); + }); + + test('删除配置 - 数据库删除', async ({ page, context }) => { + const adminPage = await context.newPage(); + + await adminPage.goto('/admin/login'); + await adminPage.fill('input[type="email"]', 'admin@novalon.cn'); + await adminPage.fill('input[type="password"]', 'admin123456'); + await adminPage.click('button[type="submit"]'); + await adminPage.waitForURL('/admin'); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const productsConfig = adminPage.locator('text=功能配置').locator('..').locator('..'); + const productsCheckbox = productsConfig.locator('input[type="checkbox"]').nth(1); + + await productsCheckbox.check(); + await productsConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + await expect(page.locator('#products')).toBeVisible(); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + await productsCheckbox.uncheck(); + await productsConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + await expect(page.locator('#products')).not.toBeVisible(); + }); + + test('批量更新配置 - 数据库事务', async ({ page, context }) => { + const adminPage = await context.newPage(); + + await adminPage.goto('/admin/login'); + await adminPage.fill('input[type="email"]', 'admin@novalon.cn'); + await adminPage.fill('input[type="password"]', 'admin123456'); + await adminPage.click('button[type="submit"]'); + await adminPage.waitForURL('/admin'); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const featureConfig = adminPage.locator('text=功能配置').locator('..').locator('..'); + const checkboxes = featureConfig.locator('input[type="checkbox"]'); + + const initialServicesState = await checkboxes.nth(0).isChecked(); + const initialProductsState = await checkboxes.nth(1).isChecked(); + const initialNewsState = await checkboxes.nth(2).isChecked(); + + await checkboxes.nth(0).setChecked(!initialServicesState); + await checkboxes.nth(1).setChecked(!initialProductsState); + await checkboxes.nth(2).setChecked(!initialNewsState); + + await featureConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 10000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + const finalServicesVisible = await page.locator('#services').isVisible(); + const finalProductsVisible = await page.locator('#products').isVisible(); + const finalNewsVisible = await page.locator('#news').isVisible(); + + expect(finalServicesVisible).toBe(!initialServicesState); + expect(finalProductsVisible).toBe(!initialProductsState); + expect(finalNewsVisible).toBe(!initialNewsState); + }); + + test('配置数据完整性 - updatedAt字段更新', async ({ page, context }) => { + const adminPage = await context.newPage(); + + await adminPage.goto('/admin/login'); + await adminPage.fill('input[type="email"]', 'admin@novalon.cn'); + await adminPage.fill('input[type="password"]', 'admin123456'); + await adminPage.click('button[type="submit"]'); + await adminPage.waitForURL('/admin'); + + await adminPage.goto('/admin/settings'); + await adminPage.waitForLoadState('networkidle'); + + const featureConfig = adminPage.locator('text=功能配置').locator('..').locator('..'); + const checkbox = featureConfig.locator('input[type="checkbox"]').first(); + + await checkbox.check(); + await featureConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await adminPage.waitForTimeout(2000); + + await checkbox.uncheck(); + await featureConfig.locator('button:has-text("保存")').click(); + await adminPage.waitForSelector('text=保存成功', { timeout: 5000 }); + + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + await expect(page.locator('#services')).not.toBeVisible(); + }); + + test('配置查询 - 按分类过滤', async ({ page }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + await page.goto('/admin/settings'); + await page.waitForLoadState('networkidle'); + + const featureConfig = page.locator('text=功能配置').locator('..').locator('..'); + await expect(featureConfig).toBeVisible(); + + const featureCount = await featureConfig.locator('input[type="checkbox"]').count(); + expect(featureCount).toBeGreaterThan(0); + }); + + test('配置查询 - 按key精确查找', async ({ page }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + await page.goto('/admin/settings'); + await page.waitForLoadState('networkidle'); + + const featureConfig = page.locator('text=功能配置').locator('..').locator('..'); + await expect(featureConfig).toBeVisible(); + + const checkboxes = featureConfig.locator('input[type="checkbox"]'); + const count = await checkboxes.count(); + expect(count).toBeGreaterThan(0); + }); +}); diff --git a/e2e/src/tests/integration/config-permission.spec.ts b/e2e/src/tests/integration/config-permission.spec.ts new file mode 100644 index 0000000..bdca439 --- /dev/null +++ b/e2e/src/tests/integration/config-permission.spec.ts @@ -0,0 +1,127 @@ +import { test, expect } from '@playwright/test'; + +test.describe('配置权限验证测试', () => { + test('未登录访问配置API - GET请求', async ({ request }) => { + const response = await request.get('/api/admin/config'); + + expect(response.status()).toBe(403); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('未登录访问配置API - POST请求', async ({ request }) => { + const newConfig = { + key: 'test_config', + value: { enabled: true }, + category: 'feature' + }; + + const response = await request.post('/api/admin/config', { + data: newConfig + }); + + expect(response.status()).toBe(403); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('未登录访问配置API - PUT请求', async ({ request }) => { + const updates = [ + { key: 'feature_services', value: { enabled: false } } + ]; + + const response = await request.put('/api/admin/config', { + data: { configs: updates } + }); + + expect(response.status()).toBe(403); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('未登录访问配置API - DELETE请求', async ({ request }) => { + const response = await request.delete('/api/admin/config?key=feature_services'); + + expect(response.status()).toBe(403); + const body = await response.json(); + expect(body.success).toBe(false); + expect(body.error).toBeDefined(); + }); + + test('管理员访问配置API - GET请求成功', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.get('/api/admin/config'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + }); + + test('管理员访问配置API - POST请求成功', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const newConfig = { + key: 'test_config_' + Date.now(), + value: { enabled: true }, + category: 'feature' + }; + + const response = await request.post('/api/admin/config', { + data: newConfig + }); + + expect(response.status()).toBe(201); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + }); + + test('管理员访问配置API - PUT请求成功', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const updates = [ + { key: 'feature_services', value: { enabled: false } } + ]; + + const response = await request.put('/api/admin/config', { + data: { configs: updates } + }); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.configs).toBeDefined(); + }); + + test('管理员访问配置API - DELETE请求成功', async ({ page, request }) => { + await page.goto('/admin/login'); + await page.fill('input[type="email"]', 'admin@novalon.cn'); + await page.fill('input[type="password"]', 'admin123456'); + await page.click('button[type="submit"]'); + await page.waitForURL('/admin'); + + const response = await request.delete('/api/admin/config?key=feature_services'); + + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.success).toBe(true); + expect(body.data).toBeDefined(); + }); +});