feat: implement scheduled cleanup job for expired data

This commit is contained in:
syntaxbullet
2026-01-06 17:44:08 +01:00
parent 606d83a7ae
commit bc89ddf7c0
4 changed files with 262 additions and 70 deletions

View File

@@ -0,0 +1,111 @@
import { describe, it, expect, mock, beforeEach, spyOn } from "bun:test";
import { cleanupService } from "./cleanup.service";
import { lootdrops, userTimers, userQuests } from "@/db/schema";
import { config } from "@/lib/config";
const mockDelete = mock();
const mockReturning = mock();
const mockWhere = mock();
const mockFindMany = mock();
mockDelete.mockReturnValue({ where: mockWhere });
mockWhere.mockReturnValue({ returning: mockReturning });
// Mock DrizzleClient
mock.module("@/lib/DrizzleClient", () => ({
DrizzleClient: {
delete: mockDelete,
query: {
userTimers: {
findMany: mockFindMany
}
}
}
}));
// Mock AuroraClient
mock.module("@/lib/BotClient", () => ({
AuroraClient: {
guilds: {
fetch: mock().mockResolvedValue({
members: {
fetch: mock().mockResolvedValue({
user: { tag: "TestUser#1234" },
roles: { remove: mock().mockResolvedValue({}) }
})
}
})
}
}
}));
// Mock Config
mock.module("@/lib/config", () => ({
config: {
system: {
cleanup: {
intervalMs: 86400000,
questArchiveDays: 30
}
}
}
}));
describe("cleanupService", () => {
beforeEach(() => {
mockDelete.mockClear();
mockWhere.mockClear();
mockReturning.mockClear();
mockFindMany.mockClear();
});
it("cleanupLootdrops should delete expired unclaimed lootdrops", async () => {
mockReturning.mockResolvedValue([{ id: "msg1" }, { id: "msg2" }]);
const count = await cleanupService.cleanupLootdrops();
expect(count).toBe(2);
expect(mockDelete).toHaveBeenCalledWith(lootdrops);
});
it("cleanupTimers should delete expired timers and handle roles", async () => {
// Mock findMany for expired ACCESS timers
mockFindMany.mockResolvedValue([
{ userId: 123n, type: 'ACCESS', key: 'role_456', metadata: { roleId: '456' } }
]);
// Mock returning for bulk delete
mockReturning.mockResolvedValue([{ userId: 789n }]); // One other timer
const count = await cleanupService.cleanupTimers();
// 1 from findMany + 1 from bulk delete (simplified mock behavior)
expect(count).toBe(2);
expect(mockDelete).toHaveBeenCalledWith(userTimers);
});
it("cleanupQuests should delete old completed quests", async () => {
mockReturning.mockResolvedValue([{ userId: 123n }]);
const count = await cleanupService.cleanupQuests();
expect(count).toBe(1);
expect(mockDelete).toHaveBeenCalledWith(userQuests);
});
it("runAll should run all cleanup tasks and log stats", async () => {
const spyLootdrops = spyOn(cleanupService, 'cleanupLootdrops').mockResolvedValue(1);
const spyTimers = spyOn(cleanupService, 'cleanupTimers').mockResolvedValue(2);
const spyQuests = spyOn(cleanupService, 'cleanupQuests').mockResolvedValue(3);
await cleanupService.runAll();
expect(spyLootdrops).toHaveBeenCalled();
expect(spyTimers).toHaveBeenCalled();
expect(spyQuests).toHaveBeenCalled();
spyLootdrops.mockRestore();
spyTimers.mockRestore();
spyQuests.mockRestore();
});
});