import { describe, expect, it } from "vitest"; import { createPlayerCharacter } from "../create-player-character.js"; import type { PlayerCharacter } from "../player-character-types.js"; import { playerCharacterId } from "../player-character-types.js"; import { isDomainError } from "../types.js"; import { expectDomainError } from "./test-helpers.js"; const id = playerCharacterId("pc-1"); function success( characters: readonly PlayerCharacter[], name: string, ac: number, maxHp: number, color = "blue", icon = "sword", ) { const result = createPlayerCharacter( characters, id, name, ac, maxHp, color, icon, ); if (isDomainError(result)) { throw new Error(`Expected success, got error: ${result.message}`); } return result; } describe("createPlayerCharacter", () => { it("creates a valid player character", () => { const { characters, events } = success( [], "Aragorn", 16, 120, "green", "shield", ); expect(characters).toHaveLength(1); expect(characters[0]).toEqual({ id, name: "Aragorn", ac: 16, maxHp: 120, color: "green", icon: "shield", }); expect(events).toEqual([ { type: "PlayerCharacterCreated", playerCharacterId: id, name: "Aragorn", }, ]); }); it("trims whitespace from name", () => { const { characters } = success([], " Gandalf ", 12, 80); expect(characters[0].name).toBe("Gandalf"); }); it("appends to existing characters", () => { const existing: PlayerCharacter = { id: playerCharacterId("pc-0"), name: "Legolas", ac: 14, maxHp: 90, color: "green", icon: "eye", }; const { characters } = success([existing], "Gimli", 18, 100, "red", "axe"); expect(characters).toHaveLength(2); expect(characters[0]).toEqual(existing); expect(characters[1].name).toBe("Gimli"); }); it("rejects empty name", () => { const result = createPlayerCharacter([], id, "", 10, 50, "blue", "sword"); expectDomainError(result, "invalid-name"); }); it("rejects whitespace-only name", () => { const result = createPlayerCharacter( [], id, " ", 10, 50, "blue", "sword", ); expectDomainError(result, "invalid-name"); }); it("rejects negative AC", () => { const result = createPlayerCharacter( [], id, "Test", -1, 50, "blue", "sword", ); expectDomainError(result, "invalid-ac"); }); it("rejects non-integer AC", () => { const result = createPlayerCharacter( [], id, "Test", 10.5, 50, "blue", "sword", ); expectDomainError(result, "invalid-ac"); }); it("allows AC of 0", () => { const { characters } = success([], "Test", 0, 50); expect(characters[0].ac).toBe(0); }); it("rejects maxHp of 0", () => { const result = createPlayerCharacter( [], id, "Test", 10, 0, "blue", "sword", ); expectDomainError(result, "invalid-max-hp"); }); it("rejects negative maxHp", () => { const result = createPlayerCharacter( [], id, "Test", 10, -5, "blue", "sword", ); expectDomainError(result, "invalid-max-hp"); }); it("rejects non-integer maxHp", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50.5, "blue", "sword", ); expectDomainError(result, "invalid-max-hp"); }); it("rejects invalid color", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50, "neon", "sword", ); expectDomainError(result, "invalid-color"); }); it("rejects invalid icon", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50, "blue", "banana", ); expectDomainError(result, "invalid-icon"); }); it("allows undefined color", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50, undefined, "sword", ); if (isDomainError(result)) throw new Error(result.message); expect(result.characters[0].color).toBeUndefined(); }); it("allows undefined icon", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50, "blue", undefined, ); if (isDomainError(result)) throw new Error(result.message); expect(result.characters[0].icon).toBeUndefined(); }); it("allows both color and icon undefined", () => { const result = createPlayerCharacter( [], id, "Test", 10, 50, undefined, undefined, ); if (isDomainError(result)) throw new Error(result.message); expect(result.characters[0].color).toBeUndefined(); expect(result.characters[0].icon).toBeUndefined(); }); it("emits exactly one event on success", () => { const { events } = success([], "Test", 10, 50); expect(events).toHaveLength(1); expect(events[0].type).toBe("PlayerCharacterCreated"); }); });