import { describe, it, expect, beforeEach } from 'vitest'; import { testClient } from 'hono/testing'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util'; import { JWTUtil } from '@d8d/shared-utils'; import { UserEntity, Role } from '@d8d/user-module'; import { File } from '@d8d/file-module'; import { Supplier } from '@d8d/supplier-module'; import { Merchant } from '@d8d/merchant-module'; import { publicGoodsRoutes } from '../../src/routes'; import { Goods, GoodsCategory } from '../../src/entities'; // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([ UserEntity, Role, Goods, GoodsCategory, File, Supplier, Merchant ]) describe('公开商品API集成测试', () => { let client: ReturnType>; let testUser: UserEntity; let testCategory: GoodsCategory; let testSupplier: Supplier; let testMerchant: Merchant; beforeEach(async () => { // 创建测试客户端 client = testClient(publicGoodsRoutes); // 获取数据源 const dataSource = await IntegrationTestDatabase.getDataSource(); // 创建测试用户 const userRepository = dataSource.getRepository(UserEntity); testUser = userRepository.create({ username: `test_user_${Math.floor(Math.random() * 100000)}`, password: 'test_password', nickname: '测试用户', registrationSource: 'web' }); await userRepository.save(testUser); // 创建测试商品分类 const categoryRepository = dataSource.getRepository(GoodsCategory); testCategory = categoryRepository.create({ name: '测试分类', parentId: 0, level: 1, state: 1, createdBy: testUser.id }); await categoryRepository.save(testCategory); // 创建测试供应商 const supplierRepository = dataSource.getRepository(Supplier); testSupplier = supplierRepository.create({ name: '测试供应商', username: `test_supplier_${Math.floor(Math.random() * 100000)}`, password: 'password123', phone: '13800138000', realname: '测试供应商', state: 1, createdBy: testUser.id }); await supplierRepository.save(testSupplier); // 创建测试商户 const merchantRepository = dataSource.getRepository(Merchant); testMerchant = merchantRepository.create({ name: '测试商户', username: `test_merchant_${Math.floor(Math.random() * 100000)}`, password: 'password123', phone: '13800138001', realname: '测试商户', state: 1, createdBy: testUser.id }); await merchantRepository.save(testMerchant); // 创建测试商品 const goodsRepository = dataSource.getRepository(Goods); // 创建可用状态的商品 const activeGoods1 = goodsRepository.create({ name: '可用商品1', price: 100.00, costPrice: 80.00, categoryId1: testCategory.id, categoryId2: testCategory.id, categoryId3: testCategory.id, goodsType: 1, supplierId: testSupplier.id, merchantId: testMerchant.id, state: 1, stock: 100, lowestBuy: 1, createdBy: testUser.id }); await goodsRepository.save(activeGoods1); const activeGoods2 = goodsRepository.create({ name: '可用商品2', price: 200.00, costPrice: 160.00, categoryId1: testCategory.id, categoryId2: testCategory.id, categoryId3: testCategory.id, goodsType: 1, supplierId: testSupplier.id, merchantId: testMerchant.id, state: 1, stock: 50, lowestBuy: 1, createdBy: testUser.id }); await goodsRepository.save(activeGoods2); const activeGoods3 = goodsRepository.create({ name: '可用商品3', price: 300.00, costPrice: 240.00, categoryId1: testCategory.id, categoryId2: testCategory.id, categoryId3: testCategory.id, goodsType: 2, supplierId: testSupplier.id, merchantId: testMerchant.id, state: 1, stock: 30, lowestBuy: 1, createdBy: testUser.id }); await goodsRepository.save(activeGoods3); // 创建不可用状态的商品(不应该被公开路由返回) const inactiveGoods = goodsRepository.create({ name: '不可用商品', price: 400.00, costPrice: 320.00, categoryId1: testCategory.id, categoryId2: testCategory.id, categoryId3: testCategory.id, goodsType: 1, supplierId: testSupplier.id, merchantId: testMerchant.id, state: 2, stock: 10, lowestBuy: 1, createdBy: testUser.id }); await goodsRepository.save(inactiveGoods); }); describe('GET /goods', () => { it('应该返回可用状态的商品列表', async () => { const response = await client.index.$get({ query: {} }); console.debug('公开商品列表响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data).toHaveProperty('data'); expect(Array.isArray(data.data)).toBe(true); // 验证只返回可用状态的商品 data.data.forEach((goods: any) => { expect(goods.state).toBe(1); }); // 验证不包含不可用状态的商品 const inactiveGoodsInResponse = data.data.find((goods: any) => goods.state === 2); expect(inactiveGoodsInResponse).toBeUndefined(); } }); it('应该支持按名称搜索', async () => { const response = await client.index.$get({ query: { keyword: '可用商品1' } }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); // 验证只返回匹配搜索的商品 data.data.forEach((goods: any) => { expect(goods.name).toContain('可用商品1'); expect(goods.state).toBe(1); }); } }); it('应该支持按分类过滤', async () => { const response = await client.index.$get({ query: { filters: JSON.stringify({ categoryId1: testCategory.id }) } }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); // 验证只返回指定分类的商品 data.data.forEach((goods: any) => { expect(goods.categoryId1).toBe(testCategory.id); expect(goods.state).toBe(1); }); } }); it('应该支持按商品类型过滤', async () => { const response = await client.index.$get({ query: { filters: JSON.stringify({ goodsType: 2 }) } }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); // 验证只返回指定类型的商品 data.data.forEach((goods: any) => { expect(goods.goodsType).toBe(2); expect(goods.state).toBe(1); }); } }); it('应该支持分页查询', async () => { const response = await client.index.$get({ query: { page: 1, pageSize: 2 } }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); expect(data.data.length).toBeLessThanOrEqual(2); // 验证只返回可用状态的商品 data.data.forEach((goods: any) => { expect(goods.state).toBe(1); }); } }); it('应该无需认证即可访问', async () => { const response = await client.index.$get({ query: {} }); expect(response.status).toBe(200); }); }); describe('GET /goods/:id', () => { it('应该返回指定商品的详情', async () => { // 先获取一个可用商品的ID const dataSource = await IntegrationTestDatabase.getDataSource(); const goodsRepository = dataSource.getRepository(Goods); const activeGoods = await goodsRepository.findOne({ where: { state: 1 } }); if (activeGoods) { const response = await client[':id'].$get({ param: { id: activeGoods.id } }); console.debug('公开商品详情响应状态:', response.status); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(data.id).toBe(activeGoods.id); expect(data.name).toBe(activeGoods.name); expect(data.state).toBe(1); // 确保是可用状态 } } }); it('应该拒绝访问不可用状态的商品', async () => { // 先获取一个不可用商品的ID const dataSource = await IntegrationTestDatabase.getDataSource(); const goodsRepository = dataSource.getRepository(Goods); const inactiveGoods = await goodsRepository.findOne({ where: { state: 2 } }); if (inactiveGoods) { const response = await client[':id'].$get({ param: { id: inactiveGoods.id } }); expect(response.status).toBe(404); // 不可用状态的商品应该返回404 } }); it('应该处理不存在的商品', async () => { const response = await client[':id'].$get({ param: { id: 999999 } }); expect(response.status).toBe(404); }); }); describe('POST /goods', () => { it('应该拒绝创建商品操作', async () => { const createData = { name: '尝试创建商品', price: 100.00, categoryId1: testCategory.id, state: 1 }; const response = await client.index.$post({ json: createData }); expect(response.status).toBe(404); // 只读模式下路由不存在 }); }); describe('PUT /goods/:id', () => { it('应该拒绝更新商品操作', async () => { // 先获取一个可用商品的ID const dataSource = await IntegrationTestDatabase.getDataSource(); const goodsRepository = dataSource.getRepository(Goods); const activeGoods = await goodsRepository.findOne({ where: { state: 1 } }); if (activeGoods) { const updateData = { name: '尝试更新商品' }; const response = await client[':id'].$put({ param: { id: activeGoods.id }, json: updateData }); expect(response.status).toBe(404); // 只读模式下路由不存在 } }); }); describe('DELETE /goods/:id', () => { it('应该拒绝删除商品操作', async () => { // 先获取一个可用商品的ID const dataSource = await IntegrationTestDatabase.getDataSource(); const goodsRepository = dataSource.getRepository(Goods); const activeGoods = await goodsRepository.findOne({ where: { state: 1 } }); if (activeGoods) { const response = await client[':id'].$delete({ param: { id: activeGoods.id } }); expect(response.status).toBe(404); // 只读模式下路由不存在 } }); }); describe('公开路由权限验证测试', () => { it('应该验证公开路由无需认证', async () => { const response = await client.index.$get({ query: {} }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); // 验证只返回可用状态的商品 data.data.forEach((goods: any) => { expect(goods.state).toBe(1); }); } }); it('应该验证只支持查询操作', async () => { // 测试所有非查询操作都应该被拒绝 const createResponse = await client.index.$post({ json: { name: '测试' } }); expect(createResponse.status).toBe(404); const updateResponse = await client[':id'].$put({ param: { id: 1 }, json: { name: '测试' } }); expect(updateResponse.status).toBe(404); const deleteResponse = await client[':id'].$delete({ param: { id: 1 } }); expect(deleteResponse.status).toBe(404); }); }); describe('商品关联关系测试', () => { it('应该正确加载商品关联关系', async () => { const response = await client.index.$get({ query: {} }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); expect(Array.isArray(data.data)).toBe(true); // 验证至少返回一个商品 if (data.data.length > 0) { const goods = data.data[0]; // 验证基础字段 expect(goods).toHaveProperty('id'); expect(goods).toHaveProperty('name'); expect(goods).toHaveProperty('price'); expect(goods).toHaveProperty('state', 1); // 验证关联关系字段存在 expect(goods).toHaveProperty('category1'); expect(goods).toHaveProperty('supplier'); expect(goods).toHaveProperty('merchant'); } } }); }); });