import { describe, it, expect, beforeEach, vi } from 'vitest'; import { testClient } from 'hono/testing'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util'; import { systemConfigRoutesMt } from '../../src/routes/system-config.routes.mt'; import { SystemConfigMt } from '../../src/entities/system-config.entity.mt'; import { SystemConfigServiceMt } from '../../src/services/system-config.service.mt'; import { UserEntityMt, RoleMt } from '@d8d/core-module-mt/user-module-mt'; import { FileMt } from '@d8d/core-module-mt/file-module-mt'; import { TestDataFactory } from '../utils/integration-test-db'; import { AuthService } from '@d8d/core-module-mt/auth-module-mt'; import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt'; import { redisUtil } from '@d8d/shared-utils'; // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt]) describe('系统配置路由API集成测试 (使用hono/testing)', () => { let client: ReturnType>; let authService: AuthService; let userService: UserServiceMt; let testToken: string; let testUser: any; beforeEach(async () => { // 创建测试客户端 client = testClient(systemConfigRoutesMt); // 获取数据源 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); // 初始化服务 userService = new UserServiceMt(dataSource); authService = new AuthService(userService); // 创建测试用户并生成token testUser = await TestDataFactory.createTestUser(dataSource, { username: 'testuser_systemconfig', password: 'TestPassword123!', email: 'testuser_systemconfig@example.com' }); // 生成测试用户的token testToken = authService.generateToken(testUser); }); describe('CRUD Operations', () => { it('应该成功创建系统配置', async () => { const configData = { configKey: 'app.login.enabled', configValue: 'true', description: '控制小程序登录功能是否开启' }; const response = await client.index.$post({ json: configData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(201); const result = await response.json(); if ('configKey' in result) { expect(result.configKey).toBe(configData.configKey); expect(result.configValue).toBe(configData.configValue); } }); it('应该根据ID获取系统配置', async () => { // 先创建配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const config = await systemConfigService.create({ configKey: 'app.payment.enabled', configValue: 'true', description: '控制支付功能是否开启', tenantId: testUser.tenantId } as SystemConfigMt); const response = await client[':id'].$get({ param: { id: config.id } }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(200); const result = await response.json(); if ('id' in result) { expect(result.id).toBe(config.id); expect(result.configKey).toBe(config.configKey); } }); it('应该更新系统配置', async () => { // 先创建配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const config = await systemConfigService.create({ configKey: 'app.notification.enabled', configValue: 'true', description: '控制通知功能是否开启', tenantId: testUser.tenantId } as SystemConfigMt); const updateData = { configValue: 'false', description: '通知功能已关闭' }; const response = await client[':id'].$put({ param: { id: config.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(200); const result = await response.json(); if ('configValue' in result) { expect(result.configValue).toBe(updateData.configValue); expect(result.description).toBe(updateData.description); } }); it('应该删除系统配置', async () => { // 先创建配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const config = await systemConfigService.create({ configKey: 'app.analytics.enabled', configValue: 'true', description: '控制分析功能是否开启', tenantId: testUser.tenantId } as SystemConfigMt); const response = await client[':id'].$delete({ param: { id: config.id } }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(204); // 验证配置已被删除 const deletedConfig = await systemConfigService.getById(config.id); expect(deletedConfig).toBeNull(); }); it('应该列出系统配置列表', async () => { // 创建多个配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); await systemConfigService.create({ configKey: 'app.feature1.enabled', configValue: 'true', tenantId: testUser.tenantId } as SystemConfigMt); await systemConfigService.create({ configKey: 'app.feature2.enabled', configValue: 'false', tenantId: testUser.tenantId } as SystemConfigMt); const response = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(200); const result = await response.json(); if ('data' in result) { expect(result.data).toHaveLength(2); } }); }); describe('Multi-tenant Isolation', () => { let tenant1User: any; let tenant2User: any; let tenant1Token: string; let tenant2Token: string; beforeEach(async () => { const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); // 创建租户1的用户 tenant1User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant1_user_systemconfig', password: 'TestPassword123!', email: 'tenant1_systemconfig@example.com', tenantId: 1 }); // 创建租户2的用户 tenant2User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant2_user_systemconfig', password: 'TestPassword123!', email: 'tenant2_systemconfig@example.com', tenantId: 2 }); // 生成租户用户的token tenant1Token = authService.generateToken(tenant1User); tenant2Token = authService.generateToken(tenant2User); }); it('应该按租户隔离配置', async () => { const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); // 为租户1创建配置 await systemConfigService.create({ configKey: 'app.shared.config', configValue: 'tenant1-value', tenantId: 1 } as SystemConfigMt); // 为租户2创建配置 await systemConfigService.create({ configKey: 'app.shared.config', configValue: 'tenant2-value', tenantId: 2 } as SystemConfigMt); // 查询租户1的配置 const response1 = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant1Token}` } }); expect(response1.status).toBe(200); const result1 = await response1.json(); if ('data' in result1) { expect(result1.data).toHaveLength(1); expect(result1.data[0].configValue).toBe('tenant1-value'); } // 查询租户2的配置 const response2 = await client.index.$get({ query: {} }, { headers: { 'Authorization': `Bearer ${tenant2Token}` } }); expect(response2.status).toBe(200); const result2 = await response2.json(); if ('data' in result2) { expect(result2.data).toHaveLength(1); expect(result2.data[0].configValue).toBe('tenant2-value'); } }); }); describe('Data Validation', () => { it('应该验证必填字段', async () => { const invalidData = { configKey: '', // 空字符串 configValue: 'true' }; const response = await client.index.$post({ json: invalidData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(400); }); it('应该验证字段长度', async () => { const invalidData = { configKey: 'a'.repeat(256), // 超过255字符 configValue: 'true' }; const response = await client.index.$post({ json: invalidData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(400); }); }); describe('自定义路由缓存刷新验证', () => { beforeEach(async () => { // 清除测试缓存 await redisUtil.deleteSystemConfig(testUser.tenantId, 'app.login.enabled'); await redisUtil.deleteSystemConfig(testUser.tenantId, 'app.payment.enabled'); }); it('应该通过自定义创建路由刷新缓存', async () => { const configData = { configKey: 'app.login.enabled', configValue: 'true', description: '控制小程序登录功能是否开启' }; // 验证缓存初始为空 const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, configData.configKey); expect(initialCache).toBeNull(); // 通过自定义路由创建配置 const response = await client.index.$post({ json: configData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(201); const result = await response.json() as any; expect(result.configKey).toBe(configData.configKey); expect(result.configValue).toBe(configData.configValue); // 验证缓存已被刷新(为空,因为setConfig会删除缓存) const afterCreateCache = await redisUtil.getSystemConfig(testUser.tenantId, configData.configKey); expect(afterCreateCache).toBeNull(); }); it('应该通过自定义更新路由刷新缓存', async () => { // 先创建配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const config = await systemConfigService.create({ configKey: 'app.payment.enabled', configValue: 'true', description: '控制支付功能是否开启', tenantId: testUser.tenantId } as any); // 设置缓存 await redisUtil.setSystemConfig(testUser.tenantId, config.configKey, 'cached-value', 3600); const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey); expect(initialCache).toBe('cached-value'); // 通过自定义路由更新配置 const updateData = { configValue: 'false', description: '支付功能已关闭' }; const response = await client[':id'].$put({ param: { id: config.id }, json: updateData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(200); const result = await response.json() as any; expect(result.configValue).toBe(updateData.configValue); // 验证缓存已被刷新 const afterUpdateCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey); expect(afterUpdateCache).toBeNull(); }); it('应该通过自定义删除路由刷新缓存', async () => { // 先创建配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const config = await systemConfigService.create({ configKey: 'app.analytics.enabled', configValue: 'true', description: '控制分析功能是否开启', tenantId: testUser.tenantId } as any); // 设置缓存 await redisUtil.setSystemConfig(testUser.tenantId, config.configKey, 'cached-value', 3600); const initialCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey); expect(initialCache).toBe('cached-value'); // 通过自定义路由删除配置 const response = await client[':id'].$delete({ param: { id: config.id } }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(204); // 验证缓存已被刷新 const afterDeleteCache = await redisUtil.getSystemConfig(testUser.tenantId, config.configKey); expect(afterDeleteCache).toBeNull(); }); }); describe('自定义路由多租户隔离验证', () => { let tenant1User: any; let tenant2User: any; let tenant1Token: string; let tenant2Token: string; beforeEach(async () => { const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); // 创建租户1的用户 tenant1User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant1_customroutes', password: 'TestPassword123!', email: 'tenant1_customroutes@example.com', tenantId: 1 }); // 创建租户2的用户 tenant2User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant2_customroutes', password: 'TestPassword123!', email: 'tenant2_customroutes@example.com', tenantId: 2 }); // 生成租户用户的token tenant1Token = authService.generateToken(tenant1User); tenant2Token = authService.generateToken(tenant2User); // 清除测试缓存 await redisUtil.deleteSystemConfig(1, 'app.shared.config'); await redisUtil.deleteSystemConfig(2, 'app.shared.config'); }); it('应该确保多租户缓存隔离', async () => { const configData = { configKey: 'app.shared.config', configValue: 'tenant1-value', description: '共享配置' }; // 租户1创建配置 const response1 = await client.index.$post({ json: configData }, { headers: { 'Authorization': `Bearer ${tenant1Token}` } }); expect(response1.status).toBe(201); // 租户2创建配置 const response2 = await client.index.$post({ json: { ...configData, configValue: 'tenant2-value' } }, { headers: { 'Authorization': `Bearer ${tenant2Token}` } }); expect(response2.status).toBe(201); // 验证租户1的缓存操作不影响租户2 const tenant1Cache = await redisUtil.getSystemConfig(1, configData.configKey); const tenant2Cache = await redisUtil.getSystemConfig(2, configData.configKey); // 两个租户的缓存都应该是空的,因为setConfig会删除缓存 expect(tenant1Cache).toBeNull(); expect(tenant2Cache).toBeNull(); }); it('应该验证租户ID正确提取', async () => { const configData = { configKey: 'app.tenant.specific', configValue: 'tenant-specific-value', description: '租户特定配置' }; // 租户1创建配置 const response = await client.index.$post({ json: configData }, { headers: { 'Authorization': `Bearer ${tenant1Token}` } }); expect(response.status).toBe(201); const result = await response.json() as any; // 验证配置属于正确的租户 expect(result.tenantId).toBe(1); // 验证租户2无法访问租户1的配置 const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); const systemConfigService = new SystemConfigServiceMt(dataSource); const tenant2Configs = await systemConfigService.getAllConfigs(2); const tenant2HasConfig = tenant2Configs.some(config => config.configKey === configData.configKey); expect(tenant2HasConfig).toBe(false); }); }); });