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(); const mockFindMany = mock(); const mockInsert = mock(); const mockUpdate = mock(); const mockDelete = mock(); const mockValues = mock(); const mockReturning = mock(); const mockSet = mock(); const mockWhere = mock(); const mockOnConflictDoNothing = mock(); // Chain setup mockInsert.mockReturnValue({ values: mockValues }); mockValues.mockReturnValue({ onConflictDoNothing: mockOnConflictDoNothing }); mockOnConflictDoNothing.mockReturnValue({ returning: mockReturning }); mockUpdate.mockReturnValue({ set: mockSet }); mockSet.mockReturnValue({ where: mockWhere }); mockWhere.mockReturnValue({ returning: mockReturning }); // Mock DrizzleClient mock.module("@/lib/DrizzleClient", () => { const createMockTx = () => ({ query: { userQuests: { findFirst: mockFindFirst, findMany: mockFindMany }, }, insert: mockInsert, update: mockUpdate, delete: mockDelete, }); return { DrizzleClient: { ...createMockTx(), transaction: async (cb: any) => cb(createMockTx()), } }; }); describe("questService", () => { let mockModifyUserBalance: any; let mockAddXp: any; beforeEach(() => { mockFindFirst.mockReset(); mockFindMany.mockReset(); mockInsert.mockClear(); mockUpdate.mockClear(); mockValues.mockClear(); mockReturning.mockClear(); mockSet.mockClear(); mockWhere.mockClear(); mockOnConflictDoNothing.mockClear(); // Setup Spies mockModifyUserBalance = spyOn(economyService, 'modifyUserBalance').mockResolvedValue({} as any); mockAddXp = spyOn(levelingService, 'addXp').mockResolvedValue({} as any); }); afterEach(() => { mockModifyUserBalance.mockRestore(); mockAddXp.mockRestore(); }); describe("assignQuest", () => { it("should assign quest", async () => { mockReturning.mockResolvedValue([{ userId: 1n, questId: 101 }]); const result = await questService.assignQuest("1", 101); expect(result).toEqual([{ userId: 1n, questId: 101 }] as any); expect(mockInsert).toHaveBeenCalledWith(userQuests); expect(mockValues).toHaveBeenCalledWith({ userId: 1n, questId: 101, progress: 0 }); }); }); describe("updateProgress", () => { it("should update progress", async () => { mockReturning.mockResolvedValue([{ userId: 1n, questId: 101, progress: 50 }]); const result = await questService.updateProgress("1", 101, 50); expect(result).toEqual([{ userId: 1n, questId: 101, progress: 50 }] as any); expect(mockUpdate).toHaveBeenCalledWith(userQuests); expect(mockSet).toHaveBeenCalledWith({ progress: 50 }); }); }); describe("completeQuest", () => { it("should complete quest and grant rewards", async () => { const mockUserQuest = { userId: 1n, questId: 101, completedAt: null, quest: { rewards: { balance: 100, xp: 50 } } }; mockFindFirst.mockResolvedValue(mockUserQuest); const result = await questService.completeQuest("1", 101); expect(result.success).toBe(true); expect(result.rewards.balance).toBe(100n); expect(result.rewards.xp).toBe(50n); // Check updates expect(mockUpdate).toHaveBeenCalledWith(userQuests); expect(mockSet).toHaveBeenCalledWith({ completedAt: expect.any(Date) }); // Check service calls expect(mockModifyUserBalance).toHaveBeenCalledWith("1", 100n, 'QUEST_REWARD', expect.any(String), null, expect.anything()); expect(mockAddXp).toHaveBeenCalledWith("1", 50n, expect.anything()); }); it("should throw if quest not assigned", async () => { mockFindFirst.mockResolvedValue(null); expect(questService.completeQuest("1", 101)).rejects.toThrow("Quest not assigned"); }); it("should throw if already completed", async () => { mockFindFirst.mockResolvedValue({ completedAt: new Date() }); expect(questService.completeQuest("1", 101)).rejects.toThrow("Quest already completed"); }); }); describe("getUserQuests", () => { it("should return user quests", async () => { const mockData = [{ questId: 1 }, { questId: 2 }]; mockFindMany.mockResolvedValue(mockData); const result = await questService.getUserQuests("1"); expect(result).toEqual(mockData as any); }); }); });