refactor: extract Discord.js code from shared services into bot layer
Move terminal.service.ts and prune.service.ts entirely to bot/modules/ since they are Discord-specific. Split lootdrop.service.ts: pure logic (activity tracking, DB ops, claim) stays in shared/, Discord operations (message sending, channel interactions) move to bot/modules/economy/ lootdrop.handler.ts. Move effect registry/handlers/types from bot/ to shared/modules/inventory/ since they contain no Discord.js imports and are needed by inventory.service.ts in shared. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,92 +82,81 @@ describe("lootdropService", () => {
|
||||
mockModifyUserBalance.mockRestore();
|
||||
});
|
||||
|
||||
describe("processMessage", () => {
|
||||
it("should track activity but not spawn if minMessages not reached", async () => {
|
||||
const mockChannel = { id: "chan1", send: mock() };
|
||||
const mockMessage = {
|
||||
author: { bot: false },
|
||||
guild: {},
|
||||
channel: mockChannel
|
||||
};
|
||||
describe("trackActivity", () => {
|
||||
it("should track activity but not spawn if minMessages not reached", () => {
|
||||
const result1 = lootdropService.trackActivity("chan1");
|
||||
const result2 = lootdropService.trackActivity("chan1");
|
||||
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
|
||||
// Expect no spawn attempt
|
||||
expect(mockChannel.send).not.toHaveBeenCalled();
|
||||
// Internal state check if possible, or just behavior
|
||||
expect(result1.shouldSpawn).toBe(false);
|
||||
expect(result2.shouldSpawn).toBe(false);
|
||||
});
|
||||
|
||||
it("should spawn lootdrop if minMessages reached and chance hits", async () => {
|
||||
const mockChannel = { id: "chan1", send: mock() };
|
||||
const mockMessage = {
|
||||
author: { bot: false },
|
||||
guild: {},
|
||||
channel: mockChannel
|
||||
};
|
||||
|
||||
mockChannel.send.mockResolvedValue({ id: "msg1" });
|
||||
it("should spawn lootdrop if minMessages reached and chance hits", () => {
|
||||
Math.random = () => 0.01; // Force hit (0.01 < 0.5)
|
||||
|
||||
// Send 3 messages
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
lootdropService.trackActivity("chan1");
|
||||
lootdropService.trackActivity("chan1");
|
||||
const result = lootdropService.trackActivity("chan1");
|
||||
|
||||
expect(mockChannel.send).toHaveBeenCalled();
|
||||
expect(mockInsert).toHaveBeenCalledWith(lootdrops);
|
||||
|
||||
// Verify DB insert
|
||||
expect(mockValues).toHaveBeenCalledWith(expect.objectContaining({
|
||||
channelId: "chan1",
|
||||
messageId: "msg1",
|
||||
currency: "GOLD"
|
||||
}));
|
||||
expect(result.shouldSpawn).toBe(true);
|
||||
});
|
||||
|
||||
it("should not spawn if chance fails", async () => {
|
||||
const mockChannel = { id: "chan1", send: mock() };
|
||||
const mockMessage = {
|
||||
author: { bot: false },
|
||||
guild: {},
|
||||
channel: mockChannel
|
||||
};
|
||||
|
||||
it("should not spawn if chance fails", () => {
|
||||
Math.random = () => 0.99; // Force fail (0.99 > 0.5)
|
||||
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
lootdropService.trackActivity("chan1");
|
||||
lootdropService.trackActivity("chan1");
|
||||
const result = lootdropService.trackActivity("chan1");
|
||||
|
||||
expect(mockChannel.send).not.toHaveBeenCalled();
|
||||
expect(result.shouldSpawn).toBe(false);
|
||||
});
|
||||
|
||||
it("should respect cooldowns", async () => {
|
||||
const mockChannel = { id: "chan1", send: mock() };
|
||||
const mockMessage = {
|
||||
author: { bot: false },
|
||||
guild: {},
|
||||
channel: mockChannel
|
||||
};
|
||||
mockChannel.send.mockResolvedValue({ id: "msg1" });
|
||||
|
||||
it("should respect cooldowns", () => {
|
||||
Math.random = () => 0.01; // Force hit
|
||||
|
||||
// Trigger spawn
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
lootdropService.trackActivity("chan1");
|
||||
lootdropService.trackActivity("chan1");
|
||||
const result1 = lootdropService.trackActivity("chan1");
|
||||
|
||||
expect(mockChannel.send).toHaveBeenCalledTimes(1);
|
||||
mockChannel.send.mockClear();
|
||||
expect(result1.shouldSpawn).toBe(true);
|
||||
|
||||
// Try again immediately (cooldown active)
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
await lootdropService.processMessage(mockMessage as any);
|
||||
lootdropService.trackActivity("chan1");
|
||||
lootdropService.trackActivity("chan1");
|
||||
const result2 = lootdropService.trackActivity("chan1");
|
||||
|
||||
expect(mockChannel.send).not.toHaveBeenCalled();
|
||||
expect(result2.shouldSpawn).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("calculateReward", () => {
|
||||
it("should return override values when provided", () => {
|
||||
const result = lootdropService.calculateReward(500, "SILVER");
|
||||
expect(result.reward).toBe(500);
|
||||
expect(result.currency).toBe("SILVER");
|
||||
});
|
||||
|
||||
it("should return random reward within range when no override", () => {
|
||||
const result = lootdropService.calculateReward();
|
||||
expect(result.reward).toBeGreaterThanOrEqual(10);
|
||||
expect(result.reward).toBeLessThanOrEqual(100);
|
||||
expect(result.currency).toBe("GOLD");
|
||||
});
|
||||
});
|
||||
|
||||
describe("persistLootdrop", () => {
|
||||
it("should insert lootdrop into database", async () => {
|
||||
await lootdropService.persistLootdrop("msg1", "chan1", 50, "GOLD");
|
||||
|
||||
expect(mockInsert).toHaveBeenCalledWith(lootdrops);
|
||||
expect(mockValues).toHaveBeenCalledWith(expect.objectContaining({
|
||||
messageId: "msg1",
|
||||
channelId: "chan1",
|
||||
rewardAmount: 50,
|
||||
currency: "GOLD"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user