瀏覽代碼

test: 完成故事010.001任务11 - 添加广告类型管理路由测试

- 添加广告类型管理员路由测试(CRUD + 权限验证):13个测试用例
- 添加广告类型用户展示路由测试:3个测试用例
- 添加类型与广告关联查询测试:4个测试用例
- 修复Entity:为UnifiedAdvertisementType.code添加唯一索引
- 修复Entity:添加与UnifiedAdvertisement的反向关系
- 更新史诗和故事文档

测试结果:57/57 测试全部通过

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 2 周之前
父節點
當前提交
755d0cd4a7

+ 7 - 1
docs/prd/epic-010-unified-ad-management.md

@@ -5,6 +5,7 @@
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
 | 1.0 | 2026-01-02 | 初始版本 | James (Claude Code) |
+| 1.1 | 2026-01-03 | 完成任务11:添加广告类型管理路由测试 | James (Claude Code) |
 
 ## 史诗目标
 
@@ -58,8 +59,13 @@
 - [x] 实现管理员路由(使用tenantAuthMiddleware)
 - [x] 实现用户展示路由(使用authMiddleware)
 - [x] 编写单元测试和集成测试
+- [x] **添加广告类型管理路由测试**(测试覆盖率提升 - 2026-01-03完成)
+  - [x] 添加广告类型管理员路由测试(CRUD + 权限验证)
+  - [x] 添加广告类型用户展示路由测试
+  - [x] 验证类型与广告的关联查询
 
-**完成日期**: 2026-01-02
+**完成日期**: 2026-01-02(初始),2026-01-03(任务11)
+**测试覆盖**: 57个测试全部通过(23个单元测试 + 34个集成测试)
 **相关文件**: `docs/stories/010.001.story.md`
 
 ### Story 2: 创建统一广告管理UI

+ 23 - 6
docs/stories/010.001.story.md

@@ -88,10 +88,10 @@ Approved
   - [x] 运行 `pnpm test` 确保所有测试通过
   - [x] 运行 `pnpm test:coverage` 确保覆盖率达标
 
-- [ ] **任务11: 添加广告类型管理路由测试** (测试覆盖率提升)
-  - [ ] 添加广告类型管理员路由测试(CRUD + 权限验证)
-  - [ ] 添加广告类型用户展示路由测试
-  - [ ] 验证类型与广告的关联查询
+- [x] **任务11: 添加广告类型管理路由测试** (测试覆盖率提升)
+  - [x] 添加广告类型管理员路由测试(CRUD + 权限验证)
+  - [x] 添加广告类型用户展示路由测试
+  - [x] 验证类型与广告的关联查询
 
 ## Dev Notes
 
@@ -334,6 +334,7 @@ pnpm typecheck
 | Date | Version | Description | Author |
 |------|---------|-------------|--------|
 | 2026-01-02 | 1.0 | 初始故事创建 | Bob (Scrum Master) |
+| 2026-01-03 | 1.1 | 完成任务11:添加广告类型管理路由测试 | James (Claude Code) |
 
 ## Dev Agent Record
 
@@ -351,10 +352,19 @@ claude-opus-4-5-20251101 (d8d-model)
    - `UnifiedAdvertisementTypeService`: 搜索 `['name', 'code']`
    - `UnifiedAdvertisementService`: 搜索 `['title', 'code']`
 
-2. **所有测试通过**: 36/36 测试通过
+2. **所有测试通过 (2026-01-02)**: 36/36 测试通过
    - 单元测试: 23个 (unified-advertisement: 12, unified-advertisement-type: 11)
    - 集成测试: 13个
 
+3. **任务11完成 (2026-01-03)**: 添加广告类型管理路由测试
+   - 添加广告类型管理员路由测试:GET /api/v1/admin/unified-advertisement-types (列表、详情)、POST (创建)、PUT (更新)、DELETE (软删除)
+   - 添加广告类型用户展示路由测试:GET /advertisement-types
+   - 添加类型与广告关联查询测试
+   - 修复Entity:为`UnifiedAdvertisementType.code`添加唯一索引,添加与`UnifiedAdvertisement`的反向关系
+   - 最终测试结果:57/57 测试通过
+   - 单元测试: 23个
+   - 集成测试: 34个 (包含新增的21个广告类型相关测试)
+
 ### File List
 
 **包配置文件**:
@@ -393,7 +403,14 @@ claude-opus-4-5-20251101 (d8d-model)
 - `tests/utils/test-data-factory.ts` - 测试数据工厂
 - `tests/unit/unified-advertisement.service.test.ts` - 广告Service单元测试(12个测试)
 - `tests/unit/unified-advertisement-type.service.test.ts` - 广告类型Service单元测试(11个测试)
