Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6056111
feat: add friends (@fehmer)
fehmer Jun 24, 2025
dd8ebf1
add dal
fehmer Jun 24, 2025
f513f74
add tests
fehmer Jun 24, 2025
9b0524d
Merge branch 'master' into feature/friends-list
fehmer Jun 24, 2025
5ef4c23
Merge branch 'master' into feature/friends-list
fehmer Jul 1, 2025
b446ba8
move friends requests to /friends/requests, change rejected to blocked
fehmer Jul 2, 2025
fdbc26c
move configuration top level, add max friends per user
fehmer Jul 2, 2025
f1e3822
don't add new friend requests to inbox
fehmer Jul 2, 2025
358cfff
filter friend requests by status and type
fehmer Jul 2, 2025
59a644f
Merge branch 'master' into feature/friends-list
fehmer Jul 7, 2025
9a8e678
allow friend to delete request if not blocked
fehmer Jul 7, 2025
0407a9f
add friends list endpoint
fehmer Jul 11, 2025
218ed10
add selectedBadgeId to friends list
fehmer Jul 14, 2025
477cbb4
add userflags to Friends
fehmer Jul 17, 2025
0338368
Merge branch 'master' into feature/friends-list
fehmer Jul 17, 2025
68276b7
fix PresetsDal test
fehmer Jul 17, 2025
ad05fc5
Merge branch 'master' into feature/friends-list
fehmer Jul 18, 2025
e4356c5
Merge remote-tracking branch 'upstream/master' into feature/friends-list
fehmer Jul 25, 2025
3ca2a33
fix schema split
fehmer Jul 25, 2025
e0f2aff
fix friend request deletion rule
fehmer Jul 26, 2025
15e59a3
Merge remote-tracking branch 'upstream/master' into feature/friends-list
fehmer Aug 4, 2025
421df44
vitest3
fehmer Aug 4, 2025
f4e6ecc
Merge branch 'master' into feature/friends-list
fehmer Aug 7, 2025
ad30e1f
Merge branch 'master' into feature/friends-list
fehmer Aug 18, 2025
3e1df8f
update tests
fehmer Aug 18, 2025
936125d
Merge branch 'master' into feature/friends-list
fehmer Aug 18, 2025
1b4777c
Merge branch 'master' into feature/friends-list
fehmer Aug 24, 2025
6f373dd
Merge branch 'master' into feature/friends-list
fehmer Aug 27, 2025
ba728a3
feat(leaderboard): add friends leaderboards (@fehmer)
fehmer Jul 27, 2025
56bd480
fixes
fehmer Jul 27, 2025
800aa21
add friendsRank
fehmer Jul 29, 2025
e40a95b
add friendsrank
fehmer Jul 29, 2025
8cac132
hide filter if friends feature is disabled, add friendsOnly to rank e…
fehmer Aug 1, 2025
fd87fc0
fix tests
fehmer Aug 4, 2025
beb58f7
add friendsOnly to daily lb
fehmer Aug 6, 2025
0e250a3
group results, count and minWpm into one redis call
fehmer Aug 6, 2025
db8ebc5
tests
fehmer Aug 6, 2025
d4c80ee
daily rank
fehmer Aug 7, 2025
eb6265f
add friendsRank to daily rank
fehmer Aug 7, 2025
64e731b
tests
fehmer Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
448 changes: 448 additions & 0 deletions backend/__tests__/__integration__/dal/friends.spec.ts

Large diffs are not rendered by default.

