fix: deduplicate distractors against each other, guard thin distractor pool

This commit is contained in:
lila 2026-04-28 15:44:29 +02:00
parent a02f3b139d
commit a02d3b3335
4 changed files with 68 additions and 370 deletions

View file

@ -32,7 +32,14 @@ const fakeTerms = [
beforeEach(() => {
vi.clearAllMocks();
mockGetGameTerms.mockResolvedValue(fakeTerms);
mockGetDistractors.mockResolvedValue(["wrong1", "wrong2", "wrong3"]);
mockGetDistractors.mockResolvedValue([
"wrong1",
"wrong2",
"wrong3",
"wrong4",
"wrong5",
"wrong6",
]);
});
describe("createGameSession", () => {
@ -151,6 +158,39 @@ describe("createGameSession", () => {
createGameSession(validRequest, store, "user-1"),
).rejects.toThrow("db timeout");
});
it("throws when fewer than 3 unique distractors remain after deduplication", async () => {
mockGetDistractors.mockResolvedValueOnce([
"cane",
"cane",
"cane",
"cane",
"cane",
"cane",
]);
await expect(
createGameSession(validRequest, store, "user-1"),
).rejects.toThrow("Not enough unique distractors");
});
it("duplicate distractors are deduplicated against each other", async () => {
mockGetDistractors.mockResolvedValueOnce([
"wrong1",
"wrong1",
"wrong1",
"wrong2",
"wrong3",
"wrong4",
]);
const session = await createGameSession(validRequest, store, "user-1");
const question = session.questions[0]!;
const optionTexts = question.options.map((o) => o.text);
expect(new Set(optionTexts).size).toBe(4);
expect(question.options).toHaveLength(4);
});
});
describe("evaluateAnswer", () => {

View file

@ -42,9 +42,16 @@ export const createGameSession = async (
6,
);
const uniqueDistractors = distractorTexts.filter(
(t) => t !== term.targetText,
);
const uniqueDistractors = [
...new Set(distractorTexts.filter((t) => t !== term.targetText)),
];
if (uniqueDistractors.length < 3) {
throw new Error(
`Not enough unique distractors for term: ${term.targetText}`,
);
}
const optionTexts = [term.targetText, ...uniqueDistractors.slice(0, 3)];
const shuffledTexts = shuffleArray(optionTexts);
const correctOptionId = shuffledTexts.indexOf(term.targetText);