import { describe, expect, it } from "vitest"; import type { ConditionId } from "../conditions.js"; import { CONDITION_DEFINITIONS } from "../conditions.js"; import { toggleCondition } from "../toggle-condition.js"; import type { Combatant, Encounter } from "../types.js"; import { combatantId, isDomainError } from "../types.js"; import { expectDomainError } from "./test-helpers.js"; function makeCombatant( name: string, conditions?: readonly ConditionId[], ): Combatant { return conditions ? { id: combatantId(name), name, conditions } : { id: combatantId(name), name }; } function enc(combatants: Combatant[]): Encounter { return { combatants, activeIndex: 0, roundNumber: 1 }; } function success(encounter: Encounter, id: string, condition: ConditionId) { const result = toggleCondition(encounter, combatantId(id), condition); if (isDomainError(result)) { throw new Error(`Expected success, got error: ${result.message}`); } return result; } describe("toggleCondition", () => { it("adds a condition when not present", () => { const e = enc([makeCombatant("A")]); const { encounter, events } = success(e, "A", "blinded"); expect(encounter.combatants[0].conditions).toEqual(["blinded"]); expect(events).toEqual([ { type: "ConditionAdded", combatantId: combatantId("A"), condition: "blinded", }, ]); }); it("removes a condition when already present", () => { const e = enc([makeCombatant("A", ["blinded"])]); const { encounter, events } = success(e, "A", "blinded"); expect(encounter.combatants[0].conditions).toBeUndefined(); expect(events).toEqual([ { type: "ConditionRemoved", combatantId: combatantId("A"), condition: "blinded", }, ]); }); it("maintains definition order when adding conditions", () => { const e = enc([makeCombatant("A", ["poisoned"])]); const { encounter } = success(e, "A", "blinded"); expect(encounter.combatants[0].conditions).toEqual(["blinded", "poisoned"]); }); it("prevents duplicate conditions", () => { const e = enc([makeCombatant("A", ["blinded"])]); // Toggling blinded again removes it, not duplicates const { encounter } = success(e, "A", "blinded"); expect(encounter.combatants[0].conditions).toBeUndefined(); }); it("rejects unknown condition", () => { const e = enc([makeCombatant("A")]); const result = toggleCondition( e, combatantId("A"), "flying" as ConditionId, ); expectDomainError(result, "unknown-condition"); }); it("returns error for nonexistent combatant", () => { const e = enc([makeCombatant("A")]); const result = toggleCondition(e, combatantId("missing"), "blinded"); expectDomainError(result, "combatant-not-found"); }); it("does not mutate input encounter", () => { const e = enc([makeCombatant("A")]); const original = JSON.parse(JSON.stringify(e)); toggleCondition(e, combatantId("A"), "blinded"); expect(e).toEqual(original); }); it("normalizes empty array to undefined on removal", () => { const e = enc([makeCombatant("A", ["charmed"])]); const { encounter } = success(e, "A", "charmed"); expect(encounter.combatants[0].conditions).toBeUndefined(); }); it("preserves order across all conditions", () => { const order = CONDITION_DEFINITIONS.map((d) => d.id); // Add in reverse order let e = enc([makeCombatant("A")]); for (const cond of [...order].reverse()) { const result = success(e, "A", cond); e = result.encounter; } expect(e.combatants[0].conditions).toEqual(order); }); });