001project_wildgrowth/backend/scripts/test-created-by-visibility.ts

421 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 测试 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<T = any> {
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<void> {
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<LoginResponse>(`${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<CreateCourseResponse>(
`${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<CourseResponse>(
`${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<CourseListResponse>(`${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<CourseListResponse>(`${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<CourseListResponse>(`${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<CourseResponse>(
`${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<CreateCourseResponse>(
`${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<ApiResponse>(
`${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();