kindergarten_java/lesingle-edu-reading-platform-frontend/tests/e2e/school/api-test.spec.ts
En 40589f59e7 chore: 重命名项目目录
前后端目录重命名:
- reading-platform-java/ → lesingle-edu-reading-platform-backend/
- reading-platform-frontend/ → lesingle-edu-reading-platform-frontend/

更新相关文件:
- 所有 shell 脚本中的目录引用
- pom.xml 和 application.yml 中的项目名称
- package.json 中的项目名称
- .claude/CLAUDE.md 中的路径引用
- README 文档中的路径引用
2026-03-26 11:31:47 +08:00

247 lines
11 KiB
TypeScript

/**
* 学校端 API 接口测试 - 检测 500 报错
*
* 测试所有学校端 API 接口,检查是否有 500 服务器错误
*/
import { test, expect } from '@playwright/test';
// 学校端测试账号
const SCHOOL_ACCOUNT = 'school1';
const SCHOOL_PASSWORD = '123456';
// API 端点列表 (学校端所有接口)
const SCHOOL_APIS = [
// ==================== 认证相关 ====================
{ method: 'POST', path: '/api/v1/auth/login', name: '登录', requiresAuth: false },
// ==================== 仪表盘/统计 ====================
{ method: 'GET', path: '/api/v1/school/stats', name: '学校统计数据' },
{ method: 'GET', path: '/api/v1/school/stats/teachers', name: '活跃教师统计', params: { limit: '5' } },
{ method: 'GET', path: '/api/v1/school/stats/courses', name: '课程使用统计' },
{ method: 'GET', path: '/api/v1/school/stats/activities', name: '最近活动', params: { limit: '5' } },
{ method: 'GET', path: '/api/v1/school/stats/lesson-trend', name: '课程趋势', params: { months: '6' } },
{ method: 'GET', path: '/api/v1/school/stats/course-distribution', name: '课程分布' },
// ==================== 教师管理 ====================
{ method: 'GET', path: '/api/v1/school/teachers', name: '教师列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/teachers/1', name: '获取教师详情' },
// ==================== 学生管理 ====================
{ method: 'GET', path: '/api/v1/school/students', name: '学生列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/students/1', name: '获取学生详情' },
{ method: 'GET', path: '/api/v1/school/students/import/template', name: '学生导入模板' },
// ==================== 班级管理 ====================
{ method: 'GET', path: '/api/v1/school/classes', name: '班级列表' },
{ method: 'GET', path: '/api/v1/school/classes/1', name: '获取班级详情' },
{ method: 'GET', path: '/api/v1/school/classes/1/students', name: '班级学生列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/classes/1/teachers', name: '班级教师列表' },
// ==================== 家长管理 ====================
{ method: 'GET', path: '/api/v1/school/parents', name: '家长列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/parents/1', name: '获取家长详情' },
// ==================== 课程管理 ====================
{ method: 'GET', path: '/api/v1/school/courses', name: '学校课程列表' },
{ method: 'GET', path: '/api/v1/school/courses/1', name: '获取学校课程详情' },
// ==================== 套餐管理 ====================
{ method: 'GET', path: '/api/v1/school/package', name: '套餐信息' },
{ method: 'GET', path: '/api/v1/school/package/usage', name: '套餐使用情况' },
{ method: 'GET', path: '/api/v1/school/packages', name: '租户套餐列表' },
// ==================== 系统设置 ====================
{ method: 'GET', path: '/api/v1/school/settings', name: '系统设置' },
// ==================== 排课管理 ====================
{ method: 'GET', path: '/api/v1/school/schedules', name: '排课列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/schedules/1', name: '获取排课详情' },
{ method: 'GET', path: '/api/v1/school/schedules/timetable', name: '课程表', params: { startDate: '2026-03-01', endDate: '2026-03-31' } },
// ==================== 排课模板 ====================
{ method: 'GET', path: '/api/v1/school/schedule-templates', name: '排课模板列表' },
{ method: 'GET', path: '/api/v1/school/schedule-templates/1', name: '获取排课模板详情' },
// ==================== 任务管理 ====================
{ method: 'GET', path: '/api/v1/school/tasks', name: '任务列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/tasks/1', name: '获取任务详情' },
{ method: 'GET', path: '/api/v1/school/tasks/stats', name: '任务统计' },
{ method: 'GET', path: '/api/v1/school/tasks/stats/by-type', name: '任务统计 (按类型)' },
{ method: 'GET', path: '/api/v1/school/tasks/stats/by-class', name: '任务统计 (按班级)' },
{ method: 'GET', path: '/api/v1/school/tasks/stats/monthly', name: '任务月度统计', params: { months: '6' } },
{ method: 'GET', path: '/api/v1/school/tasks/1/completions', name: '任务完成情况' },
// ==================== 任务模板 ====================
{ method: 'GET', path: '/api/v1/school/task-templates', name: '任务模板列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/task-templates/1', name: '获取任务模板详情' },
{ method: 'GET', path: '/api/v1/school/task-templates/default/READING', name: '获取默认任务模板' },
// ==================== 成长记录 ====================
{ method: 'GET', path: '/api/v1/school/growth-records', name: '成长记录列表', params: { pageNum: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/growth-records/1', name: '获取成长记录详情' },
{ method: 'GET', path: '/api/v1/school/growth-records/recent', name: '最近成长记录', params: { limit: '5' } },
{ method: 'GET', path: '/api/v1/school/growth-records/student/1', name: '学生成长记录', params: { pageNum: '1', pageSize: '10' } },
// ==================== 数据报告 ====================
{ method: 'GET', path: '/api/v1/school/reports/overview', name: '报告概览' },
{ method: 'GET', path: '/api/v1/school/reports/teachers', name: '教师报告' },
{ method: 'GET', path: '/api/v1/school/reports/courses', name: '课程报告' },
{ method: 'GET', path: '/api/v1/school/reports/students', name: '学生报告' },
// ==================== 操作日志 ====================
{ method: 'GET', path: '/api/v1/school/operation-logs', name: '操作日志列表', params: { page: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/operation-logs/1', name: '获取操作日志详情' },
{ method: 'GET', path: '/api/v1/school/operation-logs/stats', name: '操作日志统计' },
// ==================== 通知 ====================
{ method: 'GET', path: '/api/v1/school/notifications', name: '通知列表', params: { page: '1', pageSize: '10' } },
{ method: 'GET', path: '/api/v1/school/notifications/unread-count', name: '未读通知数量' },
// ==================== 文件上传 ====================
{ method: 'POST', path: '/api/v1/files/oss/upload', name: 'OSS 文件上传', requiresAuth: true, isUpload: true },
// ==================== 数据导出 (需要特殊处理) ====================
// 这些接口返回二进制数据,需要特殊处理
// { method: 'GET', path: '/api/v1/school/export/lessons', name: '导出课程记录' },
// { method: 'GET', path: '/api/v1/school/export/teacher-stats', name: '导出教师统计' },
// { method: 'GET', path: '/api/v1/school/export/student-stats', name: '导出学生统计' },
];
// 错误报告
const errorReport: Array<{
api: typeof SCHOOL_APIS[0];
status: number;
error: string;
response?: any;
}> = [];
// 成功报告
const successReport: Array<{
api: typeof SCHOOL_APIS[0];
status: number;
}> = [];
test.describe('学校端 API 接口 500 错误检测', () => {
let authToken: string = '';
test.beforeAll('登录获取 Token', async ({ request }) => {
const response = await request.post('http://localhost:8080/api/v1/auth/login', {
data: {
username: SCHOOL_ACCOUNT,
password: SCHOOL_PASSWORD,
role: 'school',
},
});
const data = await response.json();
authToken = data.data?.token || '';
console.log('✅ 登录成功,获取 Token');
});
for (const api of SCHOOL_APIS) {
test(`${api.method} ${api.path} - ${api.name}`, async ({ request }) => {
const url = `http://localhost:8080${api.path}`;
try {
const options: any = {
headers: {
'Content-Type': api.isUpload ? 'multipart/form-data' : 'application/json',
...(authToken && { Authorization: `Bearer ${authToken}` }),
},
};
if (api.params) {
const queryString = new URLSearchParams(api.params).toString();
options.query = api.params;
}
let response;
switch (api.method) {
case 'GET':
response = await request.get(url, options);
break;
case 'POST':
response = await request.post(url, options);
break;
case 'PUT':
response = await request.put(url, options);
break;
case 'DELETE':
response = await request.delete(url, options);
break;
default:
throw new Error(`不支持的 HTTP 方法:${api.method}`);
}
const status = response.status();
// 检查是否为 500 错误
if (status >= 500) {
let errorBody;
try {
errorBody = await response.text();
} catch (e) {
errorBody = '无法读取响应体';
}
errorReport.push({
api,
status,
error: `服务器内部错误 (500)`,
response: errorBody,
});
console.error(`❌ [500 错误] ${api.method} ${api.path} - ${api.name}`);
console.error(` 响应:${errorBody?.substring(0, 200)}`);
} else {
successReport.push({ api, status });
}
// 断言:不应出现 500 错误
expect(status).toBeLessThan(500);
} catch (error: any) {
// 网络错误或其他异常也可能意味着后端有问题
errorReport.push({
api,
status: 0,
error: error.message || '未知错误',
});
console.error(`❌ [异常] ${api.method} ${api.path} - ${api.name}: ${error.message}`);
// 失败但继续执行
}
});
}
test.afterAll('生成测试报告', async () => {
console.log('\n' + '='.repeat(80));
console.log('学校端 API 接口测试报告');
console.log('='.repeat(80));
console.log(`总接口数:${SCHOOL_APIS.length}`);
console.log(`✅ 通过:${successReport.length}`);
console.log(`❌ 500 错误:${errorReport.length}`);
console.log('='.repeat(80));
if (errorReport.length > 0) {
console.log('\n📋 500 错误接口列表:\n');
errorReport.forEach((item, index) => {
console.log(`${index + 1}. [${item.api.method}] ${item.api.path}`);
console.log(` 名称:${item.api.name}`);
console.log(` 状态码:${item.status}`);
console.log(` 错误:${item.error}`);
if (item.response) {
console.log(` 响应:${item.response?.substring(0, 300)}...`);
}
console.log('');
});
} else {
console.log('\n🎉 所有接口测试通过,无 500 错误!');
}
console.log('\n' + '='.repeat(80));
});
});