/** * Zulip账号关联服务数据库测试 * * 功能描述: * - 专门测试数据库模式下的真实数据库操作 * - 需要配置数据库环境变量才能运行 * - 测试真实的CRUD操作和业务逻辑 * * 运行条件: * - 需要设置环境变量:DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_NAME * - 数据库中需要存在 zulip_accounts 表 * * @author angjustinl * @version 1.0.0 * @since 2026-01-10 */ import { Test, TestingModule } from '@nestjs/testing'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule } from '@nestjs/config'; import { CacheModule } from '@nestjs/cache-manager'; import { ZulipAccountsService } from '../../src/core/db/zulip_accounts/zulip_accounts.service'; import { ZulipAccountsRepository } from '../../src/core/db/zulip_accounts/zulip_accounts.repository'; import { ZulipAccounts } from '../../src/core/db/zulip_accounts/zulip_accounts.entity'; import { Users } from '../../src/core/db/users/users.entity'; import { AppLoggerService } from '../../src/core/utils/logger/logger.service'; /** * 检查是否配置了数据库 */ function isDatabaseConfigured(): boolean { const requiredEnvVars = ['DB_HOST', 'DB_PORT', 'DB_USERNAME', 'DB_PASSWORD', 'DB_NAME']; return requiredEnvVars.every(varName => process.env[varName]); } // 只有在配置了数据库时才运行这些测试 const describeDatabase = isDatabaseConfigured() ? describe : describe.skip; describeDatabase('ZulipAccountsService - Database Mode', () => { let service: ZulipAccountsService; let module: TestingModule; // 只有在数据库配置完整时才输出这些信息 if (isDatabaseConfigured()) { console.log('🗄️ 运行数据库模式测试'); console.log('📊 使用真实数据库连接进行测试'); } beforeAll(async () => { module = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ envFilePath: ['.env.test', '.env'], isGlobal: true, }), TypeOrmModule.forRoot({ type: 'mysql', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT || '3306'), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, entities: [ZulipAccounts, Users], synchronize: false, logging: false, }), TypeOrmModule.forFeature([ZulipAccounts, Users]), CacheModule.register({ ttl: 300, max: 1000, }), ], providers: [ ZulipAccountsService, ZulipAccountsRepository, AppLoggerService, ], }).compile(); service = module.get(ZulipAccountsService); }, 30000); // 增加超时时间 afterAll(async () => { if (module) { await module.close(); } }); // 生成唯一的测试数据 const generateTestData = (suffix: string = Date.now().toString()) => { const timestamp = Date.now(); const uniqueId = timestamp + Math.floor(Math.random() * 1000); // 添加随机数避免冲突 return { gameUserId: uniqueId.toString(), // 使用纯数字字符串 zulipUserId: parseInt(`8${timestamp.toString().slice(-5)}`), zulipEmail: `test_db_${timestamp}_${suffix}@example.com`, zulipFullName: `数据库测试用户_${timestamp}_${suffix}`, zulipApiKeyEncrypted: 'encrypted_api_key_for_db_test', status: 'active' as const, }; }; it('should be defined', () => { expect(service).toBeDefined(); }); describe('Database CRUD Operations', () => { it('should create and retrieve account from database', async () => { const testData = generateTestData('crud'); // 创建账号 const created = await service.create(testData); expect(created).toBeDefined(); expect(created.gameUserId).toBe(testData.gameUserId); expect(created.zulipEmail).toBe(testData.zulipEmail); expect(created.status).toBe('active'); // 根据游戏用户ID查找 const found = await service.findByGameUserId(testData.gameUserId); expect(found).toBeDefined(); expect(found?.id).toBe(created.id); expect(found?.zulipUserId).toBe(testData.zulipUserId); // 清理测试数据 await service.deleteByGameUserId(testData.gameUserId); }, 15000); it('should handle duplicate creation properly', async () => { const testData = generateTestData('duplicate'); // 创建第一个账号 const created = await service.create(testData); expect(created).toBeDefined(); // 尝试创建重复账号,应该抛出异常 await expect(service.create(testData)).rejects.toThrow(); // 清理测试数据 await service.deleteByGameUserId(testData.gameUserId); }, 15000); it('should update account in database', async () => { const testData = generateTestData('update'); // 创建账号 const created = await service.create(testData); // 更新账号 const updated = await service.update(created.id, { zulipFullName: '更新后的用户名', status: 'inactive', }); expect(updated.zulipFullName).toBe('更新后的用户名'); expect(updated.status).toBe('inactive'); // 清理测试数据 await service.deleteByGameUserId(testData.gameUserId); }, 15000); it('should delete account from database', async () => { const testData = generateTestData('delete'); // 创建账号 const created = await service.create(testData); // 删除账号 const deleted = await service.delete(created.id); expect(deleted).toBe(true); // 验证账号已被删除 const found = await service.findByGameUserId(testData.gameUserId); expect(found).toBeNull(); }, 15000); }); describe('Database Business Logic', () => { it('should check email existence in database', async () => { const testData = generateTestData('email_check'); // 邮箱不存在时应该返回false const notExists = await service.existsByEmail(testData.zulipEmail); expect(notExists).toBe(false); // 创建账号 await service.create(testData); // 邮箱存在时应该返回true const exists = await service.existsByEmail(testData.zulipEmail); expect(exists).toBe(true); // 清理测试数据 await service.deleteByGameUserId(testData.gameUserId); }, 15000); it('should get status statistics from database', async () => { const stats = await service.getStatusStatistics(); expect(typeof stats.active).toBe('number'); expect(typeof stats.inactive).toBe('number'); expect(typeof stats.suspended).toBe('number'); expect(typeof stats.error).toBe('number'); expect(typeof stats.total).toBe('number'); expect(stats.total).toBe(stats.active + stats.inactive + stats.suspended + stats.error); }, 15000); it('should verify account in database', async () => { const testData = generateTestData('verify'); // 创建账号 await service.create(testData); // 验证账号 const result = await service.verifyAccount(testData.gameUserId); expect(result.success).toBe(true); expect(result.isValid).toBe(true); expect(result.verifiedAt).toBeDefined(); // 清理测试数据 await service.deleteByGameUserId(testData.gameUserId); }, 15000); }); }); // 如果没有配置数据库,显示跳过信息 if (!isDatabaseConfigured()) { console.log('⚠️ 数据库测试已跳过:未检测到数据库配置'); console.log('💡 要运行数据库测试,请设置以下环境变量:'); console.log(' - DB_HOST'); console.log(' - DB_PORT'); console.log(' - DB_USERNAME'); console.log(' - DB_PASSWORD'); console.log(' - DB_NAME'); }