WARNING: This commit contains code with significant issues that need immediate attention: 1. Type Safety Issues: - Unused import ZulipAccountsService causing compilation warnings - Implicit 'any' type in formatZulipAccount method parameter - Type inconsistencies in service injections 2. Service Integration Problems: - Inconsistent service interface usage - Missing proper type definitions for injected services - Potential runtime errors due to type mismatches 3. Code Quality Issues: - Violation of TypeScript strict mode requirements - Inconsistent error handling patterns - Missing proper interface implementations Files affected: - src/business/admin/database_management.service.ts (main issue) - Multiple test files and service implementations - Configuration and documentation updates Next steps required: 1. Fix TypeScript compilation errors 2. Implement proper type safety 3. Resolve service injection inconsistencies 4. Add comprehensive error handling 5. Update tests to match new implementations Impact: High - affects admin functionality and system stability Priority: Urgent - requires immediate review and fixes Author: moyin Date: 2026-01-10
393 lines
12 KiB
TypeScript
393 lines
12 KiB
TypeScript
/**
|
||
* 真实Zulip API测试
|
||
*
|
||
* 功能描述:
|
||
* - 测试与真实Zulip服务器的HTTP通信
|
||
* - 验证API请求格式、认证和响应处理
|
||
* - 需要真实的Zulip服务器配置才能运行
|
||
*
|
||
* 注意:
|
||
* - 这些测试需要设置环境变量:ZULIP_SERVER_URL, ZULIP_BOT_EMAIL, ZULIP_BOT_API_KEY
|
||
* - 如果没有配置,测试将被跳过
|
||
* - 测试会在真实服务器上创建消息,请谨慎使用
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.0
|
||
* @since 2026-01-10
|
||
*/
|
||
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
import { ZulipClientService, ZulipClientConfig, SendMessageResult } from '../../src/core/zulip_core/services/zulip_client.service';
|
||
|
||
// 测试配置
|
||
const REAL_ZULIP_CONFIG = {
|
||
serverUrl: process.env.ZULIP_SERVER_URL || '',
|
||
botEmail: process.env.ZULIP_BOT_EMAIL || '',
|
||
botApiKey: process.env.ZULIP_BOT_API_KEY || '',
|
||
testStream: process.env.ZULIP_TEST_STREAM || 'test-stream',
|
||
testTopic: process.env.ZULIP_TEST_TOPIC || 'API Test',
|
||
};
|
||
|
||
// 检查是否有真实配置
|
||
const hasRealConfig: boolean = !!(REAL_ZULIP_CONFIG.serverUrl &&
|
||
REAL_ZULIP_CONFIG.botEmail &&
|
||
REAL_ZULIP_CONFIG.botApiKey);
|
||
|
||
describe('Real Zulip API Integration', () => {
|
||
let service: ZulipClientService;
|
||
let clientConfig: ZulipClientConfig;
|
||
|
||
beforeAll(async () => {
|
||
if (!hasRealConfig) {
|
||
console.warn('跳过真实Zulip API测试:缺少环境变量配置');
|
||
console.warn('需要设置: ZULIP_SERVER_URL, ZULIP_BOT_EMAIL, ZULIP_BOT_API_KEY');
|
||
return;
|
||
}
|
||
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
providers: [ZulipClientService],
|
||
}).compile();
|
||
|
||
service = module.get<ZulipClientService>(ZulipClientService);
|
||
|
||
clientConfig = {
|
||
username: REAL_ZULIP_CONFIG.botEmail,
|
||
apiKey: REAL_ZULIP_CONFIG.botApiKey,
|
||
realm: REAL_ZULIP_CONFIG.serverUrl,
|
||
};
|
||
});
|
||
|
||
// 如果没有真实配置,跳过所有测试
|
||
const testIf = (condition: boolean) => condition ? it : it.skip;
|
||
|
||
describe('API连接测试', () => {
|
||
testIf(hasRealConfig)('应该能够连接到Zulip服务器', async () => {
|
||
const clientInstance = await service.createClient('test-user', clientConfig);
|
||
|
||
expect(clientInstance).toBeDefined();
|
||
expect(clientInstance.isValid).toBe(true);
|
||
expect(clientInstance.config.realm).toBe(REAL_ZULIP_CONFIG.serverUrl);
|
||
|
||
// 清理
|
||
await service.destroyClient(clientInstance);
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该能够验证API Key', async () => {
|
||
const clientInstance = await service.createClient('test-user', clientConfig);
|
||
|
||
const isValid = await service.validateApiKey(clientInstance);
|
||
expect(isValid).toBe(true);
|
||
|
||
await service.destroyClient(clientInstance);
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该拒绝无效的API Key', async () => {
|
||
const invalidConfig = {
|
||
...clientConfig,
|
||
apiKey: 'invalid-api-key-12345',
|
||
};
|
||
|
||
await expect(service.createClient('test-user', invalidConfig))
|
||
.rejects.toThrow();
|
||
}, 10000);
|
||
});
|
||
|
||
describe('消息发送测试', () => {
|
||
let clientInstance: any;
|
||
|
||
beforeEach(async () => {
|
||
if (!hasRealConfig) return;
|
||
|
||
clientInstance = await service.createClient('test-user', clientConfig);
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (clientInstance) {
|
||
await service.destroyClient(clientInstance);
|
||
}
|
||
});
|
||
|
||
testIf(hasRealConfig)('应该能够发送消息到Zulip', async () => {
|
||
const testMessage = `Test message from automated test - ${new Date().toISOString()}`;
|
||
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
REAL_ZULIP_CONFIG.testTopic,
|
||
testMessage
|
||
);
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(result.messageId).toBeDefined();
|
||
expect(typeof result.messageId).toBe('number');
|
||
|
||
console.log(`消息发送成功,ID: ${result.messageId}`);
|
||
}, 15000);
|
||
|
||
testIf(hasRealConfig)('应该处理不存在的Stream', async () => {
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
'nonexistent-stream-12345',
|
||
'test-topic',
|
||
'This should fail'
|
||
);
|
||
|
||
expect(result.success).toBe(false);
|
||
expect(result.error).toBeDefined();
|
||
console.log(`预期的错误: ${result.error}`);
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该能够发送包含特殊字符的消息', async () => {
|
||
const specialMessage = `特殊字符测试 🎮🎯🚀 @#$%^&*() - ${new Date().toISOString()}`;
|
||
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
REAL_ZULIP_CONFIG.testTopic,
|
||
specialMessage
|
||
);
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(result.messageId).toBeDefined();
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该能够发送Markdown格式的消息', async () => {
|
||
const markdownMessage = `
|
||
# Markdown测试消息
|
||
|
||
**粗体文本** 和 *斜体文本*
|
||
|
||
- 列表项 1
|
||
- 列表项 2
|
||
|
||
\`代码块\`
|
||
|
||
> 引用文本
|
||
|
||
[链接](https://example.com)
|
||
|
||
时间戳: ${new Date().toISOString()}
|
||
`.trim();
|
||
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
REAL_ZULIP_CONFIG.testTopic,
|
||
markdownMessage
|
||
);
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(result.messageId).toBeDefined();
|
||
}, 10000);
|
||
});
|
||
|
||
describe('事件队列测试', () => {
|
||
let clientInstance: any;
|
||
|
||
beforeEach(async () => {
|
||
if (!hasRealConfig) return;
|
||
|
||
clientInstance = await service.createClient('test-user', clientConfig);
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (clientInstance) {
|
||
await service.destroyClient(clientInstance);
|
||
}
|
||
});
|
||
|
||
testIf(hasRealConfig)('应该能够注册事件队列', async () => {
|
||
const result = await service.registerQueue(clientInstance, ['message']);
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(result.queueId).toBeDefined();
|
||
expect(result.lastEventId).toBeDefined();
|
||
expect(typeof result.lastEventId).toBe('number');
|
||
|
||
console.log(`队列注册成功,ID: ${result.queueId}, 最后事件ID: ${result.lastEventId}`);
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该能够获取事件', async () => {
|
||
// 先注册队列
|
||
const registerResult = await service.registerQueue(clientInstance, ['message']);
|
||
expect(registerResult.success).toBe(true);
|
||
|
||
// 获取事件(非阻塞模式)
|
||
const eventsResult = await service.getEvents(clientInstance, true);
|
||
|
||
expect(eventsResult.success).toBe(true);
|
||
expect(Array.isArray(eventsResult.events)).toBe(true);
|
||
|
||
console.log(`获取到 ${eventsResult.events?.length || 0} 个事件`);
|
||
}, 10000);
|
||
|
||
testIf(hasRealConfig)('应该能够注销事件队列', async () => {
|
||
// 先注册队列
|
||
const registerResult = await service.registerQueue(clientInstance, ['message']);
|
||
expect(registerResult.success).toBe(true);
|
||
|
||
// 注销队列
|
||
const deregisterResult = await service.deregisterQueue(clientInstance);
|
||
expect(deregisterResult).toBe(true);
|
||
expect(clientInstance.queueId).toBeUndefined();
|
||
}, 10000);
|
||
});
|
||
|
||
describe('HTTP请求详细测试', () => {
|
||
testIf(hasRealConfig)('应该发送正确格式的HTTP请求', async () => {
|
||
// 这个测试验证HTTP请求的具体格式
|
||
const clientInstance = await service.createClient('test-user', clientConfig);
|
||
|
||
// 监听HTTP请求(这需要拦截zulip-js的请求)
|
||
const originalSend = clientInstance.client.messages.send;
|
||
let capturedRequest: any = null;
|
||
|
||
clientInstance.client.messages.send = jest.fn().mockImplementation(async (params) => {
|
||
capturedRequest = params;
|
||
return originalSend.call(clientInstance.client.messages, params);
|
||
});
|
||
|
||
const testMessage = `HTTP格式测试 - ${new Date().toISOString()}`;
|
||
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
REAL_ZULIP_CONFIG.testTopic,
|
||
testMessage
|
||
);
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(capturedRequest).toBeDefined();
|
||
expect(capturedRequest.type).toBe('stream');
|
||
expect(capturedRequest.to).toBe(REAL_ZULIP_CONFIG.testStream);
|
||
expect(capturedRequest.subject).toBe(REAL_ZULIP_CONFIG.testTopic);
|
||
expect(capturedRequest.content).toBe(testMessage);
|
||
|
||
await service.destroyClient(clientInstance);
|
||
}, 15000);
|
||
});
|
||
|
||
describe('错误处理测试', () => {
|
||
testIf(hasRealConfig)('应该处理网络超时', async () => {
|
||
const clientInstance = await service.createClient('test-user', clientConfig);
|
||
|
||
// 模拟网络超时(通过修改客户端配置或使用无效的服务器地址)
|
||
const timeoutConfig = {
|
||
...clientConfig,
|
||
realm: 'https://timeout-test.invalid-domain-12345.com',
|
||
};
|
||
|
||
try {
|
||
const timeoutClient = await service.createClient('timeout-test', timeoutConfig);
|
||
// 如果到达这里,说明没有超时,跳过测试
|
||
await service.destroyClient(timeoutClient);
|
||
console.log('网络超时测试跳过:连接成功');
|
||
} catch (error) {
|
||
// 预期的超时错误
|
||
expect(error).toBeDefined();
|
||
console.log(`预期的超时错误: ${(error as Error).message}`);
|
||
}
|
||
|
||
await service.destroyClient(clientInstance);
|
||
}, 20000);
|
||
|
||
testIf(hasRealConfig)('应该处理认证错误', async () => {
|
||
const invalidConfig = {
|
||
...clientConfig,
|
||
apiKey: 'definitely-invalid-api-key-12345',
|
||
};
|
||
|
||
try {
|
||
await service.createClient('auth-test', invalidConfig);
|
||
fail('应该抛出认证错误');
|
||
} catch (error) {
|
||
expect(error).toBeDefined();
|
||
expect((error as Error).message).toContain('API Key验证失败');
|
||
console.log(`预期的认证错误: ${(error as Error).message}`);
|
||
}
|
||
}, 10000);
|
||
});
|
||
|
||
describe('性能测试', () => {
|
||
testIf(hasRealConfig)('应该测量消息发送性能', async () => {
|
||
const clientInstance = await service.createClient('perf-test', clientConfig);
|
||
const messageCount = 10; // 减少数量以避免对服务器造成压力
|
||
|
||
const startTime = Date.now();
|
||
const promises: Promise<SendMessageResult>[] = [];
|
||
|
||
for (let i = 0; i < messageCount; i++) {
|
||
promises.push(
|
||
service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
'Performance Test',
|
||
`Performance test message ${i} - ${new Date().toISOString()}`
|
||
)
|
||
);
|
||
}
|
||
|
||
const results = await Promise.all(promises);
|
||
const endTime = Date.now();
|
||
const duration = endTime - startTime;
|
||
|
||
// 验证所有消息都成功发送
|
||
results.forEach((result, index) => {
|
||
expect(result.success).toBe(true);
|
||
console.log(`消息 ${index}: ID ${result.messageId}`);
|
||
});
|
||
|
||
const avgTime = duration / messageCount;
|
||
console.log(`发送${messageCount}条消息耗时: ${duration}ms, 平均: ${avgTime.toFixed(2)}ms/条`);
|
||
|
||
// 性能断言(根据网络情况调整)
|
||
expect(avgTime).toBeLessThan(2000); // 平均每条消息不超过2秒
|
||
|
||
await service.destroyClient(clientInstance);
|
||
}, 30000);
|
||
});
|
||
|
||
// 清理测试:在所有测试完成后清理测试数据
|
||
describe('清理测试', () => {
|
||
testIf(hasRealConfig)('应该发送清理完成消息', async () => {
|
||
const clientInstance = await service.createClient('cleanup-test', clientConfig);
|
||
|
||
const cleanupMessage = `
|
||
🧹 自动化测试完成 - ${new Date().toISOString()}
|
||
|
||
本次测试运行的消息已发送完毕。
|
||
如果看到此消息,说明Zulip API集成测试成功完成。
|
||
|
||
测试包括:
|
||
- ✅ API连接和认证
|
||
- ✅ 消息发送和格式化
|
||
- ✅ 事件队列管理
|
||
- ✅ 错误处理
|
||
- ✅ 性能测试
|
||
|
||
所有测试消息可以安全删除。
|
||
`.trim();
|
||
|
||
const result = await service.sendMessage(
|
||
clientInstance,
|
||
REAL_ZULIP_CONFIG.testStream,
|
||
'Test Cleanup',
|
||
cleanupMessage
|
||
);
|
||
|
||
expect(result.success).toBe(true);
|
||
console.log('清理消息发送成功');
|
||
|
||
await service.destroyClient(clientInstance);
|
||
}, 10000);
|
||
});
|
||
});
|
||
|
||
// 导出配置检查函数,供其他测试使用
|
||
export function hasZulipConfig(): boolean {
|
||
return hasRealConfig;
|
||
}
|
||
|
||
export function getZulipTestConfig() {
|
||
return REAL_ZULIP_CONFIG;
|
||
} |