|
@@ -12,6 +12,7 @@ import { FileMt } from '@d8d/core-module-mt/file-module-mt';
|
|
|
import { TestDataFactory } from '../utils/integration-test-db';
|
|
import { TestDataFactory } from '../utils/integration-test-db';
|
|
|
import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
|
|
import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
|
|
|
import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
|
|
import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
|
|
|
|
|
+import { redisUtil } from '@d8d/shared-utils';
|
|
|
|
|
|
|
|
// 设置集成测试钩子
|
|
// 设置集成测试钩子
|
|
|
setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
|
|
setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
|
|
@@ -316,4 +317,224 @@ describe('系统配置路由API集成测试 (使用hono/testing)', () => {
|
|
|
expect(response.status).toBe(400);
|
|
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(200);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证缓存已被刷新
|
|
|
|
|
+ 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);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|