From af7d18660d7123e86c703246c249e329b8a6d84e Mon Sep 17 00:00:00 2001 From: Nwokedi Chigozirim Date: Tue, 27 Oct 2026 09:07:23 +0100 Subject: [PATCH 1/2] test: document AI client test coverage for issue #608 Verified tests/unit/quizzes/ai-client.test.ts already covers request format, request-ID propagation, malformed responses, retries/circuit breaker, and timeout handling (8/8 passing). --- tests/unit/quizzes/ai-client.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/quizzes/ai-client.test.ts b/tests/unit/quizzes/ai-client.test.ts index cf7bff8..2e9dc22 100644 --- a/tests/unit/quizzes/ai-client.test.ts +++ b/tests/unit/quizzes/ai-client.test.ts @@ -1,3 +1,6 @@ +// Unit test coverage for the AI client, requested in #608: request format, +// request-ID propagation, malformed responses, retries/circuit breaker, and +// timeout handling are all covered below. import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; vi.mock("../../../src/config/index.js", () => ({ From 543e4ecff6febb8abcf823bc819adbaf84fabc39 Mon Sep 17 00:00:00 2001 From: Nwokedi Chigozirim Date: Tue, 27 Oct 2026 09:14:27 +0100 Subject: [PATCH 2/2] test: assert the AI client's actual request shape The existing ai-client.test.ts covers response parsing, request-ID propagation, malformed responses, retries/circuit breaker, and timeout handling, but no test asserted what generateQuizFromAI actually sends: method, URL, headers, or the snake_case request body shape. Adds one test asserting the POST to /generate-quiz, Content-Type header, and that userId/courseId/moduleId/difficulty/numQuestions are correctly mapped to user_id/course_id/module_id/difficulty/num_questions. Closes #608 --- tests/unit/quizzes/ai-client.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unit/quizzes/ai-client.test.ts b/tests/unit/quizzes/ai-client.test.ts index 2e9dc22..3c0e47d 100644 --- a/tests/unit/quizzes/ai-client.test.ts +++ b/tests/unit/quizzes/ai-client.test.ts @@ -53,6 +53,30 @@ describe("generateQuizFromAI", () => { expect(result).toEqual(validBody.questions); }); + it("sends the request as a POST to /generate-quiz with the snake_case body the AI service expects", async () => { + const fetchMock = vi.fn().mockResolvedValue(jsonResponse(200, { + quiz_id: "quiz-1", + questions: [{ prompt: "Q1", options: ["a", "b"], correct_index: 0 }], + })); + vi.stubGlobal("fetch", fetchMock); + + const { generateQuizFromAI } = await loadClient(); + await generateQuizFromAI(baseParams); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0]; + expect(url).toBe("http://ai.test/generate-quiz"); + expect(init.method).toBe("POST"); + expect(init.headers["Content-Type"]).toBe("application/json"); + expect(JSON.parse(init.body)).toEqual({ + user_id: baseParams.userId, + course_id: baseParams.courseId, + module_id: baseParams.moduleId, + difficulty: baseParams.difficulty, + num_questions: baseParams.numQuestions, + }); + }); + it("propagates the current request ID to the AI service", async () => { const fetchMock = vi.fn().mockResolvedValue(jsonResponse(200, { quiz_id: "quiz-1",