-- `tests/integration/unified-advertisements.integration.test.ts` - 集成测试(13个测试)
+- `tests/integration/unified-advertisements.integration.test.ts` - 集成测试(34个测试)
+  - 广告管理员路由测试(7个)
+  - 广告用户展示路由测试(5个)
+  - 广告类型管理员路由测试(13个)
+  - 广告类型用户展示路由测试(3个)
+  - 类型与广告关联查询测试(4个)
+  - API兼容性测试(1个)
+  - 原有测试(13个)
 
 ## QA Results
 _待QA代理填写_

+ 7 - 1
packages/unified-advertisements-module/src/entities/unified-advertisement-type.entity.ts

@@ -1,4 +1,5 @@
-import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm';
+import { UnifiedAdvertisement } from './unified-advertisement.entity';
 
 @Entity('ad_type_unified')
 export class UnifiedAdvertisementType {
@@ -24,6 +25,7 @@ export class UnifiedAdvertisementType {
     length: 20,
     comment: '调用别名'
   })
+  @Index('idx_ad_type_unified_code', { unique: true })
   code!: string;
 
   @Column({
@@ -74,4 +76,8 @@ export class UnifiedAdvertisementType {
     comment: '状态 0禁用 1启用'
   })
   status!: number;
+
+  // 反向关系:一个类型可以关联多个广告
+  @OneToMany(() => UnifiedAdvertisement, advertisement => advertisement.advertisementType)
+  advertisements!: UnifiedAdvertisement[];
 }

+ 554 - 0
packages/unified-advertisements-module/tests/integration/unified-advertisements.integration.test.ts

@@ -5,7 +5,9 @@ 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 unifiedAdvertisementTypeAdminRoutes from '../../src/routes/admin/unified-advertisement-types.admin.routes';
 import unifiedAdvertisementRoutes from '../../src/routes/unified-advertisements.routes';
+import unifiedAdvertisementTypeRoutes from '../../src/routes/unified-advertisement-types.routes';
 import { UnifiedAdvertisement } from '../../src/entities/unified-advertisement.entity';
 import { UnifiedAdvertisementType } from '../../src/entities/unified-advertisement-type.entity';
 
@@ -444,4 +446,556 @@ describe('统一广告模块集成测试', () => {
       expect([200, 401]).toContain(listResponse.status);
     });
   });
