feat : add stygian guide

This commit is contained in:
2026-09-29 21:23:18 +07:00 Unverified
parent 1654d3df3a
commit 69a4d34b97
27 changed files with 11196 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
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", ""],
})),
};
}
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" }],
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 },
{ 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);
});
});