import { describe, it, expect } from "vitest"; import { selectDiverseGenres, sampleAcross } from "./genreDiversity"; const g = (...names: string[]) => names.map(name => ({ name })); describe("sampleAcross", () => { it("returns input unchanged when at or under the count", () => { expect(sampleAcross([1, 2, 3], 5)).toEqual([1, 2, 3]); }); it("spreads the sample across the whole list rather than taking a prefix", () => { // 26 letters, want ~7: a prefix would be A–G; striding spans A→Z. const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); const picked = sampleAcross(letters, 7); expect(picked[0]).toBe("A"); // Reaches deep into the alphabet, not stuck near the front. expect(picked[picked.length - 1] >= "S").toBe(true); expect(picked.length).toBeLessThanOrEqual(7); }); it("never exceeds the requested count", () => { const items = Array.from({ length: 100 }, (_, i) => i); expect(sampleAcross(items, 8).length).toBeLessThanOrEqual(8); }); }); describe("selectDiverseGenres", () => { it("returns input unchanged when at or under the limit", () => { const input = g("Rock", "Jazz"); expect(selectDiverseGenres(input, 5)).toEqual(input); }); it("seeds with the most populous genre (first in input)", () => { const out = selectDiverseGenres(g("Rock", "Jazz", "Hip Hop"), 1); expect(out.map(x => x.name)).toEqual(["Rock"]); }); it("spreads the family instead of stacking near-synonyms", () => { // Count ranking puts all the Rock variants first; a naive top-N would // return four flavours of Rock. Diversity should pull in distinct genres. const input = g( "Rock", "Hard Rock", "Classic Rock", "Pop Rock", "Jazz", "Hip Hop", "Classical" ); const names = selectDiverseGenres(input, 4).map(x => x.name); expect(names[0]).toBe("Rock"); // seeded by count expect(names).toContain("Jazz"); expect(names).toContain("Hip Hop"); expect(names).toContain("Classical"); // Only the seed represents the Rock cluster. expect(names.filter(n => n.includes("Rock"))).toEqual(["Rock"]); }); it("preserves count order as the tie-breaker among equally-distinct genres", () => { // All four are mutually distinct (no shared tokens), so every pick after // the seed is a distance tie and should follow input (count) order. const input = g("Rock", "Jazz", "Blues", "Folk"); expect(selectDiverseGenres(input, 3).map(x => x.name)).toEqual([ "Rock", "Jazz", "Blues", ]); }); it("is case- and separator-insensitive when comparing", () => { const input = g("Hip Hop", "hip-hop", "Reggae"); // "Hip Hop" and "hip-hop" share all tokens → the second is redundant. expect(selectDiverseGenres(input, 2).map(x => x.name)).toEqual([ "Hip Hop", "Reggae", ]); }); });