+
+  describe('广告类型管理员路由(超级管理员专用)', () => {
+    let adminClient: ReturnType<typeof testClient<typeof unifiedAdvertisementTypeAdminRoutes>>;
+    let superAdminToken: string;
+    let regularUserToken: string;
+
+    beforeEach(async () => {
+      adminClient = testClient(unifiedAdvertisementTypeAdminRoutes);
+
+      // 生成超级管理员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-advertisement-types', () => {
+      it('应该允许超级管理员获取广告类型列表', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$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 dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        await typeRepository.save({
+          name: '测试类型搜索',
+          code: `ts_${timestamp}`,
+          remark: '用于测试关键词搜索',
+          status: 1
+        });
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$get({
+          query: { page: 1, pageSize: 10, keyword: '测试类型搜索' }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(200);
+        if (response.status === 200) {
+          const data = await response.json();
+          expect(data.data.list.some((t: any) => t.name === '测试类型搜索')).toBe(true);
+        }
+      });
+
+      it('应该支持状态筛选', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$get({
+          query: { page: 1, pageSize: 10, status: 1 }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(200);
+        if (response.status === 200) {
+          const data = await response.json();
+          expect(data.data.list.every((t: any) => t.status === 1)).toBe(true);
+        }
+      });
+
+      it('应该拒绝普通用户访问管理员接口', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$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-advertisement-types'].$get({
+          query: { page: 1, pageSize: 10 }
+        });
+
+        expect(response.status).toBe(401);
+      });
+    });
+
+    describe('GET /api/v1/admin/unified-advertisement-types/:id', () => {
+      it('应该允许超级管理员获取广告类型详情', async () => {
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        const testType = typeRepository.create({
+          name: '测试类型详情',
+          code: `td_${timestamp}`,
+          remark: '用于测试详情接口',
+          status: 1
+        });
+        await typeRepository.save(testType);
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$get({
+          param: { id: testType.id }
+        }, {
+          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.id).toBe(testType.id);
+          expect(data.data.name).toBe(testType.name);
+        }
+      });
+
+      it('应该返回404对于不存在的类型', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$get({
+          param: { id: 999999 }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(404);
+      });
+    });
+
+    describe('POST /api/v1/admin/unified-advertisement-types', () => {
+      it('应该允许超级管理员创建广告类型', async () => {
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        const createData = {
+          name: '首页轮播',
+          code: `hb_${timestamp}`,
+          remark: '用于首页轮播图展示',
+          status: 1
+        };
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$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.name).toBe(createData.name);
+          expect(data.data.code).toBe(createData.code);
+        }
+      });
+
+      it('应该拒绝普通用户创建广告类型', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$post({
+          json: {
+            name: '测试类型',
+            code: 'test_type',
+            remark: '测试'
+          }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${regularUserToken}`
+          }
+        });
+
+        expect(response.status).toBe(403);
+      });
+
+      it('应该拒绝创建重复code的类型', async () => {
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        const existingType = typeRepository.create({
+          name: '已存在类型',
+          code: `dup_${timestamp}`,
+          remark: '用于测试重复code',
+          status: 1
+        });
+        await typeRepository.save(existingType);
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types'].$post({
+          json: {
+            name: '新类型',
+            code: `dup_${timestamp}`,
+            remark: '测试重复code'
+          }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(500);
+      });
+    });
+
+    describe('PUT /api/v1/admin/unified-advertisement-types/:id', () => {
+      it('应该允许超级管理员更新广告类型', async () => {
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        const testType = typeRepository.create({
+          name: '原始类型',
+          code: `ot_${timestamp}`,
+          remark: '原始备注',
+          status: 1
+        });
+        await typeRepository.save(testType);
+
+        const updateData = {
+          name: '更新后的类型',
+          code: `ut_${timestamp}`,
+          remark: '更新后的备注'
+        };
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$put({
+          param: { id: testType.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.name).toBe(updateData.name);
+          expect(data.data.remark).toBe(updateData.remark);
+        }
+      });
+
+      it('应该返回404对于不存在的类型', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$put({
+          param: { id: 999999 },
+          json: { name: '更新' }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(404);
+      });
+    });
+
+    describe('DELETE /api/v1/admin/unified-advertisement-types/:id', () => {
+      it('应该允许超级管理员软删除广告类型', async () => {
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+        const testType = typeRepository.create({
+          name: '待删除类型',
+          code: `dt_${timestamp}`,
+          remark: '用于测试软删除',
+          status: 1
+        });
+        await typeRepository.save(testType);
+
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$delete({
+          param: { id: testType.id }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        console.debug('删除广告类型响应状态:', response.status);
+        expect(response.status).toBe(200);
+
+        // 验证软删除
+        const deletedType = await typeRepository.findOne({
+          where: { id: testType.id }
+        });
+        expect(deletedType).toBeDefined();
+        expect(deletedType?.status).toBe(0);
+      });
+
+      it('应该返回404对于不存在的类型', async () => {
+        const response = await adminClient['/api/v1/admin/unified-advertisement-types/:id'].$delete({
+          param: { id: 999999 }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${superAdminToken}`
+          }
+        });
+
+        expect(response.status).toBe(404);
+      });
+    });
+  });
+
+  describe('广告类型用户展示路由', () => {
+    let userClient: ReturnType<typeof testClient<typeof unifiedAdvertisementTypeRoutes>>;
+    let testToken: string;
+    let testUser: UserEntityMt;
+
+    beforeEach(async () => {
+      userClient = testClient(unifiedAdvertisementTypeRoutes);
+
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const userRepository = dataSource.getRepository(UserEntityMt);
+
+      testUser = userRepository.create({
+        username: `test_user_${Date.now()}`,
+        password: 'test_password',
+        nickname: '测试用户',
+        registrationSource: 'web',
+        tenantId: 1
+      });
+      await userRepository.save(testUser);
+
+      testToken = JWTUtil.generateToken({
+        id: testUser.id,
+        username: testUser.username,
+        roles: [{ name: 'user' }]
+      });
+    });
+
+    describe('GET /advertisement-types', () => {
+      beforeEach(async () => {
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+        const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+
+        await typeRepository.save([
+          {
+            name: '首页轮播',
+            code: `hbn_${timestamp}`,
+            remark: '首页轮播图',
+            status: 1
+          },
+          {
+            name: '详情页推荐',
+            code: `dr_${timestamp}`,
+            remark: '详情页推荐',
+            status: 1
+          },
+          {
+            name: '禁用类型',
+            code: `dis_${timestamp}`,
+            remark: '已禁用',
+            status: 0
+          }
+        ]);
+      });
+
+      it('应该返回有效的广告类型列表(status=1)', async () => {
+        const response = await userClient['/api/v1/advertisement-types'].$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((t: any) => t.status === 1)).toBe(true);
+        }
+      });
+
+      it('应该支持关键词搜索', async () => {
+        const response = await userClient['/api/v1/advertisement-types'].$get({
+          query: { page: 1, pageSize: 10, keyword: '首页' }
+        }, {
+          headers: {
+            'Authorization': `Bearer ${testToken}`
+          }
+        });
+
+        expect(response.status).toBe(200);
+        if (response.status === 200) {
+          const data = await response.json();
+          expect(data.data.list.some((t: any) => t.name.includes('首页'))).toBe(true);
+        }
+      });
+
+      it('应该拒绝未认证用户访问', async () => {
+        const response = await userClient['/api/v1/advertisement-types'].$get({
+          query: { page: 1, pageSize: 10 }
+        });
+
+        expect(response.status).toBe(401);
+      });
+    });
+  });
+
+  describe('类型与广告的关联查询测试', () => {
+    let adminClient: ReturnType<typeof testClient<typeof unifiedAdvertisementAdminRoutes>>;
+    let superAdminToken: string;
+    let testType: UnifiedAdvertisementType;
+
+    beforeEach(async () => {
+      adminClient = testClient(unifiedAdvertisementAdminRoutes);
+
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+      const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
+      const timestamp = Math.floor(Date.now() / 1000).toString().slice(-6);
+
+      // 创建测试类型
+      testType = typeRepository.create({
+        name: '测试关联类型',
+        code: `trt_${timestamp}`,
+        remark: '用于测试关联查询',
+        status: 1
+      });
+      await typeRepository.save(testType);
+
+      // 创建该类型下的多个广告
+      await advertisementRepository.save([
+        {
+          title: '关联广告1',
+          typeId: testType.id,
+          code: `ra1_${timestamp}`,
+          url: 'https://example.com/1',
+          sort: 1,
+          status: 1,
+          actionType: 1,
+          createdBy: 1
+        },
+        {
+          title: '关联广告2',
+          typeId: testType.id,
+          code: `ra2_${timestamp}`,
+          url: 'https://example.com/2',
+          sort: 2,
+          status: 1,
+          actionType: 1,
+          createdBy: 1
+        }
+      ]);
+
+      superAdminToken = JWTUtil.generateToken({
+        id: 1,
+        username: 'admin',
+        roles: [{ name: 'admin' }]
+      });
+    });
+
+    it('应该能够通过类型查询关联的广告', async () => {
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+
+      // 查询类型及其关联的广告
+      const typeWithAds = await typeRepository.findOne({
+        where: { id: testType.id },
+        relations: ['advertisements']
+      });
+
+      expect(typeWithAds).toBeDefined();
+      expect(typeWithAds?.advertisements).toBeDefined();
+      expect(typeWithAds?.advertisements.length).toBeGreaterThan(0);
+      expect(typeWithAds?.advertisements.every(ad => ad.typeId === testType.id)).toBe(true);
+    });
+
+    it('应该能够在广告列表中包含类型信息', async () => {
+      const response = await adminClient['/api/v1/admin/unified-advertisements'].$get({
+        query: { page: 1, pageSize: 10 }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${superAdminToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      if (response.status === 200) {
+        const data = await response.json();
+        // 验证返回的广告包含typeId字段
+        const adsOfType = data.data.list.filter((ad: any) => ad.typeId === testType.id);
+        expect(adsOfType.length).toBeGreaterThan(0);
+      }
+    });
+
+    it('应该支持按类型ID筛选广告', async () => {
+      const response = await adminClient['/api/v1/admin/unified-advertisements'].$get({
+        query: { page: 1, pageSize: 10, typeId: testType.id }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${superAdminToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      if (response.status === 200) {
+        const data = await response.json();
+        // 验证返回的广告都属于指定类型
+        expect(data.data.list.every((ad: any) => ad.typeId === testType.id)).toBe(true);
+      }
+    });
+
+    it('软删除类型不应影响关联的广告', async () => {
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const typeRepository = dataSource.getRepository(UnifiedAdvertisementType);
+      const advertisementRepository = dataSource.getRepository(UnifiedAdvertisement);
+
+      // 软删除类型
+      await typeRepository.update(testType.id, { status: 0 });
+
+      // 广告应该仍然存在
+      const ads = await advertisementRepository.find({
+        where: { typeId: testType.id }
+      });
+      expect(ads.length).toBeGreaterThan(0);
+    });
+  });
 });