]> Untitled Git - lemmy.git/blob - api_tests/src/comment.spec.ts
Federation tests replication round1 - demonstrate absent replication of comment delet...
[lemmy.git] / api_tests / src / comment.spec.ts
1 jest.setTimeout(180000);
2
3 import { PostResponse } from "lemmy-js-client/dist/types/PostResponse";
4 import {
5   alpha,
6   beta,
7   gamma,
8   setupLogins,
9   createPost,
10   getPost,
11   resolveComment,
12   likeComment,
13   followBeta,
14   resolveBetaCommunity,
15   createComment,
16   editComment,
17   deleteComment,
18   removeComment,
19   getMentions,
20   resolvePost,
21   unfollowRemotes,
22   createCommunity,
23   registerUser,
24   reportComment,
25   listCommentReports,
26   randomString,
27   API,
28   unfollows,
29   getComments,
30   getCommentParentId,
31   resolveCommunity,
32   getPersonDetails,
33 } from "./shared";
34 import { CommentView } from "lemmy-js-client/dist/types/CommentView";
35
36 let postRes: PostResponse;
37
38 beforeAll(async () => {
39   await setupLogins();
40   await unfollows();
41   await followBeta(alpha);
42   await followBeta(gamma);
43   let betaCommunity = (await resolveBetaCommunity(alpha)).community;
44   if (betaCommunity) {
45     postRes = await createPost(alpha, betaCommunity.community.id);
46   }
47 });
48
49 afterAll(async () => {
50   await unfollows();
51 });
52
53 function assertCommentFederation(
54   commentOne?: CommentView,
55   commentTwo?: CommentView,
56 ) {
57   expect(commentOne?.comment.ap_id).toBe(commentTwo?.comment.ap_id);
58   expect(commentOne?.comment.content).toBe(commentTwo?.comment.content);
59   expect(commentOne?.creator.name).toBe(commentTwo?.creator.name);
60   expect(commentOne?.community.actor_id).toBe(commentTwo?.community.actor_id);
61   expect(commentOne?.comment.published).toBe(commentTwo?.comment.published);
62   expect(commentOne?.comment.updated).toBe(commentOne?.comment.updated);
63   expect(commentOne?.comment.deleted).toBe(commentOne?.comment.deleted);
64   expect(commentOne?.comment.removed).toBe(commentOne?.comment.removed);
65 }
66
67 test("Create a comment", async () => {
68   let commentRes = await createComment(alpha, postRes.post_view.post.id);
69   expect(commentRes.comment_view.comment.content).toBeDefined();
70   expect(commentRes.comment_view.community.local).toBe(false);
71   expect(commentRes.comment_view.creator.local).toBe(true);
72   expect(commentRes.comment_view.counts.score).toBe(1);
73
74   // Make sure that comment is liked on beta
75   let betaComment = (
76     await resolveComment(beta, commentRes.comment_view.comment)
77   ).comment;
78   expect(betaComment).toBeDefined();
79   expect(betaComment?.community.local).toBe(true);
80   expect(betaComment?.creator.local).toBe(false);
81   expect(betaComment?.counts.score).toBe(1);
82   assertCommentFederation(betaComment, commentRes.comment_view);
83 });
84
85 test("Create a comment in a non-existent post", async () => {
86   await expect(createComment(alpha, -1)).rejects.toBe("couldnt_find_post");
87 });
88
89 test("Update a comment", async () => {
90   let commentRes = await createComment(alpha, postRes.post_view.post.id);
91   // Federate the comment first
92   let betaComment = (
93     await resolveComment(beta, commentRes.comment_view.comment)
94   ).comment;
95   assertCommentFederation(betaComment, commentRes.comment_view);
96
97   let updateCommentRes = await editComment(
98     alpha,
99     commentRes.comment_view.comment.id,
100   );
101   expect(updateCommentRes.comment_view.comment.content).toBe(
102     "A jest test federated comment update",
103   );
104   expect(updateCommentRes.comment_view.community.local).toBe(false);
105   expect(updateCommentRes.comment_view.creator.local).toBe(true);
106
107   // Make sure that post is updated on beta
108   let betaCommentUpdated = (
109     await resolveComment(beta, commentRes.comment_view.comment)
110   ).comment;
111   assertCommentFederation(betaCommentUpdated, updateCommentRes.comment_view);
112 });
113
114 test("Delete a comment", async () => {
115   // creating a comment on alpha (remote from home of community)
116   let commentRes = await createComment(alpha, postRes.post_view.post.id);
117
118   // Find the comment on beta (home of community)
119   let betaComment = (
120     await resolveComment(beta, commentRes.comment_view.comment)
121   ).comment;
122
123   if (!betaComment) {
124     throw "Missing beta comment before delete";
125   }
126
127   // Find the comment on remote instance gamma
128   let gammaComment = (
129     await resolveComment(gamma, commentRes.comment_view.comment)
130   ).comment;
131
132   if (!gammaComment) {
133     throw "Missing gamma comment (remote-home-remote replication) before delete";
134   }
135
136   let deleteCommentRes = await deleteComment(
137     alpha,
138     true,
139     commentRes.comment_view.comment.id,
140   );
141   expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
142
143   // Make sure that comment is undefined on beta
144   await expect(
145     resolveComment(beta, commentRes.comment_view.comment),
146   ).rejects.toBe("couldnt_find_object");
147
148   // Make sure that comment is undefined on gamma after delete
149   await expect(
150     resolveComment(gamma, commentRes.comment_view.comment),
151   ).rejects.toBe("couldnt_find_object");
152
153   // Test undeleting the comment
154   let undeleteCommentRes = await deleteComment(
155     alpha,
156     false,
157     commentRes.comment_view.comment.id,
158   );
159   expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false);
160
161   // Make sure that comment is undeleted on beta
162   let betaComment2 = (
163     await resolveComment(beta, commentRes.comment_view.comment)
164   ).comment;
165   expect(betaComment2?.comment.deleted).toBe(false);
166   assertCommentFederation(betaComment2, undeleteCommentRes.comment_view);
167 });
168
169 test.skip("Remove a comment from admin and community on the same instance", async () => {
170   let commentRes = await createComment(alpha, postRes.post_view.post.id);
171
172   // Get the id for beta
173   let betaCommentId = (
174     await resolveComment(beta, commentRes.comment_view.comment)
175   ).comment?.comment.id;
176
177   if (!betaCommentId) {
178     throw "beta comment id is missing";
179   }
180
181   // The beta admin removes it (the community lives on beta)
182   let removeCommentRes = await removeComment(beta, true, betaCommentId);
183   expect(removeCommentRes.comment_view.comment.removed).toBe(true);
184
185   // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
186   let refetchedPostComments = await getPersonDetails(
187     alpha,
188     commentRes.comment_view.comment.creator_id,
189   );
190   expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
191
192   let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
193   expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
194
195   // Make sure that comment is unremoved on beta
196   let refetchedPostComments2 = await getComments(
197     alpha,
198     postRes.post_view.post.id,
199   );
200   expect(refetchedPostComments2.comments[0].comment.removed).toBe(false);
201   assertCommentFederation(
202     refetchedPostComments2.comments[0],
203     unremoveCommentRes.comment_view,
204   );
205 });
206
207 test("Remove a comment from admin and community on different instance", async () => {
208   let alpha_user = await registerUser(alpha);
209   let newAlphaApi: API = {
210     client: alpha.client,
211     auth: alpha_user.jwt ?? "",
212   };
213
214   // New alpha user creates a community, post, and comment.
215   let newCommunity = await createCommunity(newAlphaApi);
216   let newPost = await createPost(
217     newAlphaApi,
218     newCommunity.community_view.community.id,
219   );
220   let commentRes = await createComment(newAlphaApi, newPost.post_view.post.id);
221   expect(commentRes.comment_view.comment.content).toBeDefined();
222
223   // Beta searches that to cache it, then removes it
224   let betaComment = (
225     await resolveComment(beta, commentRes.comment_view.comment)
226   ).comment;
227
228   if (!betaComment) {
229     throw "beta comment missing";
230   }
231
232   let removeCommentRes = await removeComment(
233     beta,
234     true,
235     betaComment.comment.id,
236   );
237   expect(removeCommentRes.comment_view.comment.removed).toBe(true);
238
239   // Make sure its not removed on alpha
240   let refetchedPostComments = await getComments(
241     alpha,
242     newPost.post_view.post.id,
243   );
244   expect(refetchedPostComments.comments[0].comment.removed).toBe(false);
245   assertCommentFederation(
246     refetchedPostComments.comments[0],
247     commentRes.comment_view,
248   );
249 });
250
251 test("Unlike a comment", async () => {
252   let commentRes = await createComment(alpha, postRes.post_view.post.id);
253
254   // Lemmy automatically creates 1 like (vote) by author of comment.
255   // Make sure that comment is liked (voted up) on gamma, downstream peer
256   // This is testing replication from remote-home-remote (alpha-beta-gamma)
257   let gammaComment1 = (
258     await resolveComment(gamma, commentRes.comment_view.comment)
259   ).comment;
260   expect(gammaComment1).toBeDefined();
261   expect(gammaComment1?.community.local).toBe(false);
262   expect(gammaComment1?.creator.local).toBe(false);
263   expect(gammaComment1?.counts.score).toBe(1);
264
265   let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
266   expect(unlike.comment_view.counts.score).toBe(0);
267
268   // Make sure that comment is unliked on beta
269   let betaComment = (
270     await resolveComment(beta, commentRes.comment_view.comment)
271   ).comment;
272   expect(betaComment).toBeDefined();
273   expect(betaComment?.community.local).toBe(true);
274   expect(betaComment?.creator.local).toBe(false);
275   expect(betaComment?.counts.score).toBe(0);
276
277   // Make sure that comment is unliked on gamma, downstream peer
278   // This is testing replication from remote-home-remote (alpha-beta-gamma)
279   let gammaComment = (
280     await resolveComment(gamma, commentRes.comment_view.comment)
281   ).comment;
282   expect(gammaComment).toBeDefined();
283   expect(gammaComment?.community.local).toBe(false);
284   expect(gammaComment?.creator.local).toBe(false);
285   expect(gammaComment?.counts.score).toBe(0);
286 });
287
288 test("Federated comment like", async () => {
289   let commentRes = await createComment(alpha, postRes.post_view.post.id);
290
291   // Find the comment on beta
292   let betaComment = (
293     await resolveComment(beta, commentRes.comment_view.comment)
294   ).comment;
295
296   if (!betaComment) {
297     throw "Missing beta comment";
298   }
299
300   let like = await likeComment(beta, 1, betaComment.comment);
301   expect(like.comment_view.counts.score).toBe(2);
302
303   // Get the post from alpha, check the likes
304   let postComments = await getComments(alpha, postRes.post_view.post.id);
305   expect(postComments.comments[0].counts.score).toBe(2);
306 });
307
308 test("Reply to a comment", async () => {
309   // Create a comment on alpha, find it on beta
310   let commentRes = await createComment(alpha, postRes.post_view.post.id);
311   let betaComment = (
312     await resolveComment(beta, commentRes.comment_view.comment)
313   ).comment;
314
315   if (!betaComment) {
316     throw "Missing beta comment";
317   }
318
319   // find that comment id on beta
320
321   // Reply from beta
322   let replyRes = await createComment(
323     beta,
324     betaComment.post.id,
325     betaComment.comment.id,
326   );
327   expect(replyRes.comment_view.comment.content).toBeDefined();
328   expect(replyRes.comment_view.community.local).toBe(true);
329   expect(replyRes.comment_view.creator.local).toBe(true);
330   expect(getCommentParentId(replyRes.comment_view.comment)).toBe(
331     betaComment.comment.id,
332   );
333   expect(replyRes.comment_view.counts.score).toBe(1);
334
335   // Make sure that comment is seen on alpha
336   // TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
337   // comment, isn't working.
338   // let searchAlpha = await searchComment(alpha, replyRes.comment);
339   let postComments = await getComments(alpha, postRes.post_view.post.id);
340   let alphaComment = postComments.comments[0];
341   expect(alphaComment.comment.content).toBeDefined();
342   expect(getCommentParentId(alphaComment.comment)).toBe(
343     postComments.comments[1].comment.id,
344   );
345   expect(alphaComment.community.local).toBe(false);
346   expect(alphaComment.creator.local).toBe(false);
347   expect(alphaComment.counts.score).toBe(1);
348   assertCommentFederation(alphaComment, replyRes.comment_view);
349 });
350
351 test("Mention beta", async () => {
352   // Create a mention on alpha
353   let mentionContent = "A test mention of @lemmy_beta@lemmy-beta:8551";
354   let commentRes = await createComment(alpha, postRes.post_view.post.id);
355   let mentionRes = await createComment(
356     alpha,
357     postRes.post_view.post.id,
358     commentRes.comment_view.comment.id,
359     mentionContent,
360   );
361   expect(mentionRes.comment_view.comment.content).toBeDefined();
362   expect(mentionRes.comment_view.community.local).toBe(false);
363   expect(mentionRes.comment_view.creator.local).toBe(true);
364   expect(mentionRes.comment_view.counts.score).toBe(1);
365
366   let mentionsRes = await getMentions(beta);
367   expect(mentionsRes.mentions[0].comment.content).toBeDefined();
368   expect(mentionsRes.mentions[0].community.local).toBe(true);
369   expect(mentionsRes.mentions[0].creator.local).toBe(false);
370   expect(mentionsRes.mentions[0].counts.score).toBe(1);
371 });
372
373 test("Comment Search", async () => {
374   let commentRes = await createComment(alpha, postRes.post_view.post.id);
375   let betaComment = (
376     await resolveComment(beta, commentRes.comment_view.comment)
377   ).comment;
378   assertCommentFederation(betaComment, commentRes.comment_view);
379 });
380
381 test("A and G subscribe to B (center) A posts, G mentions B, it gets announced to A", async () => {
382   // Create a local post
383   let alphaCommunity = (await resolveCommunity(alpha, "!main@lemmy-alpha:8541"))
384     .community;
385
386   if (!alphaCommunity) {
387     throw "Missing alpha community";
388   }
389
390   let alphaPost = await createPost(alpha, alphaCommunity.community.id);
391   expect(alphaPost.post_view.community.local).toBe(true);
392
393   // Make sure gamma sees it
394   let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post;
395
396   if (!gammaPost) {
397     throw "Missing gamma post";
398   }
399
400   let commentContent =
401     "A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551";
402   let commentRes = await createComment(
403     gamma,
404     gammaPost.post.id,
405     undefined,
406     commentContent,
407   );
408   expect(commentRes.comment_view.comment.content).toBe(commentContent);
409   expect(commentRes.comment_view.community.local).toBe(false);
410   expect(commentRes.comment_view.creator.local).toBe(true);
411   expect(commentRes.comment_view.counts.score).toBe(1);
412
413   // Make sure alpha sees it
414   let alphaPostComments2 = await getComments(
415     alpha,
416     alphaPost.post_view.post.id,
417   );
418   expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
419   expect(alphaPostComments2.comments[0].community.local).toBe(true);
420   expect(alphaPostComments2.comments[0].creator.local).toBe(false);
421   expect(alphaPostComments2.comments[0].counts.score).toBe(1);
422   assertCommentFederation(
423     alphaPostComments2.comments[0],
424     commentRes.comment_view,
425   );
426
427   // Make sure beta has mentions
428   let mentionsRes = await getMentions(beta);
429   expect(mentionsRes.mentions[0].comment.content).toBe(commentContent);
430   expect(mentionsRes.mentions[0].community.local).toBe(false);
431   expect(mentionsRes.mentions[0].creator.local).toBe(false);
432   // TODO this is failing because fetchInReplyTos aren't getting score
433   // expect(mentionsRes.mentions[0].score).toBe(1);
434 });
435
436 test("Check that activity from another instance is sent to third instance", async () => {
437   // Alpha and gamma users follow beta community
438   let alphaFollow = await followBeta(alpha);
439   expect(alphaFollow.community_view.community.local).toBe(false);
440   expect(alphaFollow.community_view.community.name).toBe("main");
441
442   let gammaFollow = await followBeta(gamma);
443   expect(gammaFollow.community_view.community.local).toBe(false);
444   expect(gammaFollow.community_view.community.name).toBe("main");
445
446   // Create a post on beta
447   let betaPost = await createPost(beta, 2);
448   expect(betaPost.post_view.community.local).toBe(true);
449
450   // Make sure gamma and alpha see it
451   let gammaPost = (await resolvePost(gamma, betaPost.post_view.post)).post;
452   if (!gammaPost) {
453     throw "Missing gamma post";
454   }
455   expect(gammaPost.post).toBeDefined();
456
457   let alphaPost = (await resolvePost(alpha, betaPost.post_view.post)).post;
458   if (!alphaPost) {
459     throw "Missing alpha post";
460   }
461   expect(alphaPost.post).toBeDefined();
462
463   // The bug: gamma comments, and alpha should see it.
464   let commentContent = "Comment from gamma";
465   let commentRes = await createComment(
466     gamma,
467     gammaPost.post.id,
468     undefined,
469     commentContent,
470   );
471   expect(commentRes.comment_view.comment.content).toBe(commentContent);
472   expect(commentRes.comment_view.community.local).toBe(false);
473   expect(commentRes.comment_view.creator.local).toBe(true);
474   expect(commentRes.comment_view.counts.score).toBe(1);
475
476   // Make sure alpha sees it
477   let alphaPostComments2 = await getComments(alpha, alphaPost.post.id);
478   expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
479   expect(alphaPostComments2.comments[0].community.local).toBe(false);
480   expect(alphaPostComments2.comments[0].creator.local).toBe(false);
481   expect(alphaPostComments2.comments[0].counts.score).toBe(1);
482   assertCommentFederation(
483     alphaPostComments2.comments[0],
484     commentRes.comment_view,
485   );
486
487   await unfollowRemotes(alpha);
488   await unfollowRemotes(gamma);
489 });
490
491 test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.", async () => {
492   // Unfollow all remote communities
493   let site = await unfollowRemotes(alpha);
494   expect(
495     site.my_user?.follows.filter(c => c.community.local == false).length,
496   ).toBe(0);
497
498   // B creates a post, and two comments, should be invisible to A
499   let postRes = await createPost(beta, 2);
500   expect(postRes.post_view.post.name).toBeDefined();
501
502   let parentCommentContent = "An invisible top level comment from beta";
503   let parentCommentRes = await createComment(
504     beta,
505     postRes.post_view.post.id,
506     undefined,
507     parentCommentContent,
508   );
509   expect(parentCommentRes.comment_view.comment.content).toBe(
510     parentCommentContent,
511   );
512
513   // B creates a comment, then a child one of that.
514   let childCommentContent = "An invisible child comment from beta";
515   let childCommentRes = await createComment(
516     beta,
517     postRes.post_view.post.id,
518     parentCommentRes.comment_view.comment.id,
519     childCommentContent,
520   );
521   expect(childCommentRes.comment_view.comment.content).toBe(
522     childCommentContent,
523   );
524
525   // Follow beta again
526   let follow = await followBeta(alpha);
527   expect(follow.community_view.community.local).toBe(false);
528   expect(follow.community_view.community.name).toBe("main");
529
530   // An update to the child comment on beta, should push the post, parent, and child to alpha now
531   let updatedCommentContent = "An update child comment from beta";
532   let updateRes = await editComment(
533     beta,
534     childCommentRes.comment_view.comment.id,
535     updatedCommentContent,
536   );
537   expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
538
539   // Get the post from alpha
540   let alphaPostB = (await resolvePost(alpha, postRes.post_view.post)).post;
541   if (!alphaPostB) {
542     throw "Missing alpha post B";
543   }
544
545   let alphaPost = await getPost(alpha, alphaPostB.post.id);
546   let alphaPostComments = await getComments(alpha, alphaPostB.post.id);
547   expect(alphaPost.post_view.post.name).toBeDefined();
548   assertCommentFederation(
549     alphaPostComments.comments[1],
550     parentCommentRes.comment_view,
551   );
552   assertCommentFederation(
553     alphaPostComments.comments[0],
554     updateRes.comment_view,
555   );
556   expect(alphaPost.post_view.community.local).toBe(false);
557   expect(alphaPost.post_view.creator.local).toBe(false);
558
559   await unfollowRemotes(alpha);
560 });
561
562 test("Report a comment", async () => {
563   let betaCommunity = (await resolveBetaCommunity(beta)).community;
564   if (!betaCommunity) {
565     throw "Missing beta community";
566   }
567   let postRes = (await createPost(beta, betaCommunity.community.id)).post_view
568     .post;
569   expect(postRes).toBeDefined();
570   let commentRes = (await createComment(beta, postRes.id)).comment_view.comment;
571   expect(commentRes).toBeDefined();
572
573   let alphaComment = (await resolveComment(alpha, commentRes)).comment?.comment;
574   if (!alphaComment) {
575     throw "Missing alpha comment";
576   }
577
578   let alphaReport = (
579     await reportComment(alpha, alphaComment.id, randomString(10))
580   ).comment_report_view.comment_report;
581
582   let betaReport = (await listCommentReports(beta)).comment_reports[0]
583     .comment_report;
584   expect(betaReport).toBeDefined();
585   expect(betaReport.resolved).toBe(false);
586   expect(betaReport.original_comment_text).toBe(
587     alphaReport.original_comment_text,
588   );
589   expect(betaReport.reason).toBe(alphaReport.reason);
590 });