/** * 测试 createdBy 和 visibility 功能 * 测试场景: * 1. 用户创建课程(AI生成)- 应该设置 createdBy 和 visibility='private',生成完成后自动发布 * 2. 查询课程列表 - 应该只看到公开课程和自己创建的课程 * 3. 修改自己创建的课程 - 应该成功 * 4. 修改他人创建的课程 - 应该失败(403) * 5. 删除自己创建的课程 - 应该成功 * 6. 删除他人创建的课程 - 应该失败(403) */ import axios from 'axios'; import * as dotenv from 'dotenv'; import { join } from 'path'; // 加载环境变量 dotenv.config({ path: join(__dirname, '../.env.test') }); const BASE_URL = process.env.API_BASE_URL || 'http://localhost:3000'; const TEST_EMAIL = process.env.TEST_USER_EMAIL || `test_${Date.now()}@example.com`; const TEST_PASSWORD = process.env.TEST_USER_PASSWORD || 'Test123456!'; let authToken: string = ''; let userId: string = ''; let createdCourseId: string = ''; let otherUserToken: string = ''; let otherUserId: string = ''; interface LoginResponse { success: boolean; data: { token: string; user: { id: string; email: string; }; }; } interface ApiResponse { success: boolean; data: T; message?: string; } interface CreateCourseResponse { success: boolean; data: { courseId: string; taskId: string; status: string; }; } interface CourseResponse { success: boolean; data: { course: { id: string; title: string; created_by?: string; visibility?: string; status?: string; }; }; } interface CourseListResponse { success: boolean; data: { courses: Array<{ id: string; title: string; visibility?: string; }>; }; } async function registerUser(email: string, password: string): Promise { try { // 尝试注册,如果接口不存在或用户已存在,则跳过 await axios.post(`${BASE_URL}/api/auth/register`, { email, password, username: `测试用户_${Date.now()}`, }); console.log(`✅ 用户注册成功: ${email}`); } catch (error: any) { if (error.response?.status === 404) { // 注册接口不存在,跳过注册,直接尝试登录 console.log(`ℹ️ 注册接口不存在,跳过注册: ${email}`); } else if (error.response?.status === 400 && (error.response?.data?.message?.includes('已存在') || error.response?.data?.message?.includes('exists'))) { console.log(`ℹ️ 用户已存在: ${email}`); } else { // 其他错误也忽略,直接尝试登录 console.log(`ℹ️ 注册失败,将尝试登录: ${error.response?.status || error.message}`); } } } async function login(email: string, password: string): Promise<{ token: string; userId: string }> { // 尝试使用测试 token try { const testTokenResponse = await axios.get<{ success: boolean; data: { token: string; user: { id: string } } }>(`${BASE_URL}/api/auth/test-token`); if (testTokenResponse.data.success && testTokenResponse.data.data.token) { console.log(`✅ 使用测试 token 登录成功`); return { token: testTokenResponse.data.data.token, userId: testTokenResponse.data.data.user.id, }; } } catch (error) { console.log(`ℹ️ 测试 token 不可用,尝试其他方式`); } // 如果测试 token 不可用,尝试普通登录(需要手机号和验证码) const response = await axios.post(`${BASE_URL}/api/auth/login`, { email, password, }); if (!response.data.success || !response.data.data.token) { throw new Error('登录失败'); } return { token: response.data.data.token, userId: response.data.data.user.id, }; } async function testCreateCourse() { console.log('\n📝 测试 1: 用户创建课程(AI生成)'); const content = `# 测试课程 这是一个测试课程的内容,用于验证 createdBy 和 visibility 功能。 ## 第一章:基础概念 这是第一章的内容。 ## 第二章:进阶应用 这是第二章的内容。 `; const response = await axios.post( `${BASE_URL}/api/ai/content/upload`, { content, style: 'essence', }, { headers: { Authorization: `Bearer ${authToken}`, }, } ); if (response.data.success && response.data.data.courseId) { createdCourseId = response.data.data.courseId; console.log(`✅ 课程创建成功,courseId: ${createdCourseId}`); console.log(` taskId: ${response.data.data.taskId}`); console.log(` status: ${response.data.data.status}`); // 等待一下,让课程创建完成 await new Promise(resolve => setTimeout(resolve, 2000)); // 查询课程详情,验证 createdBy 和 visibility const courseResponse = await axios.get( `${BASE_URL}/api/courses/${createdCourseId}`, { headers: { Authorization: `Bearer ${authToken}`, }, } ); const course = courseResponse.data.data.course; console.log(`\n📋 课程信息验证:`); console.log(` createdBy: ${course.created_by || 'null'} (应该是: ${userId})`); console.log(` visibility: ${course.visibility || 'N/A'} (应该是: private)`); console.log(` status: ${course.status || 'N/A'}`); if (course.created_by === userId && course.visibility === 'private') { console.log(`✅ createdBy 和 visibility 设置正确`); } else { console.log(`❌ createdBy 或 visibility 设置错误`); } } else { throw new Error('创建课程失败'); } } async function testQueryCourses() { console.log('\n📋 测试 2: 查询课程列表'); // 测试公开接口(未登录) const publicResponse = await axios.get(`${BASE_URL}/api/courses`); const publicCourses = publicResponse.data.data.courses || []; console.log(`✅ 公开接口返回 ${publicCourses.length} 个课程`); console.log(` 所有课程 visibility 应该是 'public': ${publicCourses.every((c: any) => c.visibility === 'public' || !c.visibility)}`); // 测试登录用户接口 const userResponse = await axios.get(`${BASE_URL}/api/courses`, { headers: { Authorization: `Bearer ${authToken}`, }, }); const userCourses = userResponse.data.data.courses || []; console.log(`✅ 登录用户接口返回 ${userCourses.length} 个课程`); // 验证应该包含自己创建的课程 const hasMyCourse = userCourses.some((c: any) => c.id === createdCourseId); console.log(` 包含自己创建的课程: ${hasMyCourse ? '✅' : '❌'}`); // 测试 my-courses 接口 const myCoursesResponse = await axios.get(`${BASE_URL}/api/my-courses`, { headers: { Authorization: `Bearer ${authToken}`, }, }); const myCourses = myCoursesResponse.data.data.courses || []; console.log(`✅ /api/my-courses 返回 ${myCourses.length} 个课程`); const hasMyCourseInList = myCourses.some((c: any) => c.id === createdCourseId); console.log(` 包含自己创建的课程: ${hasMyCourseInList ? '✅' : '❌'}`); } async function testUpdateOwnCourse() { console.log('\n✏️ 测试 3: 修改自己创建的课程'); try { const response = await axios.put( `${BASE_URL}/api/courses/${createdCourseId}`, { title: '修改后的测试课程标题', description: '这是修改后的描述', }, { headers: { Authorization: `Bearer ${authToken}`, }, } ); if (response.data.success) { console.log(`✅ 修改自己创建的课程成功`); console.log(` 新标题: ${response.data.data.course.title}`); } else { const errorMsg = (response.data as any).message || '未知错误'; console.log(`❌ 修改失败: ${errorMsg}`); } } catch (error: any) { if (error.response?.status === 403) { console.log(`❌ 修改失败: 权限不足(403)`); } else { console.log(`❌ 修改失败: ${error.message}`); } } } async function testUpdateOtherUserCourse() { console.log('\n✏️ 测试 4: 尝试修改他人创建的课程(应该失败)'); // 先创建一个其他用户的课程(如果还没有) if (!otherUserToken) { const otherEmail = `other_${Date.now()}@example.com`; await registerUser(otherEmail, TEST_PASSWORD); const loginResult = await login(otherEmail, TEST_PASSWORD); otherUserToken = loginResult.token; otherUserId = loginResult.userId; } // 尝试修改第一个用户创建的课程 try { await axios.put( `${BASE_URL}/api/courses/${createdCourseId}`, { title: '尝试修改他人课程', }, { headers: { Authorization: `Bearer ${otherUserToken}`, }, } ); console.log(`❌ 不应该成功修改他人课程`); } catch (error: any) { if (error.response?.status === 403) { console.log(`✅ 正确拒绝修改他人课程(403 Forbidden)`); } else { console.log(`⚠️ 返回了其他错误: ${error.response?.status} - ${error.message}`); } } } async function testDeleteOwnCourse() { console.log('\n🗑️ 测试 5: 删除自己创建的课程'); // 先创建一个用于删除的课程 const content = `# 待删除的测试课程`; const createResponse = await axios.post( `${BASE_URL}/api/ai/content/upload`, { content, style: 'essence', }, { headers: { Authorization: `Bearer ${authToken}`, }, } ); if (createResponse.data.success) { const courseIdToDelete = createResponse.data.data.courseId; console.log(` 创建了用于删除的课程: ${courseIdToDelete}`); // 等待课程创建完成 await new Promise(resolve => setTimeout(resolve, 2000)); try { const response = await axios.delete( `${BASE_URL}/api/courses/${courseIdToDelete}`, { headers: { Authorization: `Bearer ${authToken}`, }, } ); if (response.data.success) { console.log(`✅ 删除自己创建的课程成功`); } else { console.log(`❌ 删除失败: ${response.data.message || '未知错误'}`); } } catch (error: any) { if (error.response?.status === 403) { console.log(`❌ 删除失败: 权限不足(403)`); } else { console.log(`❌ 删除失败: ${error.message}`); } } } } async function testDeleteOtherUserCourse() { console.log('\n🗑️ 测试 6: 尝试删除他人创建的课程(应该失败)'); if (!otherUserToken) { const otherEmail = `other_${Date.now()}@example.com`; await registerUser(otherEmail, TEST_PASSWORD); const loginResult = await login(otherEmail, TEST_PASSWORD); otherUserToken = loginResult.token; otherUserId = loginResult.userId; } // 尝试删除第一个用户创建的课程 try { await axios.delete( `${BASE_URL}/api/courses/${createdCourseId}`, { headers: { Authorization: `Bearer ${otherUserToken}`, }, } ); console.log(`❌ 不应该成功删除他人课程`); } catch (error: any) { if (error.response?.status === 403) { console.log(`✅ 正确拒绝删除他人课程(403 Forbidden)`); } else { console.log(`⚠️ 返回了其他错误: ${error.response?.status} - ${error.message}`); } } } async function main() { console.log('🚀 开始测试 createdBy 和 visibility 功能\n'); console.log(`测试环境: ${BASE_URL}`); try { // 1. 注册和登录测试用户 console.log('\n👤 注册和登录测试用户...'); await registerUser(TEST_EMAIL, TEST_PASSWORD); const loginResult = await login(TEST_EMAIL, TEST_PASSWORD); authToken = loginResult.token; userId = loginResult.userId; console.log(`✅ 登录成功,userId: ${userId}`); // 2. 测试创建课程 await testCreateCourse(); // 3. 测试查询课程列表 await testQueryCourses(); // 4. 测试修改自己创建的课程 await testUpdateOwnCourse(); // 5. 测试修改他人创建的课程(应该失败) await testUpdateOtherUserCourse(); // 6. 测试删除自己创建的课程 await testDeleteOwnCourse(); // 7. 测试删除他人创建的课程(应该失败) await testDeleteOtherUserCourse(); console.log('\n✅ 所有测试完成!'); } catch (error: any) { console.error('\n❌ 测试失败:', error.message); if (error.response) { console.error('响应数据:', JSON.stringify(error.response.data, null, 2)); } process.exit(1); } } main();