import { type ConditionId, combatantId, createEncounter, isDomainError, playerCharacterId, } from "@initiative/domain"; import { describe, expect, it } from "vitest"; import { addCombatantUseCase } from "../add-combatant-use-case.js"; import { adjustHpUseCase } from "../adjust-hp-use-case.js"; import { advanceTurnUseCase } from "../advance-turn-use-case.js"; import { clearEncounterUseCase } from "../clear-encounter-use-case.js"; import { createPlayerCharacterUseCase } from "../create-player-character-use-case.js"; import { deletePlayerCharacterUseCase } from "../delete-player-character-use-case.js"; import { editCombatantUseCase } from "../edit-combatant-use-case.js"; import { editPlayerCharacterUseCase } from "../edit-player-character-use-case.js"; import { removeCombatantUseCase } from "../remove-combatant-use-case.js"; import { retreatTurnUseCase } from "../retreat-turn-use-case.js"; import { setAcUseCase } from "../set-ac-use-case.js"; import { setHpUseCase } from "../set-hp-use-case.js"; import { setInitiativeUseCase } from "../set-initiative-use-case.js"; import { toggleConcentrationUseCase } from "../toggle-concentration-use-case.js"; import { toggleConditionUseCase } from "../toggle-condition-use-case.js"; import { requireSaved, stubEncounterStore, stubPlayerCharacterStore, } from "./helpers.js"; const ID_A = combatantId("a"); function emptyEncounter() { const result = createEncounter([]); if (isDomainError(result)) throw new Error("Test setup failed"); return result; } function encounterWith(...names: string[]) { let enc = emptyEncounter(); for (const name of names) { const id = combatantId(name); const store = stubEncounterStore(enc); const result = addCombatantUseCase(store, id, name); if (isDomainError(result)) throw new Error(`Setup failed: ${name}`); enc = requireSaved(store.saved); } return enc; } function encounterWithHp(name: string, maxHp: number) { const enc = encounterWith(name); const store = stubEncounterStore(enc); const id = combatantId(name); setHpUseCase(store, id, maxHp); return requireSaved(store.saved); } function createPc(name: string) { const store = stubPlayerCharacterStore([]); const id = playerCharacterId("pc-1"); createPlayerCharacterUseCase(store, id, name, 15, 40, undefined, undefined); return { id, characters: requireSaved(store.saved) }; } describe("addCombatantUseCase", () => { it("adds a combatant and saves", () => { const store = stubEncounterStore(emptyEncounter()); const result = addCombatantUseCase(store, ID_A, "Goblin"); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.combatants).toHaveLength(1); expect(saved.combatants[0].name).toBe("Goblin"); }); it("returns domain error for empty name", () => { const store = stubEncounterStore(emptyEncounter()); const result = addCombatantUseCase(store, ID_A, ""); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("adjustHpUseCase", () => { it("adjusts HP and saves", () => { const enc = encounterWithHp("Goblin", 10); const store = stubEncounterStore(enc); const result = adjustHpUseCase(store, combatantId("Goblin"), -3); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.combatants[0].currentHp).toBe(7); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = adjustHpUseCase(store, ID_A, -5); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("advanceTurnUseCase", () => { it("advances turn and saves", () => { const enc = encounterWith("A", "B"); const store = stubEncounterStore(enc); const result = advanceTurnUseCase(store); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.activeIndex).toBe(1); }); it("returns domain error on empty encounter", () => { const store = stubEncounterStore(emptyEncounter()); const result = advanceTurnUseCase(store); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("clearEncounterUseCase", () => { it("clears encounter and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = clearEncounterUseCase(store); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.combatants).toHaveLength(0); }); }); describe("editCombatantUseCase", () => { it("edits combatant name and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = editCombatantUseCase( store, combatantId("Goblin"), "Hobgoblin", ); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.combatants[0].name).toBe("Hobgoblin"); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = editCombatantUseCase(store, ID_A, "X"); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("removeCombatantUseCase", () => { it("removes combatant and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = removeCombatantUseCase(store, combatantId("Goblin")); expect(isDomainError(result)).toBe(false); const saved = requireSaved(store.saved); expect(saved.combatants).toHaveLength(0); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = removeCombatantUseCase(store, ID_A); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("retreatTurnUseCase", () => { it("retreats turn and saves", () => { const enc = encounterWith("A", "B"); const store1 = stubEncounterStore(enc); advanceTurnUseCase(store1); const store = stubEncounterStore(requireSaved(store1.saved)); const result = retreatTurnUseCase(store); expect(isDomainError(result)).toBe(false); expect(store.saved).not.toBeNull(); }); it("returns domain error on empty encounter", () => { const store = stubEncounterStore(emptyEncounter()); const result = retreatTurnUseCase(store); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("setAcUseCase", () => { it("sets AC and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = setAcUseCase(store, combatantId("Goblin"), 15); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved).combatants[0].ac).toBe(15); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = setAcUseCase(store, ID_A, 15); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("setHpUseCase", () => { it("sets max HP and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = setHpUseCase(store, combatantId("Goblin"), 20); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved).combatants[0].maxHp).toBe(20); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = setHpUseCase(store, ID_A, 20); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("setInitiativeUseCase", () => { it("sets initiative and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = setInitiativeUseCase(store, combatantId("Goblin"), 15); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved).combatants[0].initiative).toBe(15); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = setInitiativeUseCase(store, ID_A, 15); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("toggleConcentrationUseCase", () => { it("toggles concentration and saves", () => { const enc = encounterWith("Wizard"); const store = stubEncounterStore(enc); const result = toggleConcentrationUseCase(store, combatantId("Wizard")); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved).combatants[0].isConcentrating).toBe(true); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = toggleConcentrationUseCase(store, ID_A); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("toggleConditionUseCase", () => { it("toggles condition and saves", () => { const enc = encounterWith("Goblin"); const store = stubEncounterStore(enc); const result = toggleConditionUseCase( store, combatantId("Goblin"), "blinded" as ConditionId, ); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved).combatants[0].conditions).toContain( "blinded", ); }); it("returns domain error for unknown combatant", () => { const store = stubEncounterStore(emptyEncounter()); const result = toggleConditionUseCase( store, ID_A, "blinded" as ConditionId, ); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("createPlayerCharacterUseCase", () => { it("creates a player character and saves", () => { const store = stubPlayerCharacterStore([]); const id = playerCharacterId("pc-1"); const result = createPlayerCharacterUseCase( store, id, "Gandalf", 15, 40, undefined, undefined, ); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved)).toHaveLength(1); }); it("returns domain error for invalid input", () => { const store = stubPlayerCharacterStore([]); const id = playerCharacterId("pc-1"); const result = createPlayerCharacterUseCase( store, id, "", 15, 40, undefined, undefined, ); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("deletePlayerCharacterUseCase", () => { it("deletes a player character and saves", () => { const { id, characters } = createPc("Gandalf"); const store = stubPlayerCharacterStore(characters); const result = deletePlayerCharacterUseCase(store, id); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved)).toHaveLength(0); }); it("returns domain error for unknown character", () => { const store = stubPlayerCharacterStore([]); const result = deletePlayerCharacterUseCase( store, playerCharacterId("unknown"), ); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); }); describe("editPlayerCharacterUseCase", () => { it("edits a player character and saves", () => { const { id, characters } = createPc("Gandalf"); const store = stubPlayerCharacterStore(characters); const result = editPlayerCharacterUseCase(store, id, { name: "Gandalf the White", }); expect(isDomainError(result)).toBe(false); expect(requireSaved(store.saved)[0].name).toBe("Gandalf the White"); }); it("returns domain error for unknown character", () => { const store = stubPlayerCharacterStore([]); const result = editPlayerCharacterUseCase( store, playerCharacterId("unknown"), { name: "X" }, ); expect(isDomainError(result)).toBe(true); expect(store.saved).toBeNull(); }); });