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

@ -42,4 +42,21 @@ describe("InMemoryGameSessionStore", () => {
const result = await store.get("session-1");
expect(result).not.toBeNull();
});
it("update persists modified session data", async () => {
const data = {
answers: new Map([
["q1", 2],
["q2", 1],
]),
userId: "user-1",
};
await store.create("session-1", data, 60_000);
const updated = { answers: new Map([["q2", 1]]), userId: "user-1" };
await store.update("session-1", updated);
const result = await store.get("session-1");
expect(result).toEqual(updated);
});
});

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