Skip to content

Commit 62777bd

Browse files
committed
add some tests !
1 parent faee866 commit 62777bd

File tree

5 files changed

+145
-15
lines changed

5 files changed

+145
-15
lines changed

bun.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@
3434
"@eslint/js": "^9.32.0",
3535
"@microsoft/eslint-formatter-sarif": "3.1.0",
3636
"@sentry/types": "^8.55.0",
37+
"@sinonjs/fake-timers": "^14.0.0",
3738
"@types/bun": "^1.2.19",
3839
"@types/node": "^22.17.0",
3940
"@types/node-schedule": "^2.1.8",
4041
"@types/opentype.js": "^1.3.8",
4142
"@types/pg": "^8.15.5",
4243
"@types/sequelize": "^4.28.20",
44+
"@types/sinonjs__fake-timers": "^8.1.5",
4345
"@types/sqlite3": "^3.1.11",
4446
"@types/string-similarity": "^4.0.2",
4547
"@types/validator": "^13.15.2",
@@ -250,6 +252,10 @@
250252

251253
"@sequelize/utils": ["@sequelize/[email protected]", "", { "dependencies": { "@types/lodash": "^4.17.16", "lodash": "^4.17.21" } }, "sha512-YOPCa189WimlT10mokbOObkgSJvv3IgEQG7OhszSkQJWDvVjI6WmBsjD+LB2p5IY2kUWyKaRVmaDxW1u/SSQIg=="],
252254

