import { describe, expect, test, mock, beforeEach, spyOn } from "bun:test"; import { systemEvents, EVENTS } from "@shared/lib/events"; // Mock Discord.js Client and related classes mock.module("discord.js", () => ({ Client: class { constructor() { } on() { } once() { } login() { } destroy() { } removeAllListeners() { } }, Collection: Map, GatewayIntentBits: { Guilds: 1, MessageContent: 1, GuildMessages: 1, GuildMembers: 1 }, REST: class { setToken() { return this; } put() { return Promise.resolve([]); } }, Routes: { applicationGuildCommands: () => 'guild_route', applicationCommands: () => 'global_route' } })); // Mock loaders to avoid filesystem access during client init mock.module("../lib/loaders/CommandLoader", () => ({ CommandLoader: class { constructor() { } loadFromDirectory() { return Promise.resolve({ loaded: 0, skipped: 0, errors: [] }); } } })); mock.module("../lib/loaders/EventLoader", () => ({ EventLoader: class { constructor() { } loadFromDirectory() { return Promise.resolve({ loaded: 0, skipped: 0, errors: [] }); } } })); // Mock dashboard service to prevent network/db calls during event handling mock.module("@shared/modules/economy/lootdrop.service", () => ({ lootdropService: { clearCaches: mock(async () => { }) } })); mock.module("@shared/modules/trade/trade.service", () => ({ tradeService: { clearSessions: mock(() => { }) } })); mock.module("@/modules/admin/item_wizard", () => ({ clearDraftSessions: mock(() => { }) })); mock.module("@shared/modules/dashboard/dashboard.service", () => ({ dashboardService: { recordEvent: mock(() => Promise.resolve()) } })); describe("AuroraClient System Events", () => { let AuroraClient: any; beforeEach(async () => { systemEvents.removeAllListeners(); const module = await import("./BotClient"); AuroraClient = module.AuroraClient; AuroraClient.maintenanceMode = false; // MUST call explicitly now await AuroraClient.setupSystemEvents(); }); /** * Test Case: Maintenance Mode Toggle * Requirement: Client state should update when event is received */ test("should toggle maintenanceMode when MAINTENANCE_MODE event is received", async () => { systemEvents.emit(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled: true, reason: "Testing" }); await new Promise(resolve => setTimeout(resolve, 30)); expect(AuroraClient.maintenanceMode).toBe(true); systemEvents.emit(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled: false }); await new Promise(resolve => setTimeout(resolve, 30)); expect(AuroraClient.maintenanceMode).toBe(false); }); /** * Test Case: Command Reload * Requirement: loadCommands and deployCommands should be called */ test("should reload commands when RELOAD_COMMANDS event is received", async () => { const loadSpy = spyOn(AuroraClient, "loadCommands").mockImplementation(() => Promise.resolve()); const deploySpy = spyOn(AuroraClient, "deployCommands").mockImplementation(() => Promise.resolve()); systemEvents.emit(EVENTS.ACTIONS.RELOAD_COMMANDS); await new Promise(resolve => setTimeout(resolve, 50)); expect(loadSpy).toHaveBeenCalled(); expect(deploySpy).toHaveBeenCalled(); }); /** * Test Case: Cache Clearance * Requirement: Service clear methods should be triggered */ test("should trigger service cache clearance when CLEAR_CACHE is received", async () => { const { lootdropService } = await import("@shared/modules/economy/lootdrop.service"); const { tradeService } = await import("@shared/modules/trade/trade.service"); systemEvents.emit(EVENTS.ACTIONS.CLEAR_CACHE); await new Promise(resolve => setTimeout(resolve, 50)); expect(lootdropService.clearCaches).toHaveBeenCalled(); expect(tradeService.clearSessions).toHaveBeenCalled(); }); });