refactor:项目架构重构和命名规范化

- 统一文件命名为snake_case格式(kebab-case  snake_case)
- 重构zulip模块为zulip_core,明确Core层职责
- 重构user-mgmt模块为user_mgmt,统一命名规范
- 调整模块依赖关系,优化架构分层
- 删除过时的文件和目录结构
- 更新相关文档和配置文件

本次重构涉及大量文件重命名和模块重组,
旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
moyin
2026-01-08 00:14:14 +08:00
parent 4fa4bd1a70
commit bb796a2469
178 changed files with 24767 additions and 3484 deletions

View File

@@ -0,0 +1,553 @@
/**
* RealRedisService集成测试
*
* 功能描述:
* - 使用真实Redis连接进行集成测试
* - 测试Redis服务器连接和断开
* - 验证数据持久性和一致性
* - 测试Redis服务的完整工作流程
*
* 职责分离:
* - 集成测试测试与真实Redis服务器的交互
* - 连接测试验证Redis连接管理
* - 数据一致性:测试数据的持久化和读取
* - 性能测试验证Redis操作的性能表现
*
* 最近修改:
* - 2025-01-07: 功能新增 - 创建RealRedisService完整集成测试验证真实Redis交互
*
* @author moyin
* @version 1.0.0
* @since 2025-01-07
* @lastModified 2025-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { RealRedisService } from './real_redis.service';
import Redis from 'ioredis';
describe('RealRedisService Integration', () => {
let service: RealRedisService;
let module: TestingModule;
let configService: ConfigService;
// 测试配置 - 使用测试Redis实例
const testRedisConfig = {
REDIS_HOST: process.env.TEST_REDIS_HOST || 'localhost',
REDIS_PORT: parseInt(process.env.TEST_REDIS_PORT || '6379'),
REDIS_PASSWORD: process.env.TEST_REDIS_PASSWORD,
REDIS_DB: parseInt(process.env.TEST_REDIS_DB || '15'), // 使用DB 15进行测试
};
beforeAll(async () => {
// 检查是否有可用的Redis服务器
const testRedis = new Redis({
host: testRedisConfig.REDIS_HOST,
port: testRedisConfig.REDIS_PORT,
password: testRedisConfig.REDIS_PASSWORD,
db: testRedisConfig.REDIS_DB,
lazyConnect: true,
maxRetriesPerRequest: 1,
});
try {
await testRedis.ping();
// 确保连接被正确断开
testRedis.disconnect(false);
} catch (error) {
console.warn('Redis服务器不可用跳过集成测试:', (error as Error).message);
// 确保连接被正确断开
testRedis.disconnect(false);
return;
}
// 创建测试模块
module = await Test.createTestingModule({
providers: [
RealRedisService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string, defaultValue?: any) => {
return testRedisConfig[key] || defaultValue;
}),
},
},
],
}).compile();
service = module.get<RealRedisService>(RealRedisService);
configService = module.get<ConfigService>(ConfigService);
// 清空测试数据库
try {
await service.flushall();
} catch (error) {
// 忽略清空数据时的错误
}
});
afterAll(async () => {
if (service) {
try {
// 清空测试数据
await service.flushall();
} catch (error) {
// 忽略清空数据时的错误
}
try {
// 断开连接
service.onModuleDestroy();
} catch (error) {
// 忽略断开连接时的错误
}
}
if (module) {
await module.close();
}
});
beforeEach(async () => {
// 每个测试前清空数据
if (service) {
try {
await service.flushall();
} catch (error) {
// 忽略清空数据时的错误
}
}
});
// 检查Redis是否可用的辅助函数
const skipIfRedisUnavailable = () => {
if (!service) {
return true; // 返回true表示应该跳过测试
}
return false;
};
describe('Redis连接管理', () => {
it('should connect to Redis server successfully', () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
expect(service).toBeDefined();
});
it('should use correct Redis configuration', () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
expect(configService.get).toHaveBeenCalledWith('REDIS_HOST', 'localhost');
expect(configService.get).toHaveBeenCalledWith('REDIS_PORT', 6379);
expect(configService.get).toHaveBeenCalledWith('REDIS_DB', 0);
});
});
describe('基础键值操作', () => {
it('should set and get string values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:string', 'Hello Redis');
const result = await service.get('test:string');
expect(result).toBe('Hello Redis');
});
it('should handle non-existent keys', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const result = await service.get('test:nonexistent');
expect(result).toBeNull();
});
it('should delete existing keys', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:delete', 'to be deleted');
const deleted = await service.del('test:delete');
const result = await service.get('test:delete');
expect(deleted).toBe(true);
expect(result).toBeNull();
});
it('should return false when deleting non-existent keys', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const deleted = await service.del('test:nonexistent');
expect(deleted).toBe(false);
});
it('should check key existence', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:exists', 'exists');
const exists = await service.exists('test:exists');
const notExists = await service.exists('test:notexists');
expect(exists).toBe(true);
expect(notExists).toBe(false);
});
});
describe('过期时间管理', () => {
it('should set keys with TTL', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:ttl', 'expires soon', 2);
const ttl = await service.ttl('test:ttl');
expect(ttl).toBeGreaterThan(0);
expect(ttl).toBeLessThanOrEqual(2);
});
it('should expire keys after TTL', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:expire', 'will expire', 1);
// 等待过期
await new Promise(resolve => setTimeout(resolve, 1100));
const result = await service.get('test:expire');
expect(result).toBeNull();
}, 2000);
it('should set expiration on existing keys', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:expire_later', 'set expiration later');
await service.expire('test:expire_later', 2);
const ttl = await service.ttl('test:expire_later');
expect(ttl).toBeGreaterThan(0);
expect(ttl).toBeLessThanOrEqual(2);
});
it('should return -1 for keys without expiration', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:no_expire', 'never expires');
const ttl = await service.ttl('test:no_expire');
expect(ttl).toBe(-1);
});
it('should return -2 for non-existent keys', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const ttl = await service.ttl('test:nonexistent');
expect(ttl).toBe(-2);
});
it('should use setex for atomic set with expiration', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.setex('test:setex', 2, 'atomic set with expiration');
const value = await service.get('test:setex');
const ttl = await service.ttl('test:setex');
expect(value).toBe('atomic set with expiration');
expect(ttl).toBeGreaterThan(0);
expect(ttl).toBeLessThanOrEqual(2);
});
});
describe('数值操作', () => {
it('should increment numeric values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const result1 = await service.incr('test:counter');
const result2 = await service.incr('test:counter');
const result3 = await service.incr('test:counter');
expect(result1).toBe(1);
expect(result2).toBe(2);
expect(result3).toBe(3);
});
it('should increment existing numeric values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('test:existing_counter', '10');
const result = await service.incr('test:existing_counter');
expect(result).toBe(11);
});
});
describe('集合操作', () => {
it('should add and retrieve set members', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.sadd('test:set', 'member1');
await service.sadd('test:set', 'member2');
await service.sadd('test:set', 'member3');
const members = await service.smembers('test:set');
expect(members).toHaveLength(3);
expect(members).toContain('member1');
expect(members).toContain('member2');
expect(members).toContain('member3');
});
it('should remove set members', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.sadd('test:set_remove', 'member1');
await service.sadd('test:set_remove', 'member2');
await service.sadd('test:set_remove', 'member3');
await service.srem('test:set_remove', 'member2');
const members = await service.smembers('test:set_remove');
expect(members).toHaveLength(2);
expect(members).toContain('member1');
expect(members).toContain('member3');
expect(members).not.toContain('member2');
});
it('should return empty array for non-existent sets', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const members = await service.smembers('test:nonexistent_set');
expect(members).toEqual([]);
});
it('should handle duplicate set members', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.sadd('test:duplicate_set', 'member1');
await service.sadd('test:duplicate_set', 'member1'); // 重复添加
await service.sadd('test:duplicate_set', 'member2');
const members = await service.smembers('test:duplicate_set');
expect(members).toHaveLength(2);
expect(members).toContain('member1');
expect(members).toContain('member2');
});
});
describe('数据持久性和一致性', () => {
it('should persist data across operations', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
// 设置多种类型的数据
await service.set('test:persist:string', 'persistent string');
await service.set('test:persist:number', '42');
await service.sadd('test:persist:set', 'set_member');
await service.incr('test:persist:counter');
// 验证数据持久性
const stringValue = await service.get('test:persist:string');
const numberValue = await service.get('test:persist:number');
const setMembers = await service.smembers('test:persist:set');
const counterValue = await service.get('test:persist:counter');
expect(stringValue).toBe('persistent string');
expect(numberValue).toBe('42');
expect(setMembers).toContain('set_member');
expect(counterValue).toBe('1');
});
it('should maintain data consistency during concurrent operations', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
// 并发执行多个操作
const promises = [];
for (let i = 0; i < 10; i++) {
promises.push(service.incr('test:concurrent:counter'));
promises.push(service.sadd('test:concurrent:set', `member${i}`));
}
await Promise.all(promises);
// 验证结果一致性
const counterValue = await service.get('test:concurrent:counter');
const setMembers = await service.smembers('test:concurrent:set');
expect(parseInt(counterValue)).toBe(10);
expect(setMembers).toHaveLength(10);
});
});
describe('清空操作', () => {
it('should clear all data with flushall', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
// 设置一些测试数据
await service.set('test:flush1', 'value1');
await service.set('test:flush2', 'value2');
await service.sadd('test:flush_set', 'member');
// 清空所有数据
await service.flushall();
// 验证数据已清空
const value1 = await service.get('test:flush1');
const value2 = await service.get('test:flush2');
const setMembers = await service.smembers('test:flush_set');
expect(value1).toBeNull();
expect(value2).toBeNull();
expect(setMembers).toEqual([]);
});
});
describe('错误处理和边界情况', () => {
it('should handle empty string keys and values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
await service.set('', 'empty key');
await service.set('empty_value', '');
const emptyKeyValue = await service.get('');
const emptyValue = await service.get('empty_value');
expect(emptyKeyValue).toBe('empty key');
expect(emptyValue).toBe('');
});
it('should handle very long keys and values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const longKey = 'test:' + 'a'.repeat(1000);
const longValue = 'b'.repeat(10000);
await service.set(longKey, longValue);
const result = await service.get(longKey);
expect(result).toBe(longValue);
});
it('should handle special characters in keys and values', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const specialKey = 'test:特殊字符:🚀:key';
const specialValue = 'Special value with 特殊字符 and 🎉 emojis';
await service.set(specialKey, specialValue);
const result = await service.get(specialKey);
expect(result).toBe(specialValue);
});
});
describe('性能测试', () => {
it('should handle multiple operations efficiently', async () => {
if (skipIfRedisUnavailable()) {
console.log('跳过测试Redis服务器不可用');
return;
}
const startTime = Date.now();
const operations = 100;
// 执行大量操作
const promises = [];
for (let i = 0; i < operations; i++) {
promises.push(service.set(`test:perf:${i}`, `value${i}`));
}
await Promise.all(promises);
// 读取所有数据
const readPromises = [];
for (let i = 0; i < operations; i++) {
readPromises.push(service.get(`test:perf:${i}`));
}
const results = await Promise.all(readPromises);
const endTime = Date.now();
const duration = endTime - startTime;
// 验证结果正确性
expect(results).toHaveLength(operations);
results.forEach((result, index) => {
expect(result).toBe(`value${index}`);
});
// 性能检查(应该在合理时间内完成)
expect(duration).toBeLessThan(5000); // 5秒内完成
}, 10000);
});
});