97 lines
3.9 KiB
TypeScript
97 lines
3.9 KiB
TypeScript
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();
|
|
});
|
|
});
|