feat(eo): complete EO simulation flow (T1 + T3) with Gemini transcription
- Gemini batch transcription (no Deepgram live)
- blobToBase64 helper (shared/lib/audio.ts)
- AudioRecorder: remove onChunk, add maxSeconds/onMaxReached auto-submit
- Timer stops at maxSeconds and triggers auto-submission
- EnregistrementEOPage: audioBase64 to backend, fix race condition step=done
- SimulationFlowProvider: submitEoAudio(audioBase64, mimeType, nclcCible)
- MIME normalization (strip codec params)
- Split CORRECTION_EE_TIMEOUT_MS (60s) / CORRECTION_EO_TIMEOUT_MS (120s)
- PresentationGenereeT1Page: localStorage persistence
Typecheck: OK · Tests: 159/159 ✅
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
71c1ad3018
commit
d1c8b548bb
34 changed files with 3255 additions and 70 deletions
49
src/entities/transcription/__tests__/api.test.ts
Normal file
49
src/entities/transcription/__tests__/api.test.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Tests du domaine `transcription` — Sprint 4c-1.
|
||||
*
|
||||
* Valide :
|
||||
* - succès : retourne le token et expires_in
|
||||
* - erreur : ApiError propagée par apiFetch
|
||||
*/
|
||||
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||||
|
||||
vi.mock('@/shared/lib/api-client', () => ({
|
||||
apiFetch: vi.fn(),
|
||||
}))
|
||||
|
||||
import { apiFetch } from '@/shared/lib/api-client'
|
||||
import { requestDeepgramToken } from '../api'
|
||||
|
||||
const mocked = vi.mocked(apiFetch)
|
||||
|
||||
describe('requestDeepgramToken', () => {
|
||||
beforeEach(() => {
|
||||
mocked.mockReset()
|
||||
})
|
||||
|
||||
it('retourne le token et expires_in en cas de succès', async () => {
|
||||
mocked.mockResolvedValueOnce({ token: 'dg-temp-abc', expires_in: 600 })
|
||||
|
||||
const result = await requestDeepgramToken()
|
||||
|
||||
expect(result).toEqual({ token: 'dg-temp-abc', expires_in: 600 })
|
||||
expect(mocked).toHaveBeenCalledWith('/transcriptions/token', {
|
||||
method: 'POST',
|
||||
timeoutMs: 10_000,
|
||||
retry: { max: 0, baseDelayMs: 0 },
|
||||
})
|
||||
})
|
||||
|
||||
it('propage les ApiError du backend', async () => {
|
||||
mocked.mockRejectedValueOnce({
|
||||
error: true,
|
||||
code: 'AUTH_REQUIRED',
|
||||
message: 'Auth required',
|
||||
})
|
||||
|
||||
await expect(requestDeepgramToken()).rejects.toMatchObject({
|
||||
code: 'AUTH_REQUIRED',
|
||||
})
|
||||
})
|
||||
})
|
||||
21
src/entities/transcription/api.ts
Normal file
21
src/entities/transcription/api.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Appels API du domaine `transcription`.
|
||||
*
|
||||
* `POST /transcriptions/token` : timeout 10 s, retry désactivé.
|
||||
* Le retry est désactivé volontairement : un POST non-idempotent qui
|
||||
* consomme un crédit Deepgram à chaque appel ne doit pas être rejoué
|
||||
* silencieusement en cas d'erreur réseau transitoire.
|
||||
*/
|
||||
|
||||
import { apiFetch } from '@/shared/lib/api-client'
|
||||
import type { TranscriptionToken } from './types'
|
||||
|
||||
const TOKEN_TIMEOUT_MS = 10_000
|
||||
|
||||
export function requestDeepgramToken(): Promise<TranscriptionToken> {
|
||||
return apiFetch<TranscriptionToken>('/transcriptions/token', {
|
||||
method: 'POST',
|
||||
timeoutMs: TOKEN_TIMEOUT_MS,
|
||||
retry: { max: 0, baseDelayMs: 0 },
|
||||
})
|
||||
}
|
||||
15
src/entities/transcription/types.ts
Normal file
15
src/entities/transcription/types.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Types publics du domaine `transcription`.
|
||||
*
|
||||
* Le frontend obtient un token Deepgram éphémère via le backend
|
||||
* (`POST /transcriptions/token`) puis ouvre une connexion WebSocket
|
||||
* directe vers Deepgram pour la transcription live. La clé maître
|
||||
* Deepgram reste côté backend (cf. SECURITY.md).
|
||||
*/
|
||||
|
||||
export interface TranscriptionToken {
|
||||
/** JWT éphémère Deepgram (durée de vie ~10 min). */
|
||||
token: string
|
||||
/** Durée de validité du token, en secondes. */
|
||||
expires_in: number
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue