|
|
@@ -0,0 +1,441 @@
|
|
|
+import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
+import { testClient } from 'hono/testing';
|
|
|
+import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
|
|
|
+import { JWTUtil } from '@d8d/shared-utils';
|
|
|
+import { UserEntityMt, RoleMt } from '@d8d/core-module-mt/user-module-mt';
|
|
|
+import { FileMt } from '@d8d/core-module-mt/file-module-mt';
|
|
|
+import unifiedAdvertisementAdminRoutes from '../../src/routes/admin/unified-advertisements.admin.routes';
|
|
|
+import unifiedAdvertisementRoutes from '../../src/routes/unified-advertisements.routes';
|
|
|
+import { UnifiedAdvertisement } from '../../src/entities/unified-advertisement.entity';
|
|
|
+import { UnifiedAdvertisementType } from '../../src/entities/unified-advertisement-type.entity';
|
|
|
+
|
|
|
+// 设置集成测试钩子
|
|
|
+setupIntegrationDatabaseHooksWithEntities([UserEntityMt, RoleMt, FileMt, UnifiedAdvertisement, UnifiedAdvertisementType])
|
|
|
+
|
|
|
+describe('统一广告模块集成测试', () => {
|
|
|
+ describe('管理员路由(超级管理员专用)', () => {
|
|
|
+ let adminClient: ReturnType<typeof testClient<typeof unifiedAdvertisementAdminRoutes>>;
|
|
|
+ let superAdminToken: string;
|
|
|
+ let regularUserToken: string;
|
|
|
+ let testUser: UserEntityMt;
|
|
|
+ let testAdvertisementType: UnifiedAdvertisementType;
|
|
|
+
|
|
|
+ beforeEach(async () => {
|
|
|
+ // 创建测试客户端
|
|
|
+ adminClient = testClient(unifiedAdvertisementAdminRoutes);
|
|
|
+
|
|
|
+ // 获取数据源
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const userRepository = dataSource.getRepository(UserEntityMt);
|
|
|
+ const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
|
|
|
+
|
|
|
+ // 创建测试用户
|
|
|
+ testUser = userRepository.create({
|
|
|
+ username: `test_user_${Date.now()}`,
|
|
|
+ password: 'test_password',
|
|
|
+ nickname: '测试用户',
|
|
|
+ registrationSource: 'web',
|
|
|
+ tenantId: 1 // UserEntityMt requires tenantId
|
|
|
+ });
|
|
|
+ await userRepository.save(testUser);
|
|
|
+
|
|
|
+ // 创建测试广告类型
|
|
|
+ testAdvertisementType = typeRepository.create({
|
|
|
+ name: '首页轮播',
|
|
|
+ code: 'home_banner',
|
|
|
+ remark: '用于首页轮播图展示',
|
|
|
+ status: 1
|
|
|
+ });
|
|
|
+ await typeRepository.save(testAdvertisementType);
|
|
|
+
|
|
|
+ // 生成超级管理员token (ID=1)
|
|
|
+ superAdminToken = JWTUtil.generateToken({
|
|
|
+ id: 1,
|
|
|
+ username: 'admin',
|
|
|
+ roles: [{ name: 'admin' }]
|
|
|
+ });
|
|
|
+
|
|
|
+ // 生成普通用户token (ID=2)
|
|
|
+ regularUserToken = JWTUtil.generateToken({
|
|
|
+ id: 2,
|
|
|
+ username: 'user',
|
|
|
+ roles: [{ name: 'user' }]
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('GET /api/v1/admin/unified-advertisements', () => {
|
|
|
+ it('应该允许超级管理员获取广告列表', async () => {
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${superAdminToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('管理员广告列表响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ const data = await response.json();
|
|
|
+ expect(data).toHaveProperty('code', 200);
|
|
|
+ expect(data).toHaveProperty('data');
|
|
|
+ expect(data.data).toHaveProperty('list');
|
|
|
+ expect(Array.isArray(data.data.list)).toBe(true);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该拒绝普通用户访问管理员接口', async () => {
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${regularUserToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(403);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该拒绝未认证用户访问', async () => {
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(401);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('POST /api/v1/admin/unified-advertisements', () => {
|
|
|
+ it('应该允许超级管理员创建广告', async () => {
|
|
|
+ const createData = {
|
|
|
+ title: '测试广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `test_ad_${Date.now()}`,
|
|
|
+ url: 'https://example.com',
|
|
|
+ sort: 10,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements'].$post({
|
|
|
+ json: createData
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${superAdminToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('创建广告响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(201);
|
|
|
+
|
|
|
+ if (response.status === 201) {
|
|
|
+ const data = await response.json();
|
|
|
+ expect(data).toHaveProperty('code', 201);
|
|
|
+ expect(data.data).toHaveProperty('id');
|
|
|
+ expect(data.data.title).toBe(createData.title);
|
|
|
+ expect(data.data.code).toBe(createData.code);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该拒绝普通用户创建广告', async () => {
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements'].$post({
|
|
|
+ json: {
|
|
|
+ title: '测试广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: 'test_ad',
|
|
|
+ url: 'https://example.com'
|
|
|
+ }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${regularUserToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(403);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('PUT /api/v1/admin/unified-advertisements/:id', () => {
|
|
|
+ it('应该允许超级管理员更新广告', async () => {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
|
|
|
+
|
|
|
+ const testAdvertisement = advertisementRepository.create({
|
|
|
+ title: '原始广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `original_ad_${Date.now()}`,
|
|
|
+ url: 'https://example.com',
|
|
|
+ sort: 5,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ });
|
|
|
+ await advertisementRepository.save(testAdvertisement);
|
|
|
+
|
|
|
+ const updateData = {
|
|
|
+ title: '更新后的广告',
|
|
|
+ code: `updated_ad_${Date.now()}`,
|
|
|
+ sort: 15
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements/:id'].$put({
|
|
|
+ param: { id: testAdvertisement.id },
|
|
|
+ json: updateData
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${superAdminToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('更新广告响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ const data = await response.json();
|
|
|
+ expect(data.data.title).toBe(updateData.title);
|
|
|
+ expect(data.data.sort).toBe(updateData.sort);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('DELETE /api/v1/admin/unified-advertisements/:id', () => {
|
|
|
+ it('应该允许超级管理员软删除广告', async () => {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
|
|
|
+
|
|
|
+ const testAdvertisement = advertisementRepository.create({
|
|
|
+ title: '待删除广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `delete_ad_${Date.now()}`,
|
|
|
+ url: 'https://example.com',
|
|
|
+ sort: 5,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ });
|
|
|
+ await advertisementRepository.save(testAdvertisement);
|
|
|
+
|
|
|
+ const response = await adminClient['/api/v1/admin/unified-advertisements/:id'].$delete({
|
|
|
+ param: { id: testAdvertisement.id }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${superAdminToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('删除广告响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+
|
|
|
+ // 验证软删除
|
|
|
+ const deletedAdvertisement = await advertisementRepository.findOne({
|
|
|
+ where: { id: testAdvertisement.id }
|
|
|
+ });
|
|
|
+ expect(deletedAdvertisement).toBeDefined();
|
|
|
+ expect(deletedAdvertisement?.status).toBe(0);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('用户展示路由', () => {
|
|
|
+ let userClient: ReturnType<typeof testClient<typeof unifiedAdvertisementRoutes>>;
|
|
|
+ let testToken: string;
|
|
|
+ let testUser: UserEntityMt;
|
|
|
+ let testAdvertisementType: UnifiedAdvertisementType;
|
|
|
+
|
|
|
+ beforeEach(async () => {
|
|
|
+ // 创建测试客户端
|
|
|
+ userClient = testClient(unifiedAdvertisementRoutes);
|
|
|
+
|
|
|
+ // 获取数据源
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const userRepository = dataSource.getRepository(UserEntityMt);
|
|
|
+ const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
|
|
|
+
|
|
|
+ // 创建测试用户
|
|
|
+ testUser = userRepository.create({
|
|
|
+ username: `test_user_${Date.now()}`,
|
|
|
+ password: 'test_password',
|
|
|
+ nickname: '测试用户',
|
|
|
+ registrationSource: 'web',
|
|
|
+ tenantId: 1 // UserEntityMt requires tenantId
|
|
|
+ });
|
|
|
+ await userRepository.save(testUser);
|
|
|
+
|
|
|
+ // 创建测试广告类型
|
|
|
+ testAdvertisementType = typeRepository.create({
|
|
|
+ name: '首页轮播',
|
|
|
+ code: 'home_banner',
|
|
|
+ remark: '用于首页轮播图展示',
|
|
|
+ status: 1
|
|
|
+ });
|
|
|
+ await typeRepository.save(testAdvertisementType);
|
|
|
+
|
|
|
+ // 生成测试用户的token
|
|
|
+ testToken = JWTUtil.generateToken({
|
|
|
+ id: testUser.id,
|
|
|
+ username: testUser.username,
|
|
|
+ roles: [{ name: 'user' }]
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('GET /api/v1/advertisements', () => {
|
|
|
+ beforeEach(async () => {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
|
|
|
+
|
|
|
+ // 创建多个测试广告
|
|
|
+ await advertisementRepository.save([
|
|
|
+ {
|
|
|
+ title: '首页轮播1',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `home_banner_1_${Date.now()}`,
|
|
|
+ url: 'https://example.com/1',
|
|
|
+ sort: 1,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '首页轮播2',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `home_banner_2_${Date.now()}`,
|
|
|
+ url: 'https://example.com/2',
|
|
|
+ sort: 2,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '禁用广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `disabled_ad_${Date.now()}`,
|
|
|
+ url: 'https://example.com/3',
|
|
|
+ sort: 3,
|
|
|
+ status: 0,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ }
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该返回有效的广告列表(status=1)', async () => {
|
|
|
+ const response = await userClient['/api/v1/advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('用户广告列表响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ const data = await response.json();
|
|
|
+ expect(data).toHaveProperty('code', 200);
|
|
|
+ expect(data.data).toHaveProperty('list');
|
|
|
+ expect(Array.isArray(data.data.list)).toBe(true);
|
|
|
+ // 验证只返回启用状态的广告
|
|
|
+ expect(data.data.list.every((ad: any) => ad.status === 1)).toBe(true);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该支持按code筛选', async () => {
|
|
|
+ const response = await userClient['/api/v1/advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10, code: 'home_banner_1' }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该拒绝未认证用户访问', async () => {
|
|
|
+ const response = await userClient['/api/v1/advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(401);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('GET /api/v1/advertisements/:id', () => {
|
|
|
+ it('应该返回有效的广告详情', async () => {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
|
|
|
+
|
|
|
+ const testAdvertisement = advertisementRepository.create({
|
|
|
+ title: '测试广告详情',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `test_ad_detail_${Date.now()}`,
|
|
|
+ url: 'https://example.com',
|
|
|
+ sort: 5,
|
|
|
+ status: 1,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ });
|
|
|
+ await advertisementRepository.save(testAdvertisement);
|
|
|
+
|
|
|
+ const response = await userClient['/api/v1/advertisements/:id'].$get({
|
|
|
+ param: { id: testAdvertisement.id }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.debug('广告详情响应状态:', response.status);
|
|
|
+ expect(response.status).toBe(200);
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ const data = await response.json();
|
|
|
+ expect(data.data.id).toBe(testAdvertisement.id);
|
|
|
+ expect(data.data.title).toBe(testAdvertisement.title);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it('应该返回404对于禁用的广告', async () => {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
|
|
|
+
|
|
|
+ const disabledAdvertisement = advertisementRepository.create({
|
|
|
+ title: '禁用广告',
|
|
|
+ typeId: testAdvertisementType.id,
|
|
|
+ code: `disabled_ad_${Date.now()}`,
|
|
|
+ url: 'https://example.com',
|
|
|
+ sort: 5,
|
|
|
+ status: 0,
|
|
|
+ actionType: 1,
|
|
|
+ createdBy: 1
|
|
|
+ });
|
|
|
+ await advertisementRepository.save(disabledAdvertisement);
|
|
|
+
|
|
|
+ const response = await userClient['/api/v1/advertisements/:id'].$get({
|
|
|
+ param: { id: disabledAdvertisement.id }
|
|
|
+ }, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${testToken}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(response.status).toBe(404);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('API兼容性测试', () => {
|
|
|
+ it('用户展示路由API路径与原模块一致', async () => {
|
|
|
+ // 这个测试确保路由路径与原 advertisements-module-mt 完全一致
|
|
|
+ const userClient = testClient(unifiedAdvertisementRoutes);
|
|
|
+
|
|
|
+ // 验证路由存在(虽然会返回401,但说明路由注册成功)
|
|
|
+ const listResponse = await userClient['/api/v1/advertisements'].$get({
|
|
|
+ query: { page: 1, pageSize: 10 }
|
|
|
+ });
|
|
|
+ expect([200, 401]).toContain(listResponse.status);
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|