From 1f7679e5a1a8a0492f19601680397e3f517f20da Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Fri, 19 Dec 2025 13:31:14 +0100 Subject: [PATCH] test: refactor mocks to use spyOn for better isolation --- src/modules/economy/lootdrop.service.test.ts | 19 ++++----- .../inventory/inventory.service.test.ts | 40 +++++++++--------- src/modules/quest/quest.service.test.ts | 33 +++++++-------- src/modules/trade/trade.service.test.ts | 42 +++++++++---------- 4 files changed, 62 insertions(+), 72 deletions(-) diff --git a/src/modules/economy/lootdrop.service.test.ts b/src/modules/economy/lootdrop.service.test.ts index 6c039aa..cc7418f 100644 --- a/src/modules/economy/lootdrop.service.test.ts +++ b/src/modules/economy/lootdrop.service.test.ts @@ -1,7 +1,8 @@ -import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"; +import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from "bun:test"; import { lootdropService } from "./lootdrop.service"; import { lootdrops } from "@/db/schema"; import { eq, and, isNull } from "drizzle-orm"; +import { economyService } from "./economy.service"; // Mock dependencies BEFORE using service functionality const mockInsert = mock(); @@ -34,16 +35,6 @@ mock.module("@/lib/DrizzleClient", () => { }; }); -// Mock EconomyService -const mockModifyUserBalance = mock(); -mock.module("./economy.service", () => { - return { - economyService: { - modifyUserBalance: mockModifyUserBalance - } - }; -}); - // Mock Config mock.module("@/lib/config", () => ({ config: { @@ -63,6 +54,7 @@ mock.module("@/lib/config", () => ({ describe("lootdropService", () => { let originalRandom: any; + let mockModifyUserBalance: any; beforeEach(() => { mockInsert.mockClear(); @@ -74,7 +66,6 @@ describe("lootdropService", () => { mockWhere.mockClear(); mockSelect.mockClear(); mockFrom.mockClear(); - mockModifyUserBalance.mockClear(); // Reset internal state (lootdropService as any).channelActivity = new Map(); @@ -82,10 +73,14 @@ describe("lootdropService", () => { // Mock Math.random originalRandom = Math.random; + + // Spy + mockModifyUserBalance = spyOn(economyService, 'modifyUserBalance').mockResolvedValue({} as any); }); afterEach(() => { Math.random = originalRandom; + mockModifyUserBalance.mockRestore(); }); describe("processMessage", () => { diff --git a/src/modules/inventory/inventory.service.test.ts b/src/modules/inventory/inventory.service.test.ts index 3fe8901..487b878 100644 --- a/src/modules/inventory/inventory.service.test.ts +++ b/src/modules/inventory/inventory.service.test.ts @@ -1,7 +1,9 @@ -import { describe, it, expect, mock, beforeEach } from "bun:test"; +import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from "bun:test"; import { inventoryService } from "./inventory.service"; import { inventory, items, userTimers } from "@/db/schema"; -import { app } from "@/index"; // We don't need app here, removed. +// Helper to mock resolved value for spyOn +import { economyService } from "@/modules/economy/economy.service"; +import { levelingService } from "@/modules/leveling/leveling.service"; // Mock dependencies const mockFindFirst = mock(); @@ -56,20 +58,6 @@ mock.module("@/lib/DrizzleClient", () => { }; }); -const mockModifyUserBalance = mock(); -mock.module("@/modules/economy/economy.service", () => ({ - economyService: { - modifyUserBalance: mockModifyUserBalance - } -})); - -const mockAddXp = mock(); -mock.module("@/modules/leveling/leveling.service", () => ({ - levelingService: { - addXp: mockAddXp - } -})); - mock.module("@/lib/config", () => ({ config: { inventory: { @@ -80,6 +68,9 @@ mock.module("@/lib/config", () => ({ })); describe("inventoryService", () => { + let mockModifyUserBalance: any; + let mockAddXp: any; + beforeEach(() => { mockFindFirst.mockReset(); mockFindMany.mockReset(); @@ -92,9 +83,16 @@ describe("inventoryService", () => { mockWhere.mockClear(); mockSelect.mockClear(); mockFrom.mockClear(); - mockModifyUserBalance.mockClear(); - mockAddXp.mockClear(); mockOnConflictDoUpdate.mockClear(); + + // Setup Spies + mockModifyUserBalance = spyOn(economyService, 'modifyUserBalance').mockResolvedValue({} as any); + mockAddXp = spyOn(levelingService, 'addXp').mockResolvedValue({} as any); + }); + + afterEach(() => { + mockModifyUserBalance.mockRestore(); + mockAddXp.mockRestore(); }); describe("addItem", () => { @@ -124,7 +122,8 @@ describe("inventoryService", () => { const result = await inventoryService.addItem("1", 1, 5n); - expect(result.quantity).toBe(15n); + expect(result).toBeDefined(); + expect(result?.quantity).toBe(15n); expect(mockUpdate).toHaveBeenCalledWith(inventory); expect(mockSet).toHaveBeenCalledWith({ quantity: 15n }); }); @@ -163,7 +162,8 @@ describe("inventoryService", () => { const result = await inventoryService.removeItem("1", 1, 5n); expect(mockDelete).toHaveBeenCalledWith(inventory); - expect(result.quantity).toBe(0n); + expect(result).toBeDefined(); + expect(result?.quantity).toBe(0n); }); it("should throw if insufficient quantity", async () => { diff --git a/src/modules/quest/quest.service.test.ts b/src/modules/quest/quest.service.test.ts index 6460382..a9f770d 100644 --- a/src/modules/quest/quest.service.test.ts +++ b/src/modules/quest/quest.service.test.ts @@ -1,7 +1,9 @@ -import { describe, it, expect, mock, beforeEach } from "bun:test"; +import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from "bun:test"; import { questService } from "./quest.service"; import { userQuests } from "@/db/schema"; import { eq, and } from "drizzle-orm"; +import { economyService } from "@/modules/economy/economy.service"; +import { levelingService } from "@/modules/leveling/leveling.service"; // Mock dependencies const mockFindFirst = mock(); @@ -45,22 +47,10 @@ mock.module("@/lib/DrizzleClient", () => { }; }); -// Mock External Services -const mockModifyUserBalance = mock(); -mock.module("@/modules/economy/economy.service", () => ({ - economyService: { - modifyUserBalance: mockModifyUserBalance - } -})); - -const mockAddXp = mock(); -mock.module("@/modules/leveling/leveling.service", () => ({ - levelingService: { - addXp: mockAddXp - } -})); - describe("questService", () => { + let mockModifyUserBalance: any; + let mockAddXp: any; + beforeEach(() => { mockFindFirst.mockReset(); mockFindMany.mockReset(); @@ -71,8 +61,15 @@ describe("questService", () => { mockSet.mockClear(); mockWhere.mockClear(); mockOnConflictDoNothing.mockClear(); - mockModifyUserBalance.mockClear(); - mockAddXp.mockClear(); + + // Setup Spies + mockModifyUserBalance = spyOn(economyService, 'modifyUserBalance').mockResolvedValue({} as any); + mockAddXp = spyOn(levelingService, 'addXp').mockResolvedValue({} as any); + }); + + afterEach(() => { + mockModifyUserBalance.mockRestore(); + mockAddXp.mockRestore(); }); describe("assignQuest", () => { diff --git a/src/modules/trade/trade.service.test.ts b/src/modules/trade/trade.service.test.ts index 5ba30f6..d694290 100644 --- a/src/modules/trade/trade.service.test.ts +++ b/src/modules/trade/trade.service.test.ts @@ -1,6 +1,8 @@ -import { describe, it, expect, mock, beforeEach, setSystemTime } from "bun:test"; +import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from "bun:test"; import { TradeService } from "./trade.service"; import { itemTransactions } from "@/db/schema"; +import { economyService } from "@/modules/economy/economy.service"; +import { inventoryService } from "@/modules/inventory/inventory.service"; // Mock dependencies const mockInsert = mock(); @@ -22,38 +24,34 @@ mock.module("@/lib/DrizzleClient", () => { }; }); -// Mock External Services -const mockModifyUserBalance = mock(); -mock.module("@/modules/economy/economy.service", () => ({ - economyService: { - modifyUserBalance: mockModifyUserBalance - } -})); - -const mockAddItem = mock(); -const mockRemoveItem = mock(); -mock.module("@/modules/inventory/inventory.service", () => ({ - inventoryService: { - addItem: mockAddItem, - removeItem: mockRemoveItem - } -})); - describe("TradeService", () => { const userA = { id: "1", username: "UserA" }; const userB = { id: "2", username: "UserB" }; + let mockModifyUserBalance: any; + let mockAddItem: any; + let mockRemoveItem: any; + beforeEach(() => { - mockModifyUserBalance.mockClear(); - mockAddItem.mockClear(); - mockRemoveItem.mockClear(); mockInsert.mockClear(); mockValues.mockClear(); // Clear sessions (TradeService as any).sessions.clear(); + + // Spies + mockModifyUserBalance = spyOn(economyService, 'modifyUserBalance').mockResolvedValue({} as any); + mockAddItem = spyOn(inventoryService, 'addItem').mockResolvedValue({} as any); + mockRemoveItem = spyOn(inventoryService, 'removeItem').mockResolvedValue({} as any); }); + afterEach(() => { + mockModifyUserBalance.mockRestore(); + mockAddItem.mockRestore(); + mockRemoveItem.mockRestore(); + }); + + describe("createSession", () => { it("should create a new session", () => { const session = TradeService.createSession("thread1", userA, userB); @@ -108,7 +106,7 @@ describe("TradeService", () => { TradeService.addItem("thread1", "1", { id: 10, name: "Sword" }, 2n); const session = TradeService.getSession("thread1"); - expect(session?.userA.offer.items[0].quantity).toBe(3n); + expect(session?.userA.offer.items[0]!.quantity).toBe(3n); }); });