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); }); });