import { describe, expect, it } from "vitest"; import { lunarisStygianScheduleSchema, selectedStygianBosses, stygianImageFileName, stygianImageName, stygianRecommendationsSchema, } from "@/lib/stygian/schema"; function level(monsterLevel: number) { return { monsterLevel, levelConfigs: Array.from({ length: 3 }, (_, index) => ({ id: monsterLevel * 10 + index, specialMonsterIcon: `UI_MonsterIcon_Boss_${index}`, monsterStats: { hp: 1234.56 + index }, enLevelName: `Boss ${index}`, enMonsterSpecialDesc: `Description ${index}`, enMonsterSpecialMechanicName: ["Mechanic", "", "Missing description"], enLevelMaxDescription: ["Do the thing", "Ignored", ""], enRecommendedLevelMechanic: ["{SPRITE_PRESET#11003} characters", "", " {SPRITE_PRESET#11002} characters "], tagType: ["Advantage", "None", "Disadvantage"], })), }; } const schedule = { scheduleId: 5269001, challengeName: "5.7", scheduleStartTime: "2025-06-25 10:00:00", scheduleEndTime: "2025-08-06 09:59:59", levels: [level(40), level(105), level(110)], }; describe("Stygian Lunaris parsing", () => { it("selects exactly the three level 105 and 110 bosses", () => { const parsed = lunarisStygianScheduleSchema.parse(schedule); const bosses = selectedStygianBosses(parsed); expect(bosses).toHaveLength(6); expect(bosses.map((boss) => boss.level)).toEqual([105, 105, 105, 110, 110, 110]); expect(bosses[0]).toMatchObject({ name: "Boss 0", hp: 1234.56, mechanics: [{ name: "Mechanic", description: "Do the thing" }], recommendedMechanics: ["{SPRITE_PRESET#11003} characters", "", "{SPRITE_PRESET#11002} characters"], recommendedMechanicTypes: ["Advantage", "None", "Disadvantage"], imageName: "UI_Img_LeyLineChallenge_Boss_0", }); }); it("rejects a schedule missing one of the three required bosses", () => { const parsed = lunarisStygianScheduleSchema.parse({ ...schedule, levels: [level(105), { ...level(110), levelConfigs: level(110).levelConfigs.slice(0, 2) }], }); expect(() => selectedStygianBosses(parsed)).toThrow("exactly three level 110 bosses"); }); it("preserves existing Ley Line image names", () => { expect(stygianImageName("UI_Img_LeyLineChallenge_Gastrobot01")).toBe("UI_Img_LeyLineChallenge_Gastrobot01"); expect(stygianImageName("UI_Img_LeyLineChallenge_Gastrobot01.png")).toBe("UI_Img_LeyLineChallenge_Gastrobot01"); expect(stygianImageFileName("UI_Img_LeyLineChallenge_Gastrobot01")).toBe("UI_Img_LeyLineChallenge_Gastrobot01.png"); expect(stygianImageFileName("UI_Img_LeyLineChallenge_Gastrobot01.png")).toBe("UI_Img_LeyLineChallenge_Gastrobot01.png"); }); }); describe("Stygian recommendation validation", () => { const base = { expectedVersion: 1, level: 110, bossPosition: 0, units: [] }; it("accepts teams containing one to four characters", () => { expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[ { kind: "character", catalogKey: "10000001", weaponKey: null, artifactKey: null, constellationLevel: null }, { kind: "character", catalogKey: "10000002", weaponKey: "WEAPON", artifactKey: null, constellationLevel: 2, refinement: 5 }, { kind: "character", catalogKey: "10000003", weaponKey: null, artifactKey: "ARTIFACT", constellationLevel: 0 }, { kind: "character", catalogKey: "10000004", weaponKey: null, artifactKey: null, constellationLevel: 6 }, ]], }).success).toBe(true); expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[{ kind: "character", catalogKey: "10000001", weaponKey: null, artifactKey: null, constellationLevel: null }]], }).success).toBe(true); }); it("rejects empty, non-character, oversized, or duplicate teams", () => { const character = { kind: "character", catalogKey: "10000001", weaponKey: null, artifactKey: null, constellationLevel: null }; expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[]] }).success).toBe(false); expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[character, character]] }).success).toBe(false); expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[character, ...Array.from({ length: 4 }, (_, index) => ({ kind: "weapon", catalogKey: `W${index}` }))]] }).success).toBe(false); expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [[character, { kind: "element", element: "Cryo" }, character, character]] }).success).toBe(false); }); it("allows an unrestricted number of team rows and flexible units", () => { const teams = Array.from({ length: 100 }, (_, index) => Array.from({ length: 4 }, (_, member) => ({ kind: "character", catalogKey: `C${index}-${member}`, weaponKey: null, artifactKey: null, constellationLevel: null }))); const units = Array.from({ length: 100 }, (_, index) => ({ kind: "artifact", catalogKey: `A${index}` })); expect(stygianRecommendationsSchema.safeParse({ ...base, teams, units }).success).toBe(true); }); it("rejects constellation units", () => { expect(stygianRecommendationsSchema.safeParse({ ...base, teams: [], units: [{ kind: "constellation", characterKey: "10000001", constellationLevel: 1 }], }).success).toBe(false); }); });