Skip to content

Commit d885e70

Browse files
fehmerMiodec
andauthored
feat: add friend requests and list (@fehmer) (monkeytypegame#6658)
make some friends on monkeytype --------- Co-authored-by: Miodec <[email protected]>
1 parent 9aa30a2 commit d885e70

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3338
-118
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
import {
2+
describe,
3+
it,
4+
expect,
5+
vi,
6+
beforeAll,
7+
beforeEach,
8+
afterEach,
9+
} from "vitest";
10+
import { ObjectId } from "mongodb";
11+
12+
import * as ConnectionsDal from "../../../src/dal/connections";
13+
import { createConnection } from "../../__testData__/connections";
14+
15+
describe("ConnectionsDal", () => {
16+
beforeAll(async () => {
17+
await ConnectionsDal.createIndicies();
18+
});
19+
20+
describe("getRequests", () => {
21+
it("get by uid", async () => {
22+
//GIVEN
23+
const uid = new ObjectId().toHexString();
24+
const initOne = await createConnection({ initiatorUid: uid });
25+
const initTwo = await createConnection({ initiatorUid: uid });
26+
const friendOne = await createConnection({ receiverUid: uid });
27+
const _decoy = await createConnection({});
28+
29+
//WHEN / THEM
30+
31+
expect(
32+
await ConnectionsDal.getConnections({
33+
initiatorUid: uid,
34+
receiverUid: uid,
35+
})
36+
).toStrictEqual([initOne, initTwo, friendOne]);
37+
});
38+
39+
it("get by uid and status", async () => {
40+
//GIVEN
41+
const uid = new ObjectId().toHexString();
42+
const initAccepted = await createConnection({
43+
initiatorUid: uid,
44+
status: "accepted",
45+
});
46+
const _initPending = await createConnection({
47+
initiatorUid: uid,
48+
status: "pending",
49+
});
50+
const initBlocked = await createConnection({
51+
initiatorUid: uid,
52+
status: "blocked",
53+
});
54+
55+
const friendAccepted = await createConnection({
56+
receiverUid: uid,
57+
status: "accepted",
58+
});
59+
const _friendPending = await createConnection({
60+
receiverUid: uid,
61+
status: "pending",
62+
});
63+
64+
const _decoy = await createConnection({ status: "accepted" });
65+
66+
//WHEN / THEN
67+
68+
expect(
69+
await ConnectionsDal.getConnections({
70+
initiatorUid: uid,
71+
receiverUid: uid,
72+
status: ["accepted", "blocked"],
73+
})
74+
).toStrictEqual([initAccepted, initBlocked, friendAccepted]);
75+
});
76+
});
77+
78+
describe("create", () => {
79+
const now = 1715082588;
80+
beforeEach(() => {
81+
vi.useFakeTimers();
82+
vi.setSystemTime(now);
83+
});
84+
afterEach(() => {
85+
vi.useRealTimers();
86+
});
87+
88+
it("should fail creating duplicates", async () => {
89+
//GIVEN
90+
const uid = new ObjectId().toHexString();
91+
const first = await createConnection({
92+
initiatorUid: uid,
93+
});
94+
95+
//WHEN/THEN
96+
await expect(
97+
createConnection({
98+
initiatorUid: first.receiverUid,
99+
receiverUid: uid,
100+
})
101+
).rejects.toThrow("Connection request already sent");
102+
});
103+
104+
it("should create", async () => {
105+
//GIVEN
106+
const uid = new ObjectId().toHexString();
107+
const receiverUid = new ObjectId().toHexString();
108+
109+
//WHEN
110+
const created = await ConnectionsDal.create(
111+
{ uid, name: "Bob" },
112+
{ uid: receiverUid, name: "Kevin" },
113+
2
114+
);
115+
116+
//THEN
117+
expect(created).toEqual({
118+
_id: created._id,
119+
initiatorUid: uid,
120+
initiatorName: "Bob",
121+
receiverUid: receiverUid,
122+
receiverName: "Kevin",
123+
lastModified: now,
124+
status: "pending",
125+
key: `${uid}/${receiverUid}`,
126+
});
127+
});
128+
129+
it("should fail if maximum connections are reached", async () => {
130+
//GIVEN
131+
const initiatorUid = new ObjectId().toHexString();
132+
await createConnection({ initiatorUid });
133+
await createConnection({ initiatorUid });
134+
135+
//WHEN / THEM
136+
await expect(createConnection({ initiatorUid }, 2)).rejects.toThrow(
137+
"Maximum number of connections reached\nStack: create connection request"
138+
);
139+
});
140+
141+
it("should fail creating if blocked", async () => {
142+
//GIVEN
143+
const uid = new ObjectId().toHexString();
144+
const first = await createConnection({
145+
initiatorUid: uid,
146+
status: "blocked",
147+
});
148+
149+
//WHEN/THEN
150+
await expect(
151+
createConnection({
152+
initiatorUid: first.receiverUid,
153+
receiverUid: uid,
154+
})
155+
).rejects.toThrow("Connection blocked");
156+
});
157+
});
158+
describe("updateStatus", () => {
159+
const now = 1715082588;
160+
beforeEach(() => {
161+
vi.useFakeTimers();
162+
vi.setSystemTime(now);
163+
});
164+
afterEach(() => {
165+
vi.useRealTimers();
166+
});
167+
it("should update the status", async () => {
168+
//GIVEN
169+
const uid = new ObjectId().toHexString();
170+
const first = await createConnection({
171+
receiverUid: uid,
172+
lastModified: 100,
173+
});
174+
const second = await createConnection({
175+
receiverUid: uid,
176+
lastModified: 200,
177+
});
178+
179+
//WHEN
180+
await ConnectionsDal.updateStatus(
181+
uid,
182+
first._id.toHexString(),
183+
"accepted"
184+
);
185+
186+
//THEN
187+
expect(await ConnectionsDal.getConnections({ receiverUid: uid })).toEqual(
188+
[{ ...first, status: "accepted", lastModified: now }, second]
189+
);
190+
191+
//can update twice to the same status
192+
await ConnectionsDal.updateStatus(
193+
uid,
194+
first._id.toHexString(),
195+
"accepted"
196+
);
197+
});
198+
it("should fail if uid does not match the reeceiverUid", async () => {
199+
//GIVEN
200+
const uid = new ObjectId().toHexString();
201+
const first = await createConnection({
202+
initiatorUid: uid,
203+
});
204+
205+
//WHEN / THEN
206+
await expect(
207+
ConnectionsDal.updateStatus(uid, first._id.toHexString(), "accepted")
208+
).rejects.toThrow("No permission or connection not found");
209+
});
210+
});
211+
212+
describe("deleteById", () => {
213+
it("should delete by initiator", async () => {
214+
//GIVEN
215+
const uid = new ObjectId().toHexString();
216+
const first = await createConnection({
217+
initiatorUid: uid,
218+
});
219+
const second = await createConnection({
220+
initiatorUid: uid,
221+
});
222+
223+
//WHEN
224+
await ConnectionsDal.deleteById(uid, first._id.toHexString());
225+
226+
//THEN
227+
expect(
228+
await ConnectionsDal.getConnections({ initiatorUid: uid })
229+
).toStrictEqual([second]);
230+
});
231+
232+
it("should delete by receiver", async () => {
233+
//GIVEN
234+
const uid = new ObjectId().toHexString();
235+
const first = await createConnection({
236+
receiverUid: uid,
237+
});
238+
const second = await createConnection({
239+
receiverUid: uid,
240+
status: "accepted",
241+
});
242+
243+
//WHEN
244+
await ConnectionsDal.deleteById(uid, first._id.toHexString());
245+
246+
//THEN
247+
expect(
248+
await ConnectionsDal.getConnections({
249+
initiatorUid: second.initiatorUid,
250+
})
251+
).toStrictEqual([second]);
252+
});
253+
254+
it("should fail if uid does not match", async () => {
255+
//GIVEN
256+
const uid = new ObjectId().toHexString();
257+
const first = await createConnection({
258+
initiatorUid: uid,
259+
});
260+
261+
//WHEN / THEN
262+
await expect(
263+
ConnectionsDal.deleteById("Bob", first._id.toHexString())
264+
).rejects.toThrow("No permission or connection not found");
265+
});
266+
267+
it("should fail if initiator deletes blocked by receiver", async () => {
268+
//GIVEN
269+
const uid = new ObjectId().toHexString();
270+
const myRequestWasBlocked = await createConnection({
271+
initiatorName: uid,
272+
status: "blocked",
273+
});
274+
275+
//WHEN / THEN
276+
await expect(
277+
ConnectionsDal.deleteById(uid, myRequestWasBlocked._id.toHexString())
278+
).rejects.toThrow("No permission or connection not found");
279+
});
280+
it("allow receiver to delete blocked", async () => {
281+
//GIVEN
282+
const uid = new ObjectId().toHexString();
283+
const myBlockedUser = await createConnection({
284+
receiverUid: uid,
285+
status: "blocked",
286+
});
287+
288+
//WHEN
289+
await ConnectionsDal.deleteById(uid, myBlockedUser._id.toHexString());
290+
291+
//THEN
292+
expect(await ConnectionsDal.getConnections({ receiverUid: uid })).toEqual(
293+
[]
294+
);
295+
});
296+
});
297+
298+
describe("deleteByUid", () => {
299+
it("should delete by uid", async () => {
300+
//GIVEN
301+
const uid = new ObjectId().toHexString();
302+
const _initOne = await createConnection({ initiatorUid: uid });
303+
const _initTwo = await createConnection({ initiatorUid: uid });
304+
const _friendOne = await createConnection({ receiverUid: uid });
305+
const decoy = await createConnection({});
306+
307+
//WHEN
308+
await ConnectionsDal.deleteByUid(uid);
309+
310+
//THEN
311+
expect(
312+
await ConnectionsDal.getConnections({
313+
initiatorUid: uid,
314+
receiverUid: uid,
315+
})
316+
).toEqual([]);
317+
318+
expect(
319+
await ConnectionsDal.getConnections({
320+
initiatorUid: decoy.initiatorUid,
321+
})
322+
).toEqual([decoy]);
323+
});
324+
});
325+
describe("updateName", () => {
326+
it("should update the name", async () => {
327+
//GIVEN
328+
const uid = new ObjectId().toHexString();
329+
const initOne = await createConnection({
330+
initiatorUid: uid,
331+
initiatorName: "Bob",
332+
});
333+
const initTwo = await createConnection({
334+
initiatorUid: uid,
335+
initiatorName: "Bob",
336+
});
337+
const friendOne = await createConnection({
338+
receiverUid: uid,
339+
receiverName: "Bob",
340+
});
341+
const decoy = await createConnection({});
342+
343+
//WHEN
344+
await ConnectionsDal.updateName(uid, "King Bob");
345+
346+
//THEN
347+
expect(
348+
await ConnectionsDal.getConnections({
349+
initiatorUid: uid,
350+
receiverUid: uid,
351+
})
352+
).toEqual([
353+
{ ...initOne, initiatorName: "King Bob" },
354+
{ ...initTwo, initiatorName: "King Bob" },
355+
{ ...friendOne, receiverName: "King Bob" },
356+
]);
357+
358+
expect(
359+
await ConnectionsDal.getConnections({
360+
initiatorUid: decoy.initiatorUid,
361+
})
362+
).toEqual([decoy]);
363+
});
364+
});
365+
});

backend/__tests__/__integration__/dal/preset.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ describe("PresetDal", () => {
382382
).presetId;
383383

384384
//WHEN
385-
PresetDal.removePreset(uid, first);
385+
await PresetDal.removePreset(uid, first);
386386

387387
//THEN
388388
const read = await PresetDal.getPresets(uid);

0 commit comments

Comments
 (0)