170 changes: 163 additions & 7 deletions backend/__tests__/__integration__/dal/leaderboards.isolated.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ describe("LeaderboardsDal", () => {

//WHEN
await LeaderboardsDal.update("time", "15", "english");
const result = (await LeaderboardsDal.get(
const results = (await LeaderboardsDal.get(
"time",
"15",
"english",
Expand All @@ -200,7 +200,7 @@ describe("LeaderboardsDal", () => {
)) as DBLeaderboardEntry[];

//THEN
const lb = result.map((it) => _.omit(it, ["_id"]));
const lb = results.map((it) => _.omit(it, ["_id"]));

expect(lb).toEqual([
expectedLbEntry("15", { rank: 1, user: noBadge }),
Expand Down Expand Up @@ -229,7 +229,7 @@ describe("LeaderboardsDal", () => {
//WHEN
await LeaderboardsDal.update("time", "15", "english");

const result = (await LeaderboardsDal.get(
const results = (await LeaderboardsDal.get(
"time",
"15",
"english",
Expand All @@ -239,7 +239,7 @@ describe("LeaderboardsDal", () => {
)) as DBLeaderboardEntry[];

//THEN
const lb = result.map((it) => _.omit(it, ["_id"]));
const lb = results.map((it) => _.omit(it, ["_id"]));

expect(lb).toEqual([
expectedLbEntry("15", { rank: 1, user: noPremium }),
Expand All @@ -262,7 +262,7 @@ describe("LeaderboardsDal", () => {

//WHEN
await LeaderboardsDal.update("time", "15", "english");
const result = (await LeaderboardsDal.get(
const results = (await LeaderboardsDal.get(
"time",
"15",
"english",
Expand All @@ -272,14 +272,168 @@ describe("LeaderboardsDal", () => {
)) as DBLeaderboardEntry[];

//THEN
expect(result[0]?.isPremium).toBeUndefined();
expect(results[0]?.isPremium).toBeUndefined();
});
});
describe("get", () => {
it("should get for page", async () => {
//GIVEN
const _rank1 = await createUser(lbBests(pb(90), pb(105, 90, 2)));
const _rank2 = await createUser(lbBests(undefined, pb(100, 90, 1)));
const rank3 = await createUser(lbBests(undefined, pb(95, 80, 2)));
const rank4 = await createUser(lbBests(undefined, pb(90, 100, 1)));
await LeaderboardsDal.update("time", "60", "english");

//WHEN

const results = (await LeaderboardsDal.get(
"time",
"60",
"english",
1,
2,
true
)) as LeaderboardsDal.DBLeaderboardEntry[];

//THEN
const lb = results.map((it) => _.omit(it, ["_id"]));

expect(lb).toEqual([
expectedLbEntry("60", { rank: 3, user: rank3 }),
expectedLbEntry("60", { rank: 4, user: rank4 }),
]);
});
it("should get for friends only", async () => {
//GIVEN
const rank1 = await createUser(lbBests(pb(90), pb(100, 90, 2)));
const _rank2 = await createUser(lbBests(undefined, pb(100, 90, 1)));
const _rank3 = await createUser(lbBests(undefined, pb(100, 80, 2)));
const rank4 = await createUser(lbBests(undefined, pb(90, 100, 1)));
await LeaderboardsDal.update("time", "60", "english");

//WHEN

const results = (await LeaderboardsDal.get(
"time",
"60",
"english",
0,
50,
false,
[rank1.uid, rank4.uid]
)) as LeaderboardsDal.DBLeaderboardEntry[];

//THEN
const lb = results.map((it) => _.omit(it, ["_id"]));

expect(lb).toEqual([
expectedLbEntry("60", { rank: 1, user: rank1, friendsRank: 1 }),
expectedLbEntry("60", { rank: 4, user: rank4, friendsRank: 2 }),
]);
});
it("should get for friends only with page", async () => {
//GIVEN
const rank1 = await createUser(lbBests(pb(90), pb(105, 90, 2)));
const rank2 = await createUser(lbBests(undefined, pb(100, 90, 1)));
const _rank3 = await createUser(lbBests(undefined, pb(95, 80, 2)));
const rank4 = await createUser(lbBests(undefined, pb(90, 100, 1)));
await LeaderboardsDal.update("time", "60", "english");

//WHEN
const results = (await LeaderboardsDal.get(
"time",
"60",
"english",
1,
2,
false,
[rank1.uid, rank2.uid, rank4.uid]
)) as LeaderboardsDal.DBLeaderboardEntry[];

//THEN
const lb = results.map((it) => _.omit(it, ["_id"]));

expect(lb).toEqual([
expectedLbEntry("60", { rank: 4, user: rank4, friendsRank: 3 }),
]);
});
it("should return empty list if no friends", async () => {
//GIVEN

//WHEN
const results = (await LeaderboardsDal.get(
"time",
"60",
"english",
1,
2,
false,
[]
)) as LeaderboardsDal.DBLeaderboardEntry[];
//THEN
expect(results).toEqual([]);
});
});
describe("getCount / getRank", () => {
it("should get count", async () => {
//GIVEN
await createUser(lbBests(undefined, pb(105)));
await createUser(lbBests(undefined, pb(100)));
const me = await createUser(lbBests(undefined, pb(95)));
await createUser(lbBests(undefined, pb(90)));
await LeaderboardsDal.update("time", "60", "english");

//WHEN / THEN

expect(await LeaderboardsDal.getCount("time", "60", "english")) //
.toEqual(4);
expect(await LeaderboardsDal.getRank("time", "60", "english", me.uid)) //
.toEqual(
expect.objectContaining({
wpm: 95,
rank: 3,
name: me.name,
uid: me.uid,
})
);
});
it("should get for friends only", async () => {
//GIVEN
const friendOne = await createUser(lbBests(undefined, pb(105)));
await createUser(lbBests(undefined, pb(100)));
await createUser(lbBests(undefined, pb(95)));
const friendTwo = await createUser(lbBests(undefined, pb(90)));
const me = await createUser(lbBests(undefined, pb(99)));

console.log("me", me.uid);

await LeaderboardsDal.update("time", "60", "english");

const friends = [friendOne.uid, friendTwo.uid, me.uid];

//WHEN / THEN

expect(await LeaderboardsDal.getCount("time", "60", "english", friends)) //
.toEqual(3);
expect(
await LeaderboardsDal.getRank("time", "60", "english", me.uid, friends)
) //
.toEqual(
expect.objectContaining({
wpm: 99,
rank: 3,
friendsRank: 2,
name: me.name,
uid: me.uid,
})
);
});
});
});

function expectedLbEntry(
time: string,
{ rank, user, badgeId, isPremium }: ExpectedLbEntry
{ rank, user, badgeId, isPremium, friendsRank }: ExpectedLbEntry
) {
// @ts-expect-error
const lbBest: PersonalBest =
Expand All @@ -299,6 +453,7 @@ function expectedLbEntry(
discordAvatar: user.discordAvatar,
badgeId,
isPremium,
friendsRank,
};
}

Expand Down Expand Up @@ -351,4 +506,5 @@ type ExpectedLbEntry = {
user: UserDal.DBUser;
badgeId?: number;
isPremium?: boolean;
friendsRank?: number;
};
2 changes: 1 addition & 1 deletion backend/__tests__/__integration__/dal/preset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ describe("PresetDal", () => {
).presetId;

//WHEN
PresetDal.removePreset(uid, first);
await PresetDal.removePreset(uid, first);

//THEN
const read = await PresetDal.getPresets(uid);
Expand Down
Loading