test: add WeeklyXpLeaderboards integration tests (@fehmer) (#6843)

This commit is contained in:
Christian Fehmer 2025-08-07 14:48:14 +02:00 committed by GitHub
parent c8a91ede52
commit cd99d5af16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 211 additions and 12 deletions

View file

@ -0,0 +1,11 @@
import { getConnection, connect } from "../../src/init/redis";
export async function redisSetup(): Promise<void> {
await connect();
}
export async function cleanupKeys(prefix: string): Promise<void> {
// oxlint-disable-next-line no-non-null-assertion
const connection = getConnection()!;
const keys = await connection.keys(`${prefix}*`);
await Promise.all(keys?.map((it) => connection.del(it)));
}

View file

@ -0,0 +1,182 @@
import * as WeeklyXpLeaderboard from "../../../src/services/weekly-xp-leaderboard";
import { Configuration } from "@monkeytype/schemas/configuration";
import { ObjectId } from "mongodb";
import { RedisXpLeaderboardEntry } from "@monkeytype/schemas/leaderboards";
import { cleanupKeys, redisSetup } from "../redis";
const leaderboardsConfig: Configuration["leaderboards"]["weeklyXp"] = {
enabled: true,
expirationTimeInDays: 7,
xpRewardBrackets: [],
};
describe("Weekly XP Leaderboards", () => {
beforeAll(async () => {
await redisSetup();
});
afterEach(async () => {
await cleanupKeys(WeeklyXpLeaderboard.__testing.namespace);
});
describe("get", () => {
it("should get if enabled", () => {
expect(WeeklyXpLeaderboard.get(leaderboardsConfig)).toBeInstanceOf(
WeeklyXpLeaderboard.WeeklyXpLeaderboard
);
});
it("should return null if disabled", () => {
expect(WeeklyXpLeaderboard.get({ enabled: false } as any)).toBeNull();
});
});
describe("WeeklyXpLeaderboard class", () => {
// oxlint-disable-next-line no-non-null-assertion
const lb = WeeklyXpLeaderboard.get(leaderboardsConfig)!;
describe("addResult", () => {
it("adds results for user", async () => {
//GIVEN
const user1 = await givenResult(100, { timeTypedSeconds: 5 });
await givenResult(50, { ...user1, timeTypedSeconds: 5 });
const user2 = await givenResult(100, {
isPremium: true,
timeTypedSeconds: 7,
});
//WHEN
const results = await lb.getResults(0, 10, leaderboardsConfig, true);
//THEN
expect(results).toEqual([
{
...user1,
rank: 1,
timeTypedSeconds: 10,
totalXp: 150,
isPremium: false,
},
{
...user2,
rank: 2,
timeTypedSeconds: 7,
totalXp: 100,
isPremium: true,
},
]);
});
});
describe("getResults", () => {
it("gets results", async () => {
//GIVEN
const user1 = await givenResult(150);
const user2 = await givenResult(100);
//WHEN
const results = await lb.getResults(0, 10, leaderboardsConfig, true);
//THEN
expect(results).toEqual([
{ rank: 1, totalXp: 150, ...user1 },
{ rank: 2, totalXp: 100, ...user2 },
]);
});
it("gets results for page", async () => {
//GIVEN
const _user1 = await givenResult(100);
const _user2 = await givenResult(75);
const user3 = await givenResult(50);
const user4 = await givenResult(25);
//WHEN
const results = await lb.getResults(1, 2, leaderboardsConfig, true);
//THEN
expect(results).toEqual([
{ rank: 3, totalXp: 50, ...user3 },
{ rank: 4, totalXp: 25, ...user4 },
]);
});
it("gets results without premium", async () => {
//GIVEN
const user1 = await givenResult(150, { isPremium: true });
const user2 = await givenResult(100);
//WHEN
const results = await lb.getResults(0, 10, leaderboardsConfig, false);
//THEN
expect(results).toEqual([
{ rank: 1, totalXp: 150, ...user1, isPremium: undefined },
{ rank: 2, totalXp: 100, ...user2, isPremium: undefined },
]);
});
});
describe("getRank", () => {
it("gets rank", async () => {
//GIVEN
const user1 = await givenResult(100);
const _user2 = await givenResult(150);
//WHEN
const rank = await lb.getRank(user1.uid, leaderboardsConfig);
//THEN
expect(rank).toEqual({ rank: 2, totalXp: 100, ...user1 });
});
});
describe("getCount", () => {
it("gets count", async () => {
//GIVEN
await givenResult(100);
await givenResult(150);
//WHEN
const count = await lb.getCount();
//THEN
expect(count).toEqual(2);
});
});
it("purgeUserFromDailyLeaderboards", async () => {
//GIVEN
const cheater = await givenResult(50);
const validUser = await givenResult(1000);
//WHEN
await WeeklyXpLeaderboard.purgeUserFromXpLeaderboards(
cheater.uid,
leaderboardsConfig
);
//THEN
expect(await lb.getRank(cheater.uid, leaderboardsConfig)).toBeNull();
expect(await lb.getResults(0, 50, leaderboardsConfig, true)).toEqual([
{ rank: 1, totalXp: 1000, ...validUser },
]);
});
async function givenResult(
xpGained: number,
entry?: Partial<RedisXpLeaderboardEntry>
): Promise<RedisXpLeaderboardEntry> {
const uid = new ObjectId().toHexString();
const result: RedisXpLeaderboardEntry = {
uid,
name: `User ${uid}`,
lastActivityTimestamp: Date.now(),
timeTypedSeconds: 42,
badgeId: 2,
discordAvatar: `${uid}Avatar`,
discordId: `${uid}DiscordId`,
isPremium: false,
...entry,
};
await lb.addResult(leaderboardsConfig, { xpGained, entry: result });
return result;
}
});
});

View file

@ -1,12 +1,13 @@
import { Mode, Mode2 } from "@monkeytype/schemas/shared";
import * as DailyLeaderboards from "../../../src/utils/daily-leaderboards";
import { getConnection, connect as redisSetup } from "../../../src/init/redis";
import { cleanupKeys, redisSetup } from "../redis";
import { Language } from "@monkeytype/schemas/languages";
import { RedisDailyLeaderboardEntry } from "@monkeytype/schemas/leaderboards";
import { ObjectId } from "mongodb";
import { Configuration } from "@monkeytype/schemas/configuration";
const dailyLeaderboardsConfig = {
const dailyLeaderboardsConfig: Configuration["dailyLeaderboards"] = {
enabled: true,
maxResults: 10,
leaderboardExpirationTimeInDays: 1,
@ -32,7 +33,7 @@ describe("Daily Leaderboards", () => {
await redisSetup();
});
afterEach(async () => {
await getConnection()?.flushall();
await cleanupKeys(DailyLeaderboards.__testing.namespace);
});
describe("should properly handle valid and invalid modes", () => {
const testCases: {
@ -256,8 +257,7 @@ describe("Daily Leaderboards", () => {
it("purgeUserFromDailyLeaderboards", async () => {
//GIVEN
const cheater = await givenResult({ wpm: 50 });
await givenResult({ wpm: 60 });
await givenResult({ wpm: 40 });
const validUser = await givenResult();
//WHEN
await DailyLeaderboards.purgeUserFromDailyLeaderboards(
@ -266,15 +266,13 @@ describe("Daily Leaderboards", () => {
);
//THEN
expect(await lb.getRank(cheater.uid, dailyLeaderboardsConfig)).toBeNull();
expect(
(await lb.getResults(0, 50, dailyLeaderboardsConfig, false)).filter(
(it) => it.uid === cheater.uid
)
).toEqual([]);
expect(await lb.getResults(0, 50, dailyLeaderboardsConfig, true)).toEqual(
[{ rank: 1, ...validUser }]
);
});
async function givenResult(
entry: Partial<RedisDailyLeaderboardEntry>
entry?: Partial<RedisDailyLeaderboardEntry>
): Promise<RedisDailyLeaderboardEntry> {
const uid = new ObjectId().toHexString();
const result = {

View file

@ -13,7 +13,7 @@ import { omit } from "lodash";
import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
import { tryCatchSync } from "@monkeytype/util/trycatch";
type AddResultOpts = {
export type AddResultOpts = {
entry: RedisXpLeaderboardEntry;
xpGained: RedisXpLeaderboardScore;
};
@ -285,3 +285,7 @@ export async function purgeUserFromXpLeaderboards(
weeklyXpLeaderboardLeaderboardNamespace
);
}
export const __testing = {
namespace: weeklyXpLeaderboardLeaderboardNamespace,
};

View file

@ -298,3 +298,7 @@ export function getDailyLeaderboard(
return new DailyLeaderboard(modeRule, customTimestamp);
}
export const __testing = {
namespace: dailyLeaderboardNamespace,
};