322 lines
9.4 KiB
JavaScript
322 lines
9.4 KiB
JavaScript
/**
|
|
* 分类管理API测试
|
|
* TDD: 先写测试,再实现功能
|
|
*/
|
|
|
|
const request = require('supertest');
|
|
const express = require('express');
|
|
|
|
// 模拟Express应用
|
|
const app = express();
|
|
app.use(express.json());
|
|
|
|
// 测试数据
|
|
const testCategories = [
|
|
{ name: '测试一级分类1', parent_id: null, level: 1 },
|
|
{ name: '测试一级分类2', parent_id: null, level: 1 },
|
|
];
|
|
|
|
describe('分类管理API测试', () => {
|
|
|
|
// 测试1: 获取分类树
|
|
describe('GET /api/categories/tree', () => {
|
|
test('应该返回分类树结构', async () => {
|
|
const response = await request(app)
|
|
.get('/api/categories/tree')
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
expect(Array.isArray(response.body.data)).toBe(true);
|
|
// 每个一级分类应该有children属性
|
|
if (response.body.data.length > 0) {
|
|
expect(response.body.data[0]).toHaveProperty('children');
|
|
}
|
|
});
|
|
|
|
test('应该支持按层级筛选', async () => {
|
|
const response = await request(app)
|
|
.get('/api/categories/tree?level=1')
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
// 返回的都是一级分类
|
|
response.body.data.forEach(cat => {
|
|
expect(cat.level).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
// 测试2: 创建分类
|
|
describe('POST /api/categories', () => {
|
|
test('应该能创建一级分类', async () => {
|
|
const newCategory = {
|
|
name: '新材料分类',
|
|
parent_id: null,
|
|
level: 1,
|
|
description: '测试描述'
|
|
};
|
|
|
|
const response = await request(app)
|
|
.post('/api/categories')
|
|
.send(newCategory)
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.data).toHaveProperty('id');
|
|
expect(response.body.data.name).toBe(newCategory.name);
|
|
});
|
|
|
|
test('应该能在指定父分类下创建二级分类', async () => {
|
|
// 先创建父分类
|
|
const parentResponse = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '父分类', parent_id: null, level: 1 });
|
|
|
|
const parentId = parentResponse.body.data.id;
|
|
|
|
// 创建子分类
|
|
const childCategory = {
|
|
name: '子分类',
|
|
parent_id: parentId,
|
|
level: 2
|
|
};
|
|
|
|
const response = await request(app)
|
|
.post('/api/categories')
|
|
.send(childCategory)
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.data.parent_id).toBe(parentId);
|
|
expect(response.body.data.level).toBe(2);
|
|
});
|
|
|
|
test('分类名称不能为空', async () => {
|
|
const response = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '', parent_id: null, level: 1 })
|
|
.expect(400);
|
|
|
|
expect(response.body.success).toBe(false);
|
|
expect(response.body.message).toContain('名称');
|
|
});
|
|
|
|
test('同一父分类下不能有重名子分类', async () => {
|
|
// 创建父分类
|
|
const parent = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '唯一父分类', parent_id: null, level: 1 });
|
|
|
|
// 创建第一个子分类
|
|
await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '同名子分类', parent_id: parent.body.data.id, level: 2 });
|
|
|
|
// 尝试创建同名子分类
|
|
const response = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '同名子分类', parent_id: parent.body.data.id, level: 2 })
|
|
.expect(400);
|
|
|
|
expect(response.body.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
// 测试3: 更新分类
|
|
describe('PUT /api/categories/:id', () => {
|
|
test('应该能更新分类名称', async () => {
|
|
// 先创建分类
|
|
const createResponse = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '原名称', parent_id: null, level: 1 });
|
|
|
|
const categoryId = createResponse.body.data.id;
|
|
|
|
// 更新分类
|
|
const response = await request(app)
|
|
.put(`/api/categories/${categoryId}`)
|
|
.send({ name: '新名称' })
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
|
|
// 验证更新
|
|
const getResponse = await request(app)
|
|
.get(`/api/categories/${categoryId}`)
|
|
.expect(200);
|
|
|
|
expect(getResponse.body.data.name).toBe('新名称');
|
|
});
|
|
|
|
test('不能将分类设置为自己的子分类', async () => {
|
|
// 创建父分类
|
|
const parent = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '父', parent_id: null, level: 1 });
|
|
|
|
// 创建子分类
|
|
const child = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '子', parent_id: parent.body.data.id, level: 2 });
|
|
|
|
// 尝试将父分类的parent_id设为子分类(循环引用)
|
|
const response = await request(app)
|
|
.put(`/api/categories/${parent.body.data.id}`)
|
|
.send({ parent_id: child.body.data.id })
|
|
.expect(400);
|
|
|
|
expect(response.body.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
// 测试4: 删除分类
|
|
describe('DELETE /api/categories/:id', () => {
|
|
test('应该能删除空分类', async () => {
|
|
// 创建分类
|
|
const createResponse = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '待删除', parent_id: null, level: 1 });
|
|
|
|
const categoryId = createResponse.body.data.id;
|
|
|
|
// 删除分类
|
|
const response = await request(app)
|
|
.delete(`/api/categories/${categoryId}`)
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
|
|
// 验证已删除
|
|
const getResponse = await request(app)
|
|
.get(`/api/categories/${categoryId}`)
|
|
.expect(404);
|
|
});
|
|
|
|
test('删除一级分类应该级联删除二级分类', async () => {
|
|
// 创建父分类
|
|
const parent = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '父', parent_id: null, level: 1 });
|
|
|
|
// 创建子分类
|
|
const child = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '子', parent_id: parent.body.data.id, level: 2 });
|
|
|
|
// 删除父分类
|
|
await request(app)
|
|
.delete(`/api/categories/${parent.body.data.id}`)
|
|
.expect(200);
|
|
|
|
// 验证子分类也被删除
|
|
await request(app)
|
|
.get(`/api/categories/${child.body.data.id}`)
|
|
.expect(404);
|
|
});
|
|
|
|
test('有商品的分类不应该被删除', async () => {
|
|
// 创建分类
|
|
const category = await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '有商品的分类', parent_id: null, level: 1 });
|
|
|
|
// 在该分类下创建商品(模拟)
|
|
// ... 创建商品逻辑
|
|
|
|
// 尝试删除分类
|
|
const response = await request(app)
|
|
.delete(`/api/categories/${category.body.data.id}`)
|
|
.expect(400);
|
|
|
|
expect(response.body.success).toBe(false);
|
|
expect(response.body.message).toContain('商品');
|
|
});
|
|
});
|
|
|
|
// 测试5: 批量导入时自动创建分类
|
|
describe('批量导入分类处理', () => {
|
|
test('导入时应该自动创建不存在的一级分类', async () => {
|
|
const importData = {
|
|
products: [
|
|
{
|
|
name: '测试商品',
|
|
category_level1: '新一级分类',
|
|
category_level2: '新二级分类'
|
|
}
|
|
]
|
|
};
|
|
|
|
const response = await request(app)
|
|
.post('/api/products/batch-import')
|
|
.send(importData)
|
|
.expect(200);
|
|
|
|
expect(response.body.success).toBe(true);
|
|
|
|
// 验证分类已创建
|
|
const categories = await request(app)
|
|
.get('/api/categories/tree')
|
|
.expect(200);
|
|
|
|
const level1Exists = categories.body.data.some(
|
|
cat => cat.name === '新一级分类'
|
|
);
|
|
expect(level1Exists).toBe(true);
|
|
});
|
|
|
|
test('导入时应该自动创建不存在的二级分类', async () => {
|
|
// 先创建一级分类
|
|
await request(app)
|
|
.post('/api/categories')
|
|
.send({ name: '已有的一级', parent_id: null, level: 1 });
|
|
|
|
// 导入带有新二级分类的商品
|
|
const importData = {
|
|
products: [
|
|
{
|
|
name: '测试商品2',
|
|
category_level1: '已有的一级',
|
|
category_level2: '新的二级'
|
|
}
|
|
]
|
|
};
|
|
|
|
await request(app)
|
|
.post('/api/products/batch-import')
|
|
.send(importData)
|
|
.expect(200);
|
|
|
|
// 验证二级分类已创建
|
|
const categories = await request(app)
|
|
.get('/api/categories/tree')
|
|
.expect(200);
|
|
|
|
const parent = categories.body.data.find(
|
|
cat => cat.name === '已有的一级'
|
|
);
|
|
expect(parent).toBeTruthy();
|
|
expect(parent.children).toBeDefined();
|
|
|
|
const level2Exists = parent.children.some(
|
|
child => child.name === '新的二级'
|
|
);
|
|
expect(level2Exists).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
// 运行测试
|
|
if (require.main === module) {
|
|
const { execSync } = require('child_process');
|
|
try {
|
|
execSync('npx jest test-category-api.spec.js --verbose', {
|
|
cwd: __dirname,
|
|
stdio: 'inherit'
|
|
});
|
|
} catch (e) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
module.exports = { app };
|