Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QKHeGuAYh4W1jNdjMConZa
40 lines
1.8 KiB
TypeScript
40 lines
1.8 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { DEFAULT_OPENAI, OpenAiTranscriber } from '../../src/core/openai';
|
|
import type { HttpClient } from '../../src/core/ports';
|
|
|
|
// Ключ — только из окружения, никогда в репозитории.
|
|
const KEY = process.env.AITUNNEL_API_KEY ?? '';
|
|
|
|
// Под Node нет requestUrl: тот же порт на fetch. contentType уходит заголовком.
|
|
const fetchHttp: HttpClient = {
|
|
async post(req) {
|
|
const res = await fetch(req.url, {
|
|
method: 'POST',
|
|
headers: { ...req.headers, 'Content-Type': req.contentType },
|
|
body: req.body,
|
|
});
|
|
return { status: res.status, text: await res.text() };
|
|
},
|
|
};
|
|
|
|
// Характерные слова эталона tests/fixtures/test-01.txt.
|
|
const WORDS = ['тестовая', 'запись', 'проверки', 'распознавания', 'речи', 'whisper'];
|
|
|
|
describe.skipIf(!KEY)('AITUNNEL gpt-4o-transcribe (live)', () => {
|
|
it('transcribes test-01.m4a', { timeout: 120_000 }, async () => {
|
|
const audio = new Uint8Array(readFileSync(new URL('../fixtures/test-01.m4a', import.meta.url))).buffer;
|
|
const reference = readFileSync(new URL('../fixtures/test-01.txt', import.meta.url), 'utf8').toLowerCase();
|
|
for (const word of WORDS) expect(reference).toContain(word);
|
|
|
|
const t = new OpenAiTranscriber(fetchHttp, () => ({ ...DEFAULT_OPENAI, apiKey: KEY }), Math.random);
|
|
const result = await t.transcribe(audio, 'audio/mp4', new AbortController().signal);
|
|
|
|
expect(result).toMatchObject({ ok: true });
|
|
const text = result.ok ? result.text.toLowerCase() : '';
|
|
expect(text.trim()).not.toBe('');
|
|
const hits = WORDS.filter((w) => text.includes(w));
|
|
expect(hits.length, `распознано: ${text}`).toBeGreaterThanOrEqual(4);
|
|
});
|
|
});
|