forked from syntaxbullet/AuroraBot-discord
feat: Set daily claim cooldown to next UTC midnight and reset streak to 1 if missed by over 24 hours.
This commit is contained in:
@@ -173,10 +173,22 @@ describe("economyService", () => {
|
|||||||
it("should throw if cooldown is active", async () => {
|
it("should throw if cooldown is active", async () => {
|
||||||
const future = new Date("2023-01-02T12:00:00Z"); // +24h
|
const future = new Date("2023-01-02T12:00:00Z"); // +24h
|
||||||
mockFindFirst.mockResolvedValue({ expiresAt: future });
|
mockFindFirst.mockResolvedValue({ expiresAt: future });
|
||||||
|
|
||||||
expect(economyService.claimDaily("1")).rejects.toThrow("Daily already claimed");
|
expect(economyService.claimDaily("1")).rejects.toThrow("Daily already claimed");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should set cooldown to next UTC midnight", async () => {
|
||||||
|
// 2023-01-01T12:00:00Z -> Should be 2023-01-02T00:00:00Z
|
||||||
|
|
||||||
|
mockFindFirst
|
||||||
|
.mockResolvedValueOnce(undefined) // No cooldown
|
||||||
|
.mockResolvedValueOnce({ id: 1n, dailyStreak: 5, balance: 1000n });
|
||||||
|
|
||||||
|
const result = await economyService.claimDaily("1");
|
||||||
|
|
||||||
|
const expectedReset = new Date("2023-01-02T00:00:00Z");
|
||||||
|
expect(result.nextReadyAt.toISOString()).toBe(expectedReset.toISOString());
|
||||||
|
});
|
||||||
|
|
||||||
it("should reset streak if missed a day (long time gap)", async () => {
|
it("should reset streak if missed a day (long time gap)", async () => {
|
||||||
// Expired 3 days ago
|
// Expired 3 days ago
|
||||||
const past = new Date("2023-01-01T00:00:00Z"); // now is 12:00
|
const past = new Date("2023-01-01T00:00:00Z"); // now is 12:00
|
||||||
@@ -193,8 +205,31 @@ describe("economyService", () => {
|
|||||||
const result = await economyService.claimDaily("1");
|
const result = await economyService.claimDaily("1");
|
||||||
|
|
||||||
// timeSinceReady = 48h.
|
// timeSinceReady = 48h.
|
||||||
// streak = (5+1) - floor(48h / 24h) = 6 - 2 = 4.
|
// streak should reset to 1
|
||||||
expect(result.streak).toBe(4);
|
expect(result.streak).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prevent weekly bonus exploit by resetting streak", async () => {
|
||||||
|
// Mock user at streak 7.
|
||||||
|
// Mock time as 24h + 1m after expiry.
|
||||||
|
|
||||||
|
const expiredAt = new Date("2023-01-01T11:59:00Z"); // now is 12:00 next day, plus 1 min gap?
|
||||||
|
// no, 'now' is 2023-01-01T12:00:00Z set in beforeEach
|
||||||
|
|
||||||
|
// We want gap > 24h.
|
||||||
|
// If expiry was yesterday 11:59:59. Gap is 24h + 1s.
|
||||||
|
|
||||||
|
const expiredAtExploit = new Date("2022-12-31T11:59:00Z"); // Over 24h ago
|
||||||
|
|
||||||
|
mockFindFirst
|
||||||
|
.mockResolvedValueOnce({ expiresAt: expiredAtExploit })
|
||||||
|
.mockResolvedValueOnce({ id: 1n, dailyStreak: 7 });
|
||||||
|
|
||||||
|
const result = await economyService.claimDaily("1");
|
||||||
|
|
||||||
|
// Should reset to 1
|
||||||
|
expect(result.streak).toBe(1);
|
||||||
|
expect(result.isWeekly).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export const economyService = {
|
|||||||
if (cooldown) {
|
if (cooldown) {
|
||||||
const timeSinceReady = now.getTime() - cooldown.expiresAt.getTime();
|
const timeSinceReady = now.getTime() - cooldown.expiresAt.getTime();
|
||||||
if (timeSinceReady > 24 * 60 * 60 * 1000) {
|
if (timeSinceReady > 24 * 60 * 60 * 1000) {
|
||||||
streak = Math.max(1, streak - Math.floor(timeSinceReady / (24 * 60 * 60 * 1000)));
|
streak = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
streak = 1;
|
streak = 1;
|
||||||
@@ -119,8 +119,10 @@ export const economyService = {
|
|||||||
})
|
})
|
||||||
.where(eq(users.id, BigInt(userId)));
|
.where(eq(users.id, BigInt(userId)));
|
||||||
|
|
||||||
// Set new cooldown (now + 24h)
|
// Set new cooldown (Next UTC Midnight)
|
||||||
const nextReadyAt = new Date(now.getTime() + config.economy.daily.cooldownMs);
|
const nextReadyAt = new Date(now);
|
||||||
|
nextReadyAt.setUTCDate(nextReadyAt.getUTCDate() + 1);
|
||||||
|
nextReadyAt.setUTCHours(0, 0, 0, 0);
|
||||||
|
|
||||||
await txFn.insert(userTimers)
|
await txFn.insert(userTimers)
|
||||||
.values({
|
.values({
|
||||||
|
|||||||
Reference in New Issue
Block a user