inventory.test.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { resolve } from "node:path";
  2. import { describe, expect, it } from "vitest";
  3. import { createEngine, ContentRegistry, InventorySystem, World, WorldEventBus } from "../src/index.js";
  4. const repoRoot = resolve(process.cwd(), "../..");
  5. const contentDir = resolve(repoRoot, "content");
  6. const modsDir = resolve(repoRoot, "mods");
  7. function setup() {
  8. const world = new World();
  9. const registry = new ContentRegistry();
  10. const events = new WorldEventBus();
  11. world.create({ id: "player", type: "player", x: 0, y: 0 });
  12. const inv = new InventorySystem();
  13. inv.init({ world, registry, events });
  14. return { inv, world, events };
  15. }
  16. describe("InventorySystem", () => {
  17. it("init creates an inventory field on the player entity", () => {
  18. const { world } = setup();
  19. expect(world.get("player")?.inventory).toEqual({});
  20. });
  21. it("give/count/items roundtrip", () => {
  22. const { inv } = setup();
  23. expect(inv.give("hoe", 2)).toBe(2);
  24. expect(inv.give("hoe", 1)).toBe(3);
  25. expect(inv.count("hoe")).toBe(3);
  26. expect(inv.count("parsnip")).toBe(0);
  27. expect(inv.items()).toEqual({ hoe: 3 });
  28. });
  29. it("items() returns a snapshot copy, not a live reference", () => {
  30. const { inv } = setup();
  31. inv.give("hoe", 1);
  32. const snapshot = inv.items();
  33. inv.give("hoe", 1);
  34. expect(snapshot).toEqual({ hoe: 1 });
  35. expect(inv.items()).toEqual({ hoe: 2 });
  36. });
  37. it("give emits item-acquire with itemId, qty, and total", () => {
  38. const { inv, events } = setup();
  39. const seen: unknown[] = [];
  40. events.on("item-acquire", (p) => seen.push(p));
  41. inv.give("hoe", 2);
  42. expect(seen).toEqual([{ itemId: "hoe", qty: 2, total: 2 }]);
  43. });
  44. it("take decrements and emits item-use", () => {
  45. const { inv, events } = setup();
  46. const seen: unknown[] = [];
  47. events.on("item-use", (p) => seen.push(p));
  48. inv.give("hoe", 3);
  49. expect(inv.take("hoe", 1)).toBe(1);
  50. expect(seen).toEqual([{ itemId: "hoe", qty: 1, total: 2 }]);
  51. expect(inv.count("hoe")).toBe(2);
  52. });
  53. it("take clamps to the available quantity when insufficient", () => {
  54. const { inv, events } = setup();
  55. const seen: unknown[] = [];
  56. events.on("item-use", (p) => seen.push(p));
  57. inv.give("hoe", 3);
  58. expect(inv.take("hoe", 10)).toBe(3);
  59. expect(inv.count("hoe")).toBe(0);
  60. expect(inv.items()).toEqual({});
  61. expect(seen).toEqual([{ itemId: "hoe", qty: 3, total: 0 }]);
  62. });
  63. it("take of an absent item returns 0 and emits nothing", () => {
  64. const { inv, events } = setup();
  65. const seen: unknown[] = [];
  66. events.on("item-use", (p) => seen.push(p));
  67. expect(inv.take("hoe", 5)).toBe(0);
  68. expect(seen).toHaveLength(0);
  69. });
  70. it("give/take reject non-positive quantities", () => {
  71. const { inv } = setup();
  72. expect(() => inv.give("hoe", 0)).toThrow(/positive integer/);
  73. expect(() => inv.give("hoe", -1)).toThrow(/positive integer/);
  74. expect(() => inv.give("hoe", 1.5)).toThrow(/positive integer/);
  75. expect(() => inv.take("hoe", 0)).toThrow(/positive integer/);
  76. });
  77. it("wireEvents: inventory events fire onItemAcquire/onItemUse hooks with a HookContext", async () => {
  78. const engine = createEngine({ contentDir, modsDir });
  79. await engine.start();
  80. try {
  81. const acquired: unknown[] = [];
  82. const used: unknown[] = [];
  83. engine.hooks.on("onItemAcquire", (ctx) => {
  84. acquired.push({ world: ctx.world.get("player")?.id, time: ctx.time.get().day });
  85. });
  86. engine.hooks.on("onItemUse", (ctx) => {
  87. used.push({ world: ctx.world.get("player")?.id, time: ctx.time.get().day });
  88. });
  89. const inv = engine.systems.get<InventorySystem>("inventory");
  90. expect(inv).toBeDefined();
  91. inv!.give("hoe", 2);
  92. inv!.take("hoe", 1);
  93. expect(acquired).toEqual([{ world: "player", time: 1 }]);
  94. expect(used).toEqual([{ world: "player", time: 1 }]);
  95. expect(inv!.items()).toEqual({ hoe: 1 });
  96. } finally {
  97. await engine.stop();
  98. }
  99. });
  100. });