feat(eo): align correction EO on 3.6a format + Deepgram token + T1 presentation generation
Sprint 4a:
- correctEO aligned on CorrectionRapport format (revelation, diagnostic, criteres, conseil_nclc, erreurs_codes)
- nclc_cible parameter (default 9, accepts 9|10)
- Fire-and-forget modele + exercices jobs (same pattern as EE)
- EO-specific DeepSeek prompt (oral transcript tolerance, 4 TCF criteria)
- Gemini transcribeAudio: 30s timeout + 1 retry
- POST /presentations/generate: 5-field questionnaire → DeepSeek generates oral presentation (~220-260 words, NCLC 7-8)
- Migration 006_sprint_4a_eo.sql (documentation only — no audio storage)
Sprint 4b:
- POST /transcriptions/token: Deepgram temporary API key (600s TTL)
- Removed audio storage pipeline (audioStorage.ts, XOR validation, 14MB limit)
- Backend receives transcript text only, no audio files
- TD-10/TD-11 resolved (Sprint 3.6c), TD-16/17/18 resolved (4b cleanup)
Typecheck: OK · Tests: 241/241 ✅
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f5954e6d72
commit
7cac057062
18 changed files with 2907 additions and 911 deletions
161
src/routes/__tests__/correctionsEO.test.ts
Normal file
161
src/routes/__tests__/correctionsEO.test.ts
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { Hono } from "hono";
|
||||
|
||||
// ─── Mocks ───────────────────────────────────────────────────────────────
|
||||
|
||||
vi.mock("../../middleware/auth", () => ({
|
||||
authMiddleware: async (c: any, next: any) => {
|
||||
const authHeader = c.req.header("Authorization");
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return c.json({ error: true, code: "AUTH_REQUIRED" }, 401);
|
||||
}
|
||||
c.set("profile", {
|
||||
id: "user-1",
|
||||
email: "u@test.com",
|
||||
plan: "standard",
|
||||
simulations_used: 3,
|
||||
});
|
||||
await next();
|
||||
},
|
||||
}));
|
||||
|
||||
const { correctEOMock } = vi.hoisted(() => ({ correctEOMock: vi.fn() }));
|
||||
vi.mock("../../controllers/correctionController", () => ({
|
||||
correctEE: vi.fn(),
|
||||
correctEO: correctEOMock,
|
||||
}));
|
||||
|
||||
import correctionsRoutes from "../corrections";
|
||||
|
||||
function buildApp() {
|
||||
const app = new Hono();
|
||||
app.route("/corrections", correctionsRoutes);
|
||||
return app;
|
||||
}
|
||||
|
||||
const AUTH = { Authorization: "Bearer x" };
|
||||
const JSON_HEADERS = { ...AUTH, "Content-Type": "application/json" };
|
||||
|
||||
describe("POST /corrections/eo — Sprint 4a", () => {
|
||||
beforeEach(() => {
|
||||
correctEOMock.mockReset();
|
||||
});
|
||||
|
||||
it("401 sans Authorization", async () => {
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", { method: "POST" });
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
|
||||
it("400 si simulationId manquant", async () => {
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ tache: "EO_T1", transcript: "t" }),
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.code).toBe("VALIDATION_ERROR");
|
||||
});
|
||||
|
||||
it("400 si tache invalide (EO_T2 par exemple)", async () => {
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({
|
||||
simulationId: "s1",
|
||||
tache: "EO_T2",
|
||||
transcript: "t",
|
||||
}),
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it("400 si transcript manquant", async () => {
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ simulationId: "s1", tache: "EO_T1" }),
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.code).toBe("VALIDATION_ERROR");
|
||||
});
|
||||
|
||||
it("400 si nclc_cible invalide", async () => {
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({
|
||||
simulationId: "s1",
|
||||
tache: "EO_T1",
|
||||
transcript: "t",
|
||||
nclc_cible: 8,
|
||||
}),
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it("200 quand le controller renvoie un rapport (mode transcript)", async () => {
|
||||
correctEOMock.mockResolvedValue({
|
||||
data: {
|
||||
score: 14,
|
||||
nclc: 9,
|
||||
simulation_id: "s1",
|
||||
diagnostic: "d",
|
||||
},
|
||||
});
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({
|
||||
simulationId: "s1",
|
||||
tache: "EO_T1",
|
||||
transcript: "Bonjour je m appelle Pierre",
|
||||
}),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.score).toBe(14);
|
||||
expect(correctEOMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
simulationId: "s1",
|
||||
tache: "EO_T1",
|
||||
nclcCible: 9,
|
||||
transcript: "Bonjour je m appelle Pierre",
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("200 avec nclc_cible=10 transmis au controller", async () => {
|
||||
correctEOMock.mockResolvedValue({
|
||||
data: { score: 16, nclc: 10, simulation_id: "s2", diagnostic: "d" },
|
||||
});
|
||||
const app = buildApp();
|
||||
const res = await app.request("/corrections/eo", {
|
||||
method: "POST",
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({
|
||||
simulationId: "s2",
|
||||
tache: "EO_T1",
|
||||
transcript: "Bonjour",
|
||||
nclc_cible: 10,
|
||||
}),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(correctEOMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
simulationId: "s2",
|
||||
nclcCible: 10,
|
||||
transcript: "Bonjour",
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue