195 lines
9.4 KiB
JavaScript
195 lines
9.4 KiB
JavaScript
/**
|
||
* P4-API - 会员端团课高级搜索 API 测试
|
||
* 流程:会员登录 → 按课程类型筛选 → 按标签筛选 → 类型+标签组合筛选 → 验证结果
|
||
*
|
||
* 使用 HTTP API(HMAC-SHA256 签名 + JWT Token)
|
||
* 前置条件:后端服务 http://192.168.110.64:8084 已启动
|
||
*/
|
||
const api = require('../helpers/api-helper');
|
||
|
||
let authToken = null;
|
||
let memberId = null;
|
||
let typeId = null;
|
||
let labelId = null;
|
||
|
||
describe('P4-API - 团课高级搜索', () => {
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤0:登录 & 准备筛选条件
|
||
// ════════════════════════════════════════════════════
|
||
beforeAll(async () => {
|
||
console.log('════════════════════════════════════════════');
|
||
console.log('P4-API - 团课高级搜索测试');
|
||
console.log('════════════════════════════════════════════');
|
||
|
||
const result = await api.memberLogin();
|
||
expect(result).not.toBeNull();
|
||
authToken = result.accessToken;
|
||
memberId = result.memberId;
|
||
console.log(`[P4-API] 会员登录成功: memberId=${memberId}`);
|
||
|
||
// 先获取课程类型列表,找第一个类型ID
|
||
const typeRes = await api.getCourseTypes();
|
||
if (api.isSuccess(typeRes)) {
|
||
const types = typeRes.data?.data || typeRes.data || [];
|
||
const records = Array.isArray(types) ? types : (types.records || types.content || []);
|
||
if (records.length > 0) {
|
||
typeId = records[0].id;
|
||
console.log(`[P4-API] 选择课程类型: id=${typeId}, name=${records[0].typeName || records[0].name}`);
|
||
}
|
||
}
|
||
|
||
// 尝试从课程列表获取第一个标签ID
|
||
try {
|
||
const searchRes = await api.searchCourses('', authToken);
|
||
if (api.isSuccess(searchRes)) {
|
||
const content = searchRes.data?.data?.content || searchRes.data?.content || searchRes.data || [];
|
||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||
// 从第一个课程取标签
|
||
for (const course of courses) {
|
||
const labels = course.labels || course.labelList || [];
|
||
if (Array.isArray(labels) && labels.length > 0) {
|
||
const firstLabel = labels[0];
|
||
labelId = typeof firstLabel === 'object' ? firstLabel.id : firstLabel;
|
||
const name = typeof firstLabel === 'object' ? firstLabel.labelName : '';
|
||
console.log(`[P4-API] 选择标签: id=${labelId}, name=${name}`);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.warn('[P4-API] 获取标签失败:', e.message);
|
||
}
|
||
}, 60000);
|
||
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤1:按课程类型筛选
|
||
// ════════════════════════════════════════════════════
|
||
describe('按课程类型筛选', () => {
|
||
test('TC-SEARCH-001: 按类型筛选应返回正确结果', async () => {
|
||
if (!typeId) {
|
||
console.warn('[TC-SEARCH-001] 无类型ID,跳过');
|
||
return;
|
||
}
|
||
|
||
const res = await api.searchCoursesByType(typeId, authToken);
|
||
expect(res.status).toBe(200);
|
||
|
||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||
console.log(`[TC-SEARCH-001] 类型筛选结果: ${courses.length} 个课程`);
|
||
|
||
// 验证所有返回课程的类型匹配
|
||
if (courses.length > 0) {
|
||
courses.slice(0, 3).forEach(c => {
|
||
console.log(` - ${c.courseName || c.name} (typeId=${c.courseTypeId || c.typeId})`);
|
||
});
|
||
}
|
||
|
||
expect(courses.length).toBeGreaterThanOrEqual(0);
|
||
});
|
||
});
|
||
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤2:按标签筛选
|
||
// ════════════════════════════════════════════════════
|
||
describe('按标签筛选', () => {
|
||
test('TC-SEARCH-002: 按标签筛选应返回正确结果', async () => {
|
||
if (!labelId) {
|
||
console.warn('[TC-SEARCH-002] 无标签ID,跳过');
|
||
return;
|
||
}
|
||
|
||
const res = await api.searchCoursesByLabel(labelId, authToken);
|
||
expect(res.status).toBe(200);
|
||
|
||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||
console.log(`[TC-SEARCH-002] 标签筛选结果: ${courses.length} 个课程`);
|
||
|
||
if (courses.length > 0) {
|
||
courses.slice(0, 3).forEach(c => {
|
||
const labels = (c.labels || c.labelList || []).map(l =>
|
||
typeof l === 'object' ? l.labelName : l
|
||
);
|
||
console.log(` - ${c.courseName || c.name} (labels: [${labels.join(', ')}])`);
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤3:类型+标签组合筛选
|
||
// ════════════════════════════════════════════════════
|
||
describe('类型+标签组合筛选', () => {
|
||
test('TC-SEARCH-003: 组合筛选应返回交集结果', async () => {
|
||
if (!typeId || !labelId) {
|
||
console.warn('[TC-SEARCH-003] 缺少类型或标签ID,跳过');
|
||
return;
|
||
}
|
||
|
||
const res = await api.searchCoursesCombined(typeId, labelId, authToken);
|
||
expect(res.status).toBe(200);
|
||
|
||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||
console.log(`[TC-SEARCH-003] 组合筛选结果: ${courses.length} 个课程`);
|
||
|
||
if (courses.length > 0) {
|
||
courses.slice(0, 3).forEach(c => {
|
||
console.log(` - ${c.courseName || c.name} (id=${c.id})`);
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤4:边界场景
|
||
// ════════════════════════════════════════════════════
|
||
describe('边界场景', () => {
|
||
test('TC-SEARCH-004: 搜索不存在类型返回200(后端可能忽略无效筛选)', async () => {
|
||
const res = await api.searchCoursesByType(99999, authToken);
|
||
// 应确保API正常响应,即使筛选参数无效也不应该崩溃
|
||
console.log(`[TC-SEARCH-004] 不存在类型: status=${res.status}`);
|
||
expect(res.status).toBe(200);
|
||
});
|
||
|
||
test('TC-SEARCH-005: 搜索不存在标签返回200(后端可能忽略无效筛选)', async () => {
|
||
const res = await api.searchCoursesByLabel(99999, authToken);
|
||
console.log(`[TC-SEARCH-005] 不存在标签: status=${res.status}`);
|
||
expect(res.status).toBe(200);
|
||
});
|
||
|
||
test('TC-SEARCH-006: 空关键词搜索可返回全部课程', async () => {
|
||
const res = await api.searchCourses('', authToken);
|
||
expect(res.status).toBe(200);
|
||
|
||
const content = res.data?.data?.content || res.data?.content || res.data || [];
|
||
const courses = Array.isArray(content) ? content : (content.records || []);
|
||
console.log(`[TC-SEARCH-006] 空搜索返回: ${courses.length} 个课程`);
|
||
expect(courses.length).toBeGreaterThanOrEqual(0);
|
||
});
|
||
});
|
||
|
||
// ════════════════════════════════════════════════════
|
||
// 步骤5:总结
|
||
// ════════════════════════════════════════════════════
|
||
describe('总结', () => {
|
||
test('TC-P4-SUMMARY: 团课高级搜索汇总', () => {
|
||
console.log('');
|
||
console.log('════════════════════════════════════════════');
|
||
console.log('P4-API - 团课高级搜索测试结束');
|
||
console.log('════════════════════════════════════════════');
|
||
console.log(' 已测试:');
|
||
console.log(` 1. 按类型筛选 (typeId=${typeId || 'N/A'})`);
|
||
console.log(` 2. 按标签筛选 (labelId=${labelId || 'N/A'})`);
|
||
console.log(` 3. 类型+标签组合筛选`);
|
||
console.log(' 4. 不存在类型/标签边界');
|
||
console.log(' 5. 空关键词搜索');
|
||
console.log('════════════════════════════════════════════');
|
||
|
||
expect(memberId).toBeDefined();
|
||
expect(authToken).toBeTruthy();
|
||
});
|
||
});
|
||
});
|