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('系统配置Redis缓存集成测试', () => { let client: ReturnType>; let authService: AuthService; let userService: UserServiceMt; let systemConfigService: SystemConfigServiceMt; 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); systemConfigService = new SystemConfigServiceMt(dataSource); // 创建测试用户并生成token testUser = await TestDataFactory.createTestUser(dataSource, { username: 'testuser_rediscache', password: 'TestPassword123!', email: 'testuser_rediscache@example.com' }); // 生成测试用户的token testToken = authService.generateToken(testUser); // 清除测试前的缓存 await redisUtil.clearTenantSystemConfigs(testUser.tenantId); }); describe('缓存命中测试', () => { it('应该从缓存中获取配置值', async () => { // 先创建配置 const config = await systemConfigService.create({ configKey: 'app.cache.test', configValue: 'cached-value', description: '缓存测试配置', tenantId: testUser.tenantId } as SystemConfigMt); // 第一次查询 - 应该从数据库获取并写入缓存 const firstResult = await systemConfigService.getConfigByKey('app.cache.test', testUser.tenantId); expect(firstResult).toBe('cached-value'); // 验证缓存已写入 const cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.cache.test'); expect(cachedValue).toBe('cached-value'); // 第二次查询 - 应该从缓存获取 const secondResult = await systemConfigService.getConfigByKey('app.cache.test', testUser.tenantId); expect(secondResult).toBe('cached-value'); }); it('应该批量从缓存中获取配置值', async () => { // 创建多个配置 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 firstResult = await systemConfigService.getConfigsByKeys( ['app.feature1.enabled', 'app.feature2.enabled'], testUser.tenantId ); expect(firstResult['app.feature1.enabled']).toBe('true'); expect(firstResult['app.feature2.enabled']).toBe('false'); // 验证缓存已写入 const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, ['app.feature1.enabled', 'app.feature2.enabled']); expect(cachedValues['app.feature1.enabled']).toBe('true'); expect(cachedValues['app.feature2.enabled']).toBe('false'); // 第二次批量查询 - 应该从缓存获取 const secondResult = await systemConfigService.getConfigsByKeys( ['app.feature1.enabled', 'app.feature2.enabled'], testUser.tenantId ); expect(secondResult['app.feature1.enabled']).toBe('true'); expect(secondResult['app.feature2.enabled']).toBe('false'); }); }); describe('缓存失效测试', () => { it('应该在配置更新时清除缓存', async () => { // 创建配置 const config = await systemConfigService.create({ configKey: 'app.update.test', configValue: 'initial-value', tenantId: testUser.tenantId } as SystemConfigMt); // 查询一次以填充缓存 await systemConfigService.getConfigByKey('app.update.test', testUser.tenantId); // 验证缓存已写入 let cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.update.test'); expect(cachedValue).toBe('initial-value'); // 更新配置 await systemConfigService.setConfig('app.update.test', 'updated-value', testUser.tenantId, '更新后的描述'); // 验证缓存已清除 cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.update.test'); expect(cachedValue).toBeNull(); // 再次查询应该从数据库获取新值 const result = await systemConfigService.getConfigByKey('app.update.test', testUser.tenantId); expect(result).toBe('updated-value'); }); it('应该在配置删除时清除缓存', async () => { // 创建配置 const config = await systemConfigService.create({ configKey: 'app.delete.test', configValue: 'to-be-deleted', tenantId: testUser.tenantId } as SystemConfigMt); // 查询一次以填充缓存 await systemConfigService.getConfigByKey('app.delete.test', testUser.tenantId); // 验证缓存已写入 let cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.delete.test'); expect(cachedValue).toBe('to-be-deleted'); // 删除配置 await systemConfigService.deleteConfig('app.delete.test', testUser.tenantId); // 验证缓存已清除 cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, 'app.delete.test'); expect(cachedValue).toBeNull(); }); }); describe('缓存穿透保护测试', () => { it('应该防止缓存穿透攻击', async () => { const nonExistentKey = 'app.nonexistent.config'; // 第一次查询不存在的配置 const firstResult = await systemConfigService.getConfigByKey(nonExistentKey, testUser.tenantId); expect(firstResult).toBeNull(); // 验证空值缓存已设置 const cachedValue = await redisUtil.getSystemConfig(testUser.tenantId, nonExistentKey); expect(redisUtil.isNullValue(cachedValue)).toBe(true); // 第二次查询应该从空值缓存返回null const secondResult = await systemConfigService.getConfigByKey(nonExistentKey, testUser.tenantId); expect(secondResult).toBeNull(); }); it('应该在批量查询中防止缓存穿透', async () => { const existentKey = 'app.existent.config'; const nonExistentKey = 'app.nonexistent.config'; // 创建一个存在的配置 await systemConfigService.create({ configKey: existentKey, configValue: 'existent-value', tenantId: testUser.tenantId } as SystemConfigMt); // 批量查询包含存在和不存在的配置 const result = await systemConfigService.getConfigsByKeys( [existentKey, nonExistentKey], testUser.tenantId ); expect(result[existentKey]).toBe('existent-value'); expect(result[nonExistentKey]).toBeUndefined(); // 验证空值缓存已设置 const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, [existentKey, nonExistentKey]); expect(cachedValues[existentKey]).toBe('existent-value'); expect(redisUtil.isNullValue(cachedValues[nonExistentKey])).toBe(true); }); }); describe('多租户缓存隔离测试', () => { let tenant1User: any; let tenant2User: any; beforeEach(async () => { const dataSource = await IntegrationTestDatabase.getDataSource(); if (!dataSource) throw new Error('Database not initialized'); // 创建租户1的用户 tenant1User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant1_user_cache', password: 'TestPassword123!', email: 'tenant1_cache@example.com', tenantId: 1 }); // 创建租户2的用户 tenant2User = await TestDataFactory.createTestUser(dataSource, { username: 'tenant2_user_cache', password: 'TestPassword123!', email: 'tenant2_cache@example.com', tenantId: 2 }); // 清除测试前的缓存 await redisUtil.clearTenantSystemConfigs(1); await redisUtil.clearTenantSystemConfigs(2); }); it('应该按租户隔离缓存', async () => { const sharedConfigKey = 'app.shared.config'; // 为租户1创建配置 await systemConfigService.create({ configKey: sharedConfigKey, configValue: 'tenant1-value', tenantId: 1 } as SystemConfigMt); // 为租户2创建配置 await systemConfigService.create({ configKey: sharedConfigKey, configValue: 'tenant2-value', tenantId: 2 } as SystemConfigMt); // 查询租户1的配置 const tenant1Result = await systemConfigService.getConfigByKey(sharedConfigKey, 1); expect(tenant1Result).toBe('tenant1-value'); // 查询租户2的配置 const tenant2Result = await systemConfigService.getConfigByKey(sharedConfigKey, 2); expect(tenant2Result).toBe('tenant2-value'); // 验证缓存隔离 const tenant1Cached = await redisUtil.getSystemConfig(1, sharedConfigKey); const tenant2Cached = await redisUtil.getSystemConfig(2, sharedConfigKey); expect(tenant1Cached).toBe('tenant1-value'); expect(tenant2Cached).toBe('tenant2-value'); }); }); describe('缓存预热测试', () => { it('应该成功预热缓存', async () => { // 创建一些常用配置 await systemConfigService.create({ configKey: 'app.login.enabled', configValue: 'true', tenantId: testUser.tenantId } as SystemConfigMt); await systemConfigService.create({ configKey: 'app.payment.enabled', configValue: 'false', tenantId: testUser.tenantId } as SystemConfigMt); // 预热缓存 await systemConfigService.warmUpCache(testUser.tenantId); // 验证缓存已预热 const cachedValues = await redisUtil.getSystemConfigs(testUser.tenantId, [ 'app.login.enabled', 'app.payment.enabled', 'app.notification.enabled' ]); expect(cachedValues['app.login.enabled']).toBe('true'); expect(cachedValues['app.payment.enabled']).toBe('false'); expect(redisUtil.isNullValue(cachedValues['app.notification.enabled'])).toBe(true); }); }); });