/** * Mock AI Provider HTTP Server * 用于集成测试中替代 DeepSeek API。 * 支持: 正常响应 / 超时 / 429 / 500 / 非法 JSON */ import { createServer, Server, IncomingMessage, ServerResponse } from 'http'; interface Behavior { status: number; response: unknown; delayMs?: number; } const DEFAULT_RESPONSE = { id: 'mock-deepseek-001', object: 'chat.completion', created: Math.floor(Date.now() / 1000), model: 'deepseek-chat', choices: [{ index: 0, message: { role: 'assistant', content: JSON.stringify({ score: 85, summary: 'Mock AI: good understanding of the topic', strengths: ['Clear explanation', 'Accurate facts'], weaknesses: ['Could elaborate more on edge cases'], confidence: 0.9, learningState: 'proficient', riskLevel: 'low', }), }, finish_reason: 'stop', }], usage: { prompt_tokens: 100, completion_tokens: 50, total_tokens: 150 }, }; export class MockAIProvider { private server: Server | null = null; private port = 0; private behavior: Behavior = { status: 200, response: DEFAULT_RESPONSE }; // ========== behavior controls ========== setNormal() { this.behavior = { status: 200, response: DEFAULT_RESPONSE }; } setTimeout() { this.behavior = { status: 200, response: DEFAULT_RESPONSE, delayMs: 120_000 }; } set429() { this.behavior = { status: 429, response: { error: { message: 'Rate limit exceeded', type: 'rate_limit_error' } } }; } set500() { this.behavior = { status: 500, response: { error: { message: 'Internal server error' } } }; } setInvalidJson() { this.behavior = { status: 200, response: 'not valid json {{{{{' }; } get url(): string { return `http://127.0.0.1:${this.port}`; } async start(): Promise { return new Promise((resolve, reject) => { this.server = createServer((_req: IncomingMessage, res: ServerResponse) => { const b = this.behavior; if (b.delayMs) { // Simulate timeout — never respond return; } if (typeof b.response === 'string') { res.writeHead(b.status, { 'Content-Type': 'text/plain' }); res.end(b.response); } else { res.writeHead(b.status, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(b.response)); } }); this.server.on('error', reject); this.server.listen(0, '127.0.0.1', () => { const addr = this.server!.address(); if (addr && typeof addr === 'object') { this.port = addr.port; resolve(this.port); } else { reject(new Error('Could not get port')); } }); }); } async stop(): Promise { return new Promise((resolve) => { if (this.server) { this.server.close(() => resolve()); this.server = null; } else { resolve(); } }); } }