第一版提交,答题功能OK,题库管理待完善
This commit is contained in:
363
test/openspec.test.ts
Normal file
363
test/openspec.test.ts
Normal file
@@ -0,0 +1,363 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
|
||||
import request from 'supertest';
|
||||
import { app } from '../api/app';
|
||||
import { initDatabase } from '../api/database';
|
||||
|
||||
// 测试数据
|
||||
const testUser = {
|
||||
name: '测试用户',
|
||||
phone: '13800138000',
|
||||
password: 'test123'
|
||||
};
|
||||
|
||||
const testCategory = {
|
||||
name: '测试类别',
|
||||
description: '测试类别描述'
|
||||
};
|
||||
|
||||
const testSubject = {
|
||||
name: '测试科目',
|
||||
totalScore: 100,
|
||||
timeLimitMinutes: 60,
|
||||
typeRatios: { single: 40, multiple: 30, judgment: 30 },
|
||||
categoryRatios: { '通用': 100 }
|
||||
};
|
||||
|
||||
const testQuestion = {
|
||||
content: '测试题目内容',
|
||||
type: 'single',
|
||||
options: ['选项A', '选项B', '选项C', '选项D'],
|
||||
answer: '选项A',
|
||||
score: 10,
|
||||
category: '通用'
|
||||
};
|
||||
|
||||
describe('OpenSpec 1.1.0 功能测试', () => {
|
||||
let server: any;
|
||||
let userId: string;
|
||||
let categoryId: string;
|
||||
let subjectId: string;
|
||||
let questionId: string;
|
||||
let taskId: string;
|
||||
let adminToken: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// 初始化数据库
|
||||
await initDatabase();
|
||||
|
||||
// 启动测试服务器
|
||||
server = app.listen(0);
|
||||
|
||||
// 创建管理员用户并获取token
|
||||
const adminLogin = await request(server)
|
||||
.post('/api/admin/login')
|
||||
.send({ username: 'admin', password: 'admin123' });
|
||||
|
||||
adminToken = adminLogin.body.data.token;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('1. 题目类别管理', () => {
|
||||
it('应该创建题目类别', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/admin/question-categories')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(testCategory);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe(testCategory.name);
|
||||
|
||||
categoryId = response.body.data.id;
|
||||
});
|
||||
|
||||
it('应该获取题目类别列表', async () => {
|
||||
const response = await request(server)
|
||||
.get('/api/question-categories');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data)).toBe(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('应该更新题目类别', async () => {
|
||||
const updatedData = { ...testCategory, name: '更新后的类别' };
|
||||
|
||||
const response = await request(server)
|
||||
.put(`/api/admin/question-categories/${categoryId}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(updatedData);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe('更新后的类别');
|
||||
});
|
||||
});
|
||||
|
||||
describe('2. 考试科目管理', () => {
|
||||
it('应该创建考试科目', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/admin/subjects')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(testSubject);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe(testSubject.name);
|
||||
|
||||
subjectId = response.body.data.id;
|
||||
});
|
||||
|
||||
it('应该获取考试科目列表', async () => {
|
||||
const response = await request(server)
|
||||
.get('/api/exam-subjects');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data)).toBe(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('应该更新考试科目', async () => {
|
||||
const updatedData = { ...testSubject, name: '更新后的科目' };
|
||||
|
||||
const response = await request(server)
|
||||
.put(`/api/admin/subjects/${subjectId}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(updatedData);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe('更新后的科目');
|
||||
});
|
||||
});
|
||||
|
||||
describe('3. 题目管理(带类别)', () => {
|
||||
it('应该创建带类别的题目', async () => {
|
||||
const questionWithCategory = {
|
||||
...testQuestion,
|
||||
category: testCategory.name
|
||||
};
|
||||
|
||||
const response = await request(server)
|
||||
.post('/api/questions')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(questionWithCategory);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.category).toBe(testCategory.name);
|
||||
|
||||
questionId = response.body.data.id;
|
||||
});
|
||||
|
||||
it('应该按类别筛选题目', async () => {
|
||||
const response = await request(server)
|
||||
.get('/api/questions')
|
||||
.query({ category: testCategory.name });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(0);
|
||||
expect(response.body.data[0].category).toBe(testCategory.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('4. 用户管理', () => {
|
||||
it('应该创建用户', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/users')
|
||||
.send(testUser);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe(testUser.name);
|
||||
|
||||
userId = response.body.data.id;
|
||||
});
|
||||
|
||||
it('应该验证用户密码', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/users/validate')
|
||||
.send({ name: testUser.name, phone: testUser.phone, password: testUser.password });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('应该拒绝错误的密码', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/users/validate')
|
||||
.send({ name: testUser.name, phone: testUser.phone, password: 'wrongpassword' });
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('应该获取用户列表(管理员)', async () => {
|
||||
const response = await request(server)
|
||||
.get('/api/admin/users')
|
||||
.set('Authorization', `Bearer ${adminToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data)).toBe(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('5. 考试任务管理', () => {
|
||||
it('应该创建考试任务', async () => {
|
||||
const taskData = {
|
||||
name: '测试任务',
|
||||
subjectId: subjectId,
|
||||
startAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // 昨天
|
||||
endAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 明天
|
||||
userIds: [userId]
|
||||
};
|
||||
|
||||
const response = await request(server)
|
||||
.post('/api/admin/tasks')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send(taskData);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.name).toBe('测试任务');
|
||||
|
||||
taskId = response.body.data.id;
|
||||
});
|
||||
|
||||
it('应该获取用户任务列表', async () => {
|
||||
const response = await request(server)
|
||||
.get(`/api/exam-tasks/user/${userId}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data)).toBe(true);
|
||||
expect(response.body.data.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('6. 基于科目的答题', () => {
|
||||
it('应该基于科目生成试卷', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/quiz/generate')
|
||||
.send({
|
||||
userId: userId,
|
||||
subjectId: subjectId
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data.questions)).toBe(true);
|
||||
expect(response.body.data.questions.length).toBeGreaterThan(0);
|
||||
expect(response.body.data.totalScore).toBe(testSubject.totalScore);
|
||||
expect(response.body.data.timeLimit).toBe(testSubject.timeLimitMinutes);
|
||||
});
|
||||
|
||||
it('应该基于任务生成试卷', async () => {
|
||||
const response = await request(server)
|
||||
.post('/api/quiz/generate')
|
||||
.send({
|
||||
userId: userId,
|
||||
taskId: taskId
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(Array.isArray(response.body.data.questions)).toBe(true);
|
||||
expect(response.body.data.questions.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('7. 答题提交(带科目/任务信息)', () => {
|
||||
let questions: any[];
|
||||
|
||||
beforeAll(async () => {
|
||||
// 获取题目用于测试
|
||||
const response = await request(server)
|
||||
.post('/api/quiz/generate')
|
||||
.send({ userId: userId, subjectId: subjectId });
|
||||
|
||||
questions = response.body.data.questions;
|
||||
});
|
||||
|
||||
it('应该提交带科目信息的答案', async () => {
|
||||
const answers = questions.map((q: any) => ({
|
||||
questionId: q.id,
|
||||
userAnswer: q.answer,
|
||||
score: q.score,
|
||||
isCorrect: true
|
||||
}));
|
||||
|
||||
const response = await request(server)
|
||||
.post('/api/quiz/submit')
|
||||
.send({
|
||||
userId: userId,
|
||||
subjectId: subjectId,
|
||||
answers: answers
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.recordId).toBeDefined();
|
||||
});
|
||||
|
||||
it('应该提交带任务信息的答案', async () => {
|
||||
const answers = questions.map((q: any) => ({
|
||||
questionId: q.id,
|
||||
userAnswer: q.answer,
|
||||
score: q.score,
|
||||
isCorrect: true
|
||||
}));
|
||||
|
||||
const response = await request(server)
|
||||
.post('/api/quiz/submit')
|
||||
.send({
|
||||
userId: userId,
|
||||
taskId: taskId,
|
||||
answers: answers
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.recordId).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('8. 数据清理', () => {
|
||||
it('应该删除考试任务', async () => {
|
||||
const response = await request(server)
|
||||
.delete(`/api/admin/tasks/${taskId}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('应该删除考试科目', async () => {
|
||||
const response = await request(server)
|
||||
.delete(`/api/admin/subjects/${subjectId}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('应该删除题目类别', async () => {
|
||||
const response = await request(server)
|
||||
.delete(`/api/admin/question-categories/${categoryId}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
71
test/test_openspec_features.js
Normal file
71
test/test_openspec_features.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
|
||||
const testAPI = async () => {
|
||||
try {
|
||||
console.log('🚀 开始测试 OpenSpec 1.1.0 新功能...\n');
|
||||
|
||||
// 1. 测试题目类别管理
|
||||
console.log('📋 1. 测试题目类别管理');
|
||||
|
||||
// 获取题目类别列表
|
||||
const categoriesResponse = await axios.get(`${API_BASE_URL}/question-categories`);
|
||||
console.log('✅ 获取题目类别列表成功:', categoriesResponse.data.data.length, '个类别');
|
||||
|
||||
// 2. 测试考试科目管理
|
||||
console.log('\n📚 2. 测试考试科目管理');
|
||||
|
||||
// 获取考试科目列表
|
||||
const subjectsResponse = await axios.get(`${API_BASE_URL}/exam-subjects`);
|
||||
console.log('✅ 获取考试科目列表成功:', subjectsResponse.data.data.length, '个科目');
|
||||
|
||||
// 3. 测试用户登录(带密码验证)
|
||||
console.log('\n👤 3. 测试用户登录(带密码验证)');
|
||||
|
||||
const userData = {
|
||||
name: '测试用户',
|
||||
phone: '13800138001',
|
||||
password: 'test123'
|
||||
};
|
||||
|
||||
const userResponse = await axios.post(`${API_BASE_URL}/users`, userData);
|
||||
console.log('✅ 创建用户成功:', userResponse.data.data.name);
|
||||
|
||||
// 测试密码验证
|
||||
const validateResponse = await axios.post(`${API_BASE_URL}/users/validate`, {
|
||||
name: userData.name,
|
||||
phone: userData.phone,
|
||||
password: userData.password
|
||||
});
|
||||
console.log('✅ 密码验证成功');
|
||||
|
||||
// 4. 测试基于科目的答题
|
||||
console.log('\n📝 4. 测试基于科目的答题');
|
||||
|
||||
if (subjectsResponse.data.data.length > 0) {
|
||||
const subjectId = subjectsResponse.data.data[0].id;
|
||||
const userId = userResponse.data.data.id;
|
||||
|
||||
const quizResponse = await axios.post(`${API_BASE_URL}/quiz/generate`, {
|
||||
userId: userId,
|
||||
subjectId: subjectId
|
||||
});
|
||||
|
||||
console.log('✅ 基于科目生成试卷成功:');
|
||||
console.log(' - 题目数量:', quizResponse.data.data.questions.length);
|
||||
console.log(' - 总分:', quizResponse.data.data.totalScore);
|
||||
console.log(' - 时长:', quizResponse.data.data.timeLimit, '分钟');
|
||||
}
|
||||
|
||||
console.log('\n🎉 所有测试通过!OpenSpec 1.1.0 功能正常运行。');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 测试失败:', error.response?.data?.message || error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// 等待服务器启动后执行测试
|
||||
setTimeout(() => {
|
||||
testAPI();
|
||||
}, 5000);
|
||||
54
test/test_password_validation.js
Normal file
54
test/test_password_validation.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
|
||||
const testPasswordValidation = async () => {
|
||||
console.log('🔒 测试密码验证修复...\n');
|
||||
|
||||
try {
|
||||
// 创建测试用户
|
||||
const userData = {
|
||||
name: '密码测试用户',
|
||||
phone: '13800138002',
|
||||
password: 'correctpassword'
|
||||
};
|
||||
|
||||
console.log('1. 创建测试用户...');
|
||||
const createResponse = await axios.post(`${API_BASE_URL}/users`, userData);
|
||||
console.log('✅ 用户创建成功');
|
||||
|
||||
// 测试正确密码
|
||||
console.log('\n2. 测试正确密码验证...');
|
||||
const correctPasswordResponse = await axios.post(`${API_BASE_URL}/users/validate`, {
|
||||
name: userData.name,
|
||||
phone: userData.phone,
|
||||
password: userData.password
|
||||
});
|
||||
console.log('✅ 正确密码验证通过');
|
||||
|
||||
// 测试错误密码
|
||||
console.log('\n3. 测试错误密码验证...');
|
||||
try {
|
||||
await axios.post(`${API_BASE_URL}/users/validate`, {
|
||||
name: userData.name,
|
||||
phone: userData.phone,
|
||||
password: 'wrongpassword'
|
||||
});
|
||||
console.log('❌ 错误:错误密码竟然通过了验证!');
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 400) {
|
||||
console.log('✅ 错误密码正确被拒绝');
|
||||
} else {
|
||||
console.log('❌ 意外的错误:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n🎉 密码验证修复测试完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 测试失败:', error.response?.data?.message || error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// 执行测试
|
||||
testPasswordValidation();
|
||||
Reference in New Issue
Block a user