import { describe, expect, it } from 'vitest'; import { beginAiRequestInspection, clearAiRequestInspections, completeAiRequestInspection, isInspectableAiRequest, subscribeAiRequestInspections, type AiRequestInspectionRecord } from '../../../shared/frontend/ai-request-inspector/ai-request-inspector'; describe('AI request inspector route matching', () => { it.each([ ['/provider-lab/runs', 'POST'], ['/projects/p1/reviews/text', 'POST'], ['/assets/a1/review', 'POST'], ['/admin/novel-creation-wizard/proposals/jobs', 'POST'], ['/projects/p1/novel-generation/plans/n1/ip-bible/jobs', 'POST'], ['/projects/p1/novel-generation/plans/n1/chapters/2/run/jobs', 'POST'], ['/projects/p1/novel-generation/plans/n1/chapters/batch/jobs', 'POST'], ['/admin/hit-analyses', 'POST'], ['/admin/hit-analyses/h1/analyze', 'POST'], ['/admin/hit-analyses/h1/patterns', 'POST'], ['/admin/providers/provider-1/test', 'POST'], ['/projects/p1/story-bible/generate', 'POST'], ['/projects/p1/characters/extract', 'POST'], ['/characters/c1/anchor-video-test', 'POST'], ['/characters/c1/generate-images', 'POST'], ['/characters/c1/images/i1/review-visual', 'POST'], ['/projects/p1/episodes/plan-request-preview', 'POST'], ['/episodes/e1/script/request-preview', 'POST'], ['/episodes/e1/script/generate', 'POST'], ['/episodes/e1/audio/segments/2/retry', 'POST'], ['/episodes/e1/live-action/shots/prepare', 'POST'], ['/episodes/e1/live-action/video-clips/preflight?provider_code=kling', 'GET'], ['/episodes/e1/live-action/video-clips/cost-estimate?provider_code=kling', 'GET'], ['/live-action/video-clips/v1/quality-check', 'POST'], ['/episodes/e1/live-action/render', 'POST'] ])('captures %s %s', (path, method) => { expect(isInspectableAiRequest(path, method)).toBe(true); }); it.each([ ['/projects/p1', 'PATCH'], ['/projects/p1/novel-generation/plans', 'POST'], ['/projects/p1/novel-generation/plans/n1', 'PATCH'], ['/projects/p1/story-bible/confirm', 'POST'], ['/characters/c1/set-anchor', 'POST'], ['/episodes/e1/live-action/shots/s1/prompt', 'POST'], ['/live-action/video-clips/v1/manual-review', 'POST'], ['/live-action/video-clips/v1/select-candidate', 'POST'], ['/assets/a1', 'GET'] ])('ignores non-AI mutation %s %s', (path, method) => { expect(isInspectableAiRequest(path, method)).toBe(false); }); it('records path, query and body while redacting secrets', () => { clearAiRequestInspections(); let latest: AiRequestInspectionRecord[] = []; const unsubscribe = subscribeAiRequestInspections((records) => { latest = records; }); beginAiRequestInspection({ path: '/episodes/e1/live-action/video-clips/preflight?provider_code=kling&image_id=1&image_id=2', method: 'GET', body: { prompt: '测试提示词', api_key: 'must-not-leak' } }); expect(latest[0]?.request_body).toEqual({ path_params: '/episodes/e1/live-action/video-clips/preflight', query_params: { provider_code: 'kling', image_id: ['1', '2'] }, body: { prompt: '测试提示词', api_key: '[已隐藏敏感字段]' } }); unsubscribe(); clearAiRequestInspections(); }); it('keeps the complete response from request previews as execution evidence', () => { clearAiRequestInspections(); let latest: AiRequestInspectionRecord[] = []; const unsubscribe = subscribeAiRequestInspections((records) => { latest = records; }); const id = beginAiRequestInspection({ path: '/episodes/e1/script/request-preview', method: 'POST', body: { provider_code: 'text-provider' } }); completeAiRequestInspection(id, { requestId: 'req-1', responseData: { compiled_prompt: '完整剧本提示词', temperature: 0.4 } }); expect(latest[0]?.execution_evidence).toEqual({ compiled_prompt: '完整剧本提示词', temperature: 0.4 }); unsubscribe(); clearAiRequestInspections(); }); });