This commit is contained in:
Daniel Bulant 2026-05-25 18:10:22 +02:00
parent 7609feead0
commit ff733b9774
No known key found for this signature in database
2 changed files with 59 additions and 4 deletions

View file

@ -1,5 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import type { Question, QuizRound } from "../../party-types";
import { buildAudioMetadataQuestion } from "../audio-question-generator";
import { buildNumericQuestion } from "../numeric-question-generator";
import {
buildMemberOptions,
@ -266,6 +267,50 @@ describe("question generation", () => {
expect(question).toBeNull();
});
it("builds audio questions with fewer than four real options", async () => {
const randomSpy = vi.spyOn(Math, "random").mockReturnValue(0.99);
const db = createFakeDb(null);
const analytics = {
storyClusters: [
{
tracks: [
{
name: "Shared Track One",
artists: [{ name: "Shared Artist One" }],
albumName: "Shared Album One",
},
{
name: "Shared Track Two",
artists: [{ name: "Shared Artist Two" }],
albumName: "Shared Album Two",
},
],
artists: [
{ name: "Shared Artist One" },
{ name: "Shared Artist Two" },
],
genres: [],
},
],
groupSummary: {
mostSharedGenres: [],
},
} as PartyAnalytics;
try {
const question = await buildAudioMetadataQuestion(db, analytics, 0, []);
expect(question).not.toBeNull();
expect(question?.type).toBe("choice");
if (question?.type === "choice") {
expect(question.options).toHaveLength(2);
expect(question.text).toContain("Shared Track Two");
}
} finally {
randomSpy.mockRestore();
}
});
it("selects a fresh party song when the current one was already used", async () => {
const db = createSongFallbackDb([
makeSong("track-1", "spotify:track:one", "One"),

View file

@ -135,7 +135,7 @@ export function getMostSharedGenreNames(analytics: PartyAnalytics): string[] {
export function pickQuestionCandidate<T extends QuestionLike>(
candidates: QuestionCandidate<T>[],
history: QuizRound[],
index: number,
_index: number,
): T | null {
const seenKeys = new Set<string>();
const seenSubjects = new Set<string>();
@ -165,7 +165,7 @@ export function pickQuestionCandidate<T extends QuestionLike>(
if (fresh.length === 0) return null;
const pool = fresh;
const candidate = pool[index % pool.length];
const candidate = pickRandom(pool);
if (!candidate) return null;
return {
...candidate.question,
@ -510,7 +510,8 @@ export function buildOrderedOptions(
const options = uniqueStrings(
values.filter((value): value is string => isUsableText(value)),
);
return options.length >= desiredCount ? options.slice(0, desiredCount) : null;
const optionCount = getAvailableOptionCount(options.length, desiredCount);
return optionCount ? options.slice(0, optionCount) : null;
}
export function buildOptionsWithCorrect(
@ -523,7 +524,8 @@ export function buildOptionsWithCorrect(
correct,
...candidates.filter((c) => isUsableText(c) && c !== correct),
]);
return options.length >= desiredCount ? options.slice(0, desiredCount) : null;
const optionCount = getAvailableOptionCount(options.length, desiredCount);
return optionCount ? options.slice(0, optionCount) : null;
}
export function pickRandom<T>(items: T[]): T | null {
@ -532,6 +534,14 @@ export function pickRandom<T>(items: T[]): T | null {
return items[index] ?? null;
}
function getAvailableOptionCount(
availableCount: number,
desiredCount: number,
): number | null {
if (availableCount < 2) return null;
return Math.min(availableCount, desiredCount);
}
export function getCurrentLeader(
quizState: { scores: Record<string, number> },
members: PartyQuestionMember[],