forked from syntaxbullet/AuroraBot-discord
152 lines
5.1 KiB
TypeScript
152 lines
5.1 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|