255+
"@sinonjs/commons": ["@sinonjs/[email protected]", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="],
256+
257+
"@sinonjs/fake-timers": ["@sinonjs/[email protected]", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-QfoXRaUTjMVVn/ZbnD4LS3TPtqOkOdKIYCKldIVPnuClcwRKat6LI2mRZ2s5qiBfO6Fy03An35dSls/2/FEc0Q=="],
258+
253259
"@tootallnate/once": ["@tootallnate/[email protected]", "", {}, "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="],
254260

255261
"@ts-morph/common": ["@ts-morph/[email protected]", "", { "dependencies": { "fast-glob": "^3.2.7", "minimatch": "^3.0.4", "mkdirp": "^1.0.4", "path-browserify": "^1.0.1" } }, "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w=="],
@@ -306,6 +312,8 @@
306312

307313
"@types/shimmer": ["@types/[email protected]", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="],
308314

315+
"@types/sinonjs__fake-timers": ["@types/[email protected]", "", {}, "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ=="],
316+
309317
"@types/sqlite3": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-KYF+QgxAnnAh7DWPdNDroxkDI3/MspH1NMx6m/N/6fT1G6+jvsw4/ZePt8R8cr7ta58aboeTfYFBDxTJ5yv15w=="],
310318

311319
"@types/string-similarity": ["@types/[email protected]", "", {}, "sha512-LkJQ/jsXtCVMK+sKYAmX/8zEq+/46f1PTQw7YtmQwb74jemS1SlNLmARM2Zml9DgdDTWKAtc5L13WorpHPDjDA=="],
@@ -1254,6 +1262,8 @@
12541262

12551263
"type-check": ["[email protected]", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
12561264

1265+
"type-detect": ["[email protected]", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="],
1266+
12571267
"type-fest": ["[email protected]", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
12581268

12591269
"typed-array-buffer": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,18 @@
6262
],
6363
"repository": "https://github.com/TheDeveloperDen/DevDenBot.git",
6464
"devDependencies": {
65-
"@microsoft/eslint-formatter-sarif": "3.1.0",
6665
"@eslint/eslintrc": "^3.3.1",
6766
"@eslint/js": "^9.32.0",
67+
"@microsoft/eslint-formatter-sarif": "3.1.0",
6868
"@sentry/types": "^8.55.0",
69+
"@sinonjs/fake-timers": "^14.0.0",
6970
"@types/bun": "^1.2.19",
7071
"@types/node": "^22.17.0",
7172
"@types/node-schedule": "^2.1.8",
7273
"@types/opentype.js": "^1.3.8",
7374
"@types/pg": "^8.15.5",
7475
"@types/sequelize": "^4.28.20",
76+
"@types/sinonjs__fake-timers": "^8.1.5",
7577
"@types/sqlite3": "^3.1.11",
7678
"@types/string-similarity": "^4.0.2",
7779
"@types/validator": "^13.15.2",
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { test, expect, mock, jest, afterAll } from "bun:test";
2+
import { Client, Guild, GuildMember, TextChannel } from "discord.js";
3+
import { DDUser } from "../../store/models/DDUser.js";
4+
import {
5+
scheduleAllReminders,
6+
scheduledReminders,
7+
} from "./dailyReward.reminder.js";
8+
import { beforeEach } from "node:test";
9+
import { install } from "@sinonjs/fake-timers";
10+
11+
mock.module("../../store/models/DDUser.js", () => {
12+
return {
13+
DDUser: {
14+
findAll: async () => {
15+
return [
16+
{ id: 1n, lastDailyTime: new Date() },
17+
{ id: 2n, lastDailyTime: new Date() },
18+
] as DDUser[];
19+
},
20+
findOrCreate: async (data: { where: { id: bigint } }) => {
21+
return [
22+
{ id: data.where.id, lastDailyTime: new Date() } as DDUser,
23+
true,
24+
];
25+
},
26+
},
27+
};
28+
});
29+
30+
mock.module("../../util/users.js", () => ({
31+
isSpecialUser: () => true,
32+
actualMention: (user: GuildMember) => `<@${user.id}>`,
33+
}));
34+
35+
export function fakeTimers() {
36+
const clock = install();
37+
38+
beforeEach(() => {
39+
clock.reset();
40+
});
41+
42+
afterAll(() => {
43+
clock.uninstall();
44+
});
45+
46+
return clock;
47+
}
48+
const clock = fakeTimers();
49+
test("scheduleAllReminders", async () => {
50+
jest.useFakeTimers();
51+
console.log(new Date().toLocaleString());
52+
const mockGuildFetch = mock(
53+
async () =>
54+
({
55+
members: {
56+
fetch: mockMembersFetch,
57+
},
58+
}) as unknown as Guild,
59+
);
60+
const mockChannelSend = mock(async () => Promise.resolve());
61+
const mockChannelFetch = mock(
62+
async () =>
63+
({
64+
isSendable: () => true,
65+
send: mockChannelSend,
66+
}) as unknown as TextChannel,
67+
);
68+
const mockMembersFetch = mock(
69+
async (id: string) =>
70+
({
71+
id: id,
72+
user: { tag: `User#${id}` },
73+
lastDailyTime: new Date(),
74+
}) as unknown as GuildMember,
75+
);
76+
77+
const mockClient = {
78+
guilds: {
79+
fetch: mockGuildFetch,
80+
},
81+
channels: {
82+
fetch: mockChannelFetch,
83+
},
84+
members: {
85+
fetch: mockMembersFetch,
86+
},
87+
} as unknown as Client;
88+
89+
await scheduleAllReminders(mockClient);
90+
91+
expect(mockGuildFetch).toHaveBeenCalledTimes(1);
92+
expect(mockChannelFetch).toHaveBeenCalledTimes(0); // no immediate reminders
93+
94+
await clock.tickAsync(1000 * 60 * 60 * 25); // fast forward 24 hours
95+
96+
expect(scheduledReminders.size).toBe(2); // two users should have reminders scheduled
97+
98+
expect(mockChannelFetch).toHaveBeenCalledTimes(2); // should have sent reminders for both users
99+
expect(scheduledReminders.size).toBe(2);
100+
expect(mockChannelSend).toHaveBeenCalledTimes(2);
101+
expect(mockChannelSend).toHaveBeenCalledWith({
102+
content: expect.stringContaining("<@1>"),
103+
});
104+
expect(mockChannelSend).toHaveBeenCalledWith({
105+
content: expect.stringContaining("<@2>"),
106+
});
107+
});

src/modules/xp/dailyReward.reminder.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import {
88
getActualDailyStreak,
99
getNextDailyTime,
1010
} from "./dailyReward.command.js";
11+
import { Op } from "@sequelize/core";
12+
13+
const FORTY_EIGHT_HOURS_IN_MS = 48 * 60 * 60 * 1000;
1114

1215
const sendReminder = async (client: Client, user: GuildMember) => {
1316
const botCommands = await client.channels.fetch(config.channels.botCommands);
@@ -23,11 +26,15 @@ const sendReminder = async (client: Client, user: GuildMember) => {
2326
logger.error("lastClaimTime is null");
2427
return;
2528
}
26-
if (new Date().getTime() - lastClaimTime.getTime() > 1000 * 60 * 60 * 48) {
29+
if (
30+
new Date().getTime() - lastClaimTime.getTime() >
31+
FORTY_EIGHT_HOURS_IN_MS
32+
) {
2733
logger.info(
2834
`User ${user.user.tag} has not claimed their daily in over 48 hours, not reminding them and cancelling future reminders`,
2935
);
3036
scheduledReminders.get(ddUser.id)?.cancel();
37+
scheduledReminders.delete(ddUser.id);
3138
return;
3239
}
3340
await botCommands.send({
@@ -70,6 +77,8 @@ export const scheduleReminder = async (
7077
return;
7178
}
7279

80+
// This will not be perfectly accurate, and if we ever scale to multiple instances of the bot, we'll need to
81+
// use a more robust system to avoid duplicate reminders.
7382
const job = scheduleJob(
7483
{
7584
hour: time.getHours(),
@@ -88,13 +97,23 @@ export const scheduleReminder = async (
8897

8998
export const scheduleAllReminders = async (client: Client) => {
9099
const guild = await client.guilds.fetch(config.guildId);
91-
const list = await guild.members.fetch();
92-
logger.debug(`Scheduling reminders for ${list.size} members`);
100+
const usersWithDaily = await DDUser.findAll({
101+
where: {
102+
lastDailyTime: {
103+
[Op.ne]: undefined,
104+
},
105+
},
106+
});
107+
108+
logger.debug(`Scheduling reminders for ${usersWithDaily.length} members`);
93109

94-
for (const member of Array.from(list.values()).filter(isSpecialUser)) {
95-
const ddUser = await getOrCreateUserById(BigInt(member.id));
110+
for (const ddUser of usersWithDaily) {
111+
const member = await guild.members.fetch(ddUser.id.toString());
112+
if (!member || !isSpecialUser(member)) {
113+
continue;
114+
}
96115
await scheduleReminder(client, member, ddUser);
97116
}
98117
};
99118

100-
const scheduledReminders = new Map<bigint, Job>();
119+
export const scheduledReminders = new Map<bigint, Job>();

src/store/models/Bump.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,13 @@ export function getStreak(
7676
let currentStreak = 0;
7777
let highestStreak = 0;
7878
for (const streak of bumpStreaks) {
79-
console.debug(`Streak for user ${userId}:`, streak);
80-
8179
if (streak[0]!.userId === userId) {
8280
currentStreak += streak.length;
83-
console.debug(`Current streak for user ${userId}:`, currentStreak);
8481
} else {
8582
highestStreak = Math.max(highestStreak, currentStreak);
86-
console.debug(
87-
`Reset current streak for user at ${userId}:`,
88-
currentStreak,
89-
);
9083
currentStreak = 0;
9184
}
9285
highestStreak = Math.max(highestStreak, currentStreak);
93-
console.debug(`Highest streak for user ${userId}:`, highestStreak);
9486
}
9587
return {
9688
current: currentStreak,

0 commit comments

Comments
 (0)