import { describe, expect, it } from "vitest"; import { rankAnalyticsItems, selectAnalytics, type AnalyticsItem, type PublicAnalytics } from "./model"; const item = (key: string, guideCount: number, stygianCount: number, name = key): AnalyticsItem => ({ key, name, imageUrl: null, rarity: null, guideCount, stygianCount, contributions: [{ guideId: "g0", teamSignature: "guide-team", count: guideCount }, { guideId: "s0", teamSignature: "stygian-team", count: stygianCount }], }); describe("analytics source selection", () => { it("merges sources, changes order, and removes Stygian-only items when excluded", () => { const items = [item("shared", 2, 5), item("guide", 4, 0), item("stygian", 0, 3)]; expect(rankAnalyticsItems(items, true).map(({ key, count }) => [key, count])).toEqual([ ["shared", 7], ["guide", 4], ["stygian", 3], ]); expect(rankAnalyticsItems(items, false).map(({ key, count }) => [key, count])).toEqual([ ["guide", 4], ["shared", 2], ]); expect(items[0].guideCount).toBe(2); }); it("breaks ties by name and then key", () => { expect(rankAnalyticsItems([item("z", 1, 0, "Alpha"), item("b", 1, 0, "Beta"), item("a", 1, 0, "Alpha")], true) .map(({ key }) => key)).toEqual(["a", "z", "b"]); }); it("updates every ranking and summary and handles empty sources", () => { const entries = [item("shared", 2, 1), item("stygian", 0, 1)]; const data: PublicAnalytics = { guides: [ ...Array.from({ length: 5 }, (_, index) => ({ id: `g${index}`, source: "guide" as const, name: `Guide ${index}`, character: null })), ...Array.from({ length: 3 }, (_, index) => ({ id: `s${index}`, source: "stygian" as const, name: `Stygian ${index}`, character: null })), ], teamGroups: [{ guideId: "g0", signature: "guide-team", count: 6 }, { guideId: "s0", signature: "stygian-team", count: 9 }], guideCount: 5, stygianScheduleCount: 3, guideTeamCount: 6, stygianTeamCount: 9, team: { character: entries, weapon: entries, artifact: entries }, recommendation: { weapon: entries, artifact: entries }, }; const included = selectAnalytics(data, true); const excluded = selectAnalytics(data, false); expect(included.teamCount).toBe(15); expect(excluded.teamCount).toBe(6); expect(excluded.stygianScheduleCount).toBe(0); expect(excluded.guideCount).toBe(5); for (const list of [...Object.values(excluded.team), ...Object.values(excluded.recommendation)]) { expect(list.map(({ key, count }) => [key, count])).toEqual([["shared", 2]]); } expect(rankAnalyticsItems([], true)).toEqual([]); expect(rankAnalyticsItems([item("unused", 0, 0)], false)).toEqual([]); }); it("excludes guides before deduplicating and preserves per-member equipment counts", () => { const entry: AnalyticsItem = { ...item("weapon", 7, 2), contributions: [ { guideId: "g1", teamSignature: "shared", count: 4 }, { guideId: "g2", teamSignature: "shared", count: 2 }, { guideId: "g2", teamSignature: "different-build", count: 1 }, { guideId: "s1", teamSignature: "shared", count: 2 }, ], }; const recommendation = { ...entry, contributions: [{ guideId: "g1", teamSignature: null, count: 3 }, { guideId: "g2", teamSignature: null, count: 4 }] }; const data: PublicAnalytics = { guides: [{ id: "g1", source: "guide", name: "One", character: null }, { id: "g2", source: "guide", name: "Two", character: null }, { id: "s1", source: "stygian", name: "Stygian", character: null }], teamGroups: [{ guideId: "g1", signature: "shared", count: 2 }, { guideId: "g2", signature: "shared", count: 1 }, { guideId: "g2", signature: "different-build", count: 1 }, { guideId: "s1", signature: "shared", count: 1 }], guideCount: 2, stygianScheduleCount: 1, guideTeamCount: 4, stygianTeamCount: 1, team: { character: [], weapon: [entry], artifact: [] }, recommendation: { weapon: [recommendation], artifact: [] }, }; expect(selectAnalytics(data, true).team.weapon[0].count).toBe(9); const unique = selectAnalytics(data, true, new Set(), true); expect(unique.teamCount).toBe(2); expect(unique.team.weapon[0].count).toBe(3); expect(unique.recommendation.weapon[0].count).toBe(7); const excluded = selectAnalytics(data, true, new Set(["g1"]), true); expect(excluded.guideCount).toBe(1); expect(excluded.team.weapon[0].count).toBe(3); expect(excluded.recommendation.weapon[0].count).toBe(4); expect(selectAnalytics(data, true, new Set(["g1", "g2"]), true).team.weapon[0].count).toBe(2); const empty = selectAnalytics(data, false, new Set(["g1", "g2"]), true); expect(empty.teamCount).toBe(0); expect(empty.team.weapon).toEqual([]); }); });