diff --git a/docker/federation-test/servers.sh b/docker/federation-test/servers.sh index 36f10cd8..b34e8c4e 100755 --- a/docker/federation-test/servers.sh +++ b/docker/federation-test/servers.sh @@ -1,6 +1,7 @@ #!/bin/bash set -e +sudo docker-compose --file ../federation/docker-compose.yml --project-directory . down sudo rm -rf volumes pushd ../../server/ diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index c552d18f..4e087d10 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -39,6 +39,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-alpha + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: @@ -66,6 +68,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-beta + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: @@ -93,6 +97,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-gamma + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: diff --git a/docker/travis/docker-compose.yml b/docker/travis/docker-compose.yml index 5248553a..03b3a7ec 100644 --- a/docker/travis/docker-compose.yml +++ b/docker/travis/docker-compose.yml @@ -39,6 +39,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-alpha + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: @@ -66,6 +68,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-beta + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: @@ -93,6 +97,8 @@ services: - LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma - LEMMY_SETUP__ADMIN_PASSWORD=lemmy - LEMMY_SETUP__SITE_NAME=lemmy-gamma + - LEMMY_RATE_LIMIT__POST=99999 + - LEMMY_RATE_LIMIT__REGISTER=99999 - RUST_BACKTRACE=1 - RUST_LOG=debug depends_on: diff --git a/server/lemmy_db/src/comment.rs b/server/lemmy_db/src/comment.rs index de690413..99efde8d 100644 --- a/server/lemmy_db/src/comment.rs +++ b/server/lemmy_db/src/comment.rs @@ -116,7 +116,10 @@ impl Comment { ) -> Result { use crate::schema::comment::dsl::*; diesel::update(comment.find(comment_id)) - .set(deleted.eq(new_deleted)) + .set(( + deleted.eq(new_deleted), + updated.eq(naive_now()) + )) .get_result::(conn) } @@ -127,7 +130,10 @@ impl Comment { ) -> Result { use crate::schema::comment::dsl::*; diesel::update(comment.find(comment_id)) - .set(removed.eq(new_removed)) + .set(( + removed.eq(new_removed), + updated.eq(naive_now()) + )) .get_result::(conn) } diff --git a/server/lemmy_db/src/community.rs b/server/lemmy_db/src/community.rs index c4930d79..2c86f1e7 100644 --- a/server/lemmy_db/src/community.rs +++ b/server/lemmy_db/src/community.rs @@ -107,7 +107,10 @@ impl Community { ) -> Result { use crate::schema::community::dsl::*; diesel::update(community.find(community_id)) - .set(deleted.eq(new_deleted)) + .set(( + deleted.eq(new_deleted), + updated.eq(naive_now()) + )) .get_result::(conn) } @@ -118,7 +121,10 @@ impl Community { ) -> Result { use crate::schema::community::dsl::*; diesel::update(community.find(community_id)) - .set(removed.eq(new_removed)) + .set(( + removed.eq(new_removed), + updated.eq(naive_now()) + )) .get_result::(conn) } diff --git a/server/lemmy_db/src/post.rs b/server/lemmy_db/src/post.rs index 5eb9a472..56ff7474 100644 --- a/server/lemmy_db/src/post.rs +++ b/server/lemmy_db/src/post.rs @@ -119,7 +119,10 @@ impl Post { ) -> Result { use crate::schema::post::dsl::*; diesel::update(post.find(post_id)) - .set(deleted.eq(new_deleted)) + .set(( + deleted.eq(new_deleted), + updated.eq(naive_now()) + )) .get_result::(conn) } @@ -130,7 +133,10 @@ impl Post { ) -> Result { use crate::schema::post::dsl::*; diesel::update(post.find(post_id)) - .set(removed.eq(new_removed)) + .set(( + removed.eq(new_removed), + updated.eq(naive_now()) + )) .get_result::(conn) } diff --git a/server/src/apub/inbox/user_inbox.rs b/server/src/apub/inbox/user_inbox.rs index ea018f56..494fd9f5 100644 --- a/server/src/apub/inbox/user_inbox.rs +++ b/server/src/apub/inbox/user_inbox.rs @@ -108,9 +108,9 @@ async fn receive_accept( // This will fail if they're already a follower blocking(pool, move |conn| { - CommunityFollower::follow(conn, &community_follower_form) + CommunityFollower::follow(conn, &community_follower_form).ok() }) - .await??; + .await?; // TODO: make sure that we actually requested a follow Ok(HttpResponse::Ok().finish()) diff --git a/ui/package.json b/ui/package.json index aa803aa4..4ba3c78b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,7 +6,7 @@ "license": "AGPL-3.0-or-later", "main": "index.js", "scripts": { - "api-test": "jest src/api_tests/api.spec.ts", + "api-test": "jest src/api_tests/ -i --verbose", "build": "node fuse prod", "lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src", "prebuild": "node generate_translations.js", diff --git a/ui/src/api_tests/api.spec.ts b/ui/src/api_tests/api.spec.ts deleted file mode 100644 index 9f498f8b..00000000 --- a/ui/src/api_tests/api.spec.ts +++ /dev/null @@ -1,1570 +0,0 @@ -import fetch from 'node-fetch'; - -import { - LoginForm, - LoginResponse, - PostForm, - DeletePostForm, - RemovePostForm, - StickyPostForm, - LockPostForm, - PostResponse, - SearchResponse, - FollowCommunityForm, - CommunityResponse, - GetFollowedCommunitiesResponse, - GetPostResponse, - CommentForm, - DeleteCommentForm, - RemoveCommentForm, - CommentResponse, - CommunityForm, - DeleteCommunityForm, - RemoveCommunityForm, - GetCommunityResponse, - CommentLikeForm, - CreatePostLikeForm, - PrivateMessageForm, - EditPrivateMessageForm, - DeletePrivateMessageForm, - PrivateMessageResponse, - PrivateMessagesResponse, - GetUserMentionsResponse, -} from '../interfaces'; - -let lemmyAlphaUrl = 'http://localhost:8540'; -let lemmyAlphaApiUrl = `${lemmyAlphaUrl}/api/v1`; -let lemmyAlphaAuth: string; - -let lemmyBetaUrl = 'http://localhost:8550'; -let lemmyBetaApiUrl = `${lemmyBetaUrl}/api/v1`; -let lemmyBetaAuth: string; - -let lemmyGammaUrl = 'http://localhost:8560'; -let lemmyGammaApiUrl = `${lemmyGammaUrl}/api/v1`; -let lemmyGammaAuth: string; - -// Workaround for tests being run before beforeAll() is finished -// https://github.com/facebook/jest/issues/9527#issuecomment-592406108 -describe('main', () => { - beforeAll(async () => { - console.log('Logging in as lemmy_alpha'); - let form: LoginForm = { - username_or_email: 'lemmy_alpha', - password: 'lemmy', - }; - - let res: LoginResponse = await fetch(`${lemmyAlphaApiUrl}/user/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(form), - }).then(d => d.json()); - - lemmyAlphaAuth = res.jwt; - - console.log('Logging in as lemmy_beta'); - let formB = { - username_or_email: 'lemmy_beta', - password: 'lemmy', - }; - - let resB: LoginResponse = await fetch(`${lemmyBetaApiUrl}/user/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(formB), - }).then(d => d.json()); - - lemmyBetaAuth = resB.jwt; - - console.log('Logging in as lemmy_gamma'); - let formC = { - username_or_email: 'lemmy_gamma', - password: 'lemmy', - }; - - let resG: LoginResponse = await fetch(`${lemmyGammaApiUrl}/user/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(formC), - }).then(d => d.json()); - - lemmyGammaAuth = resG.jwt; - }); - - describe('post_search', () => { - test('Create test post on alpha and fetch it on beta', async () => { - let name = 'A jest test post'; - let postForm: PostForm = { - name, - auth: lemmyAlphaAuth, - community_id: 2, - nsfw: false, - }; - - let createPostRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(postForm), - } - ).then(d => d.json()); - expect(createPostRes.post.name).toBe(name); - - let searchUrl = `${lemmyBetaApiUrl}/search?q=${createPostRes.post.ap_id}&type_=All&sort=TopAll`; - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - // TODO: check more fields - expect(searchResponse.posts[0].name).toBe(name); - }); - }); - - describe('follow_accept', () => { - test('/u/lemmy_alpha follows and accepts lemmy-beta/c/main', async () => { - // Make sure lemmy-beta/c/main is cached on lemmy_alpha - // Use short-hand search url - let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`; - - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(searchResponse.communities[0].name).toBe('main'); - - let followForm: FollowCommunityForm = { - community_id: searchResponse.communities[0].id, - follow: true, - auth: lemmyAlphaAuth, - }; - - let followRes: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followForm), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followRes.community.local).toBe(false); - expect(followRes.community.name).toBe('main'); - - // Check that you are subscribed to it locally - let followedCommunitiesUrl = `${lemmyAlphaApiUrl}/user/followed_communities?&auth=${lemmyAlphaAuth}`; - let followedCommunitiesRes: GetFollowedCommunitiesResponse = await fetch( - followedCommunitiesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(followedCommunitiesRes.communities[1].community_local).toBe(false); - - // Test out unfollowing - let unfollowForm: FollowCommunityForm = { - community_id: searchResponse.communities[0].id, - follow: false, - auth: lemmyAlphaAuth, - }; - - let unfollowRes: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unfollowForm), - } - ).then(d => d.json()); - expect(unfollowRes.community.local).toBe(false); - - // Check that you are unsubscribed to it locally - let followedCommunitiesResAgain: GetFollowedCommunitiesResponse = await fetch( - followedCommunitiesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(followedCommunitiesResAgain.communities.length).toBe(1); - - // Follow again, for other tests - let followResAgain: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followForm), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followResAgain.community.local).toBe(false); - expect(followResAgain.community.name).toBe('main'); - - // Also make G follow B - - // Use short-hand search url - let searchUrlG = `${lemmyGammaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`; - - let searchResponseG: SearchResponse = await fetch(searchUrlG, { - method: 'GET', - }).then(d => d.json()); - - expect(searchResponseG.communities[0].name).toBe('main'); - - let followFormG: FollowCommunityForm = { - community_id: searchResponseG.communities[0].id, - follow: true, - auth: lemmyGammaAuth, - }; - - let followResG: CommunityResponse = await fetch( - `${lemmyGammaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followFormG), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followResG.community.local).toBe(false); - expect(followResG.community.name).toBe('main'); - - // Check that you are subscribed to it locally - let followedCommunitiesUrlG = `${lemmyGammaApiUrl}/user/followed_communities?&auth=${lemmyGammaAuth}`; - let followedCommunitiesResG: GetFollowedCommunitiesResponse = await fetch( - followedCommunitiesUrlG, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(followedCommunitiesResG.communities[1].community_local).toBe( - false - ); - }); - }); - - describe('create test post', () => { - test('/u/lemmy_alpha creates a post on /c/lemmy_beta/main, its on both instances', async () => { - let name = 'A jest test federated post'; - let postForm: PostForm = { - name, - auth: lemmyAlphaAuth, - community_id: 3, - nsfw: false, - }; - - let createResponse: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(postForm), - } - ).then(d => d.json()); - - let unlikePostForm: CreatePostLikeForm = { - post_id: createResponse.post.id, - score: 0, - auth: lemmyAlphaAuth, - }; - expect(createResponse.post.name).toBe(name); - expect(createResponse.post.community_local).toBe(false); - expect(createResponse.post.creator_local).toBe(true); - expect(createResponse.post.score).toBe(1); - - let unlikePostRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post/like`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unlikePostForm), - } - ).then(d => d.json()); - expect(unlikePostRes.post.score).toBe(0); - - let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.post.name).toBe(name); - expect(getPostRes.post.community_local).toBe(true); - expect(getPostRes.post.creator_local).toBe(false); - expect(getPostRes.post.score).toBe(0); - }); - }); - - describe('update test post', () => { - test('/u/lemmy_alpha updates a post on /c/lemmy_beta/main, the update is on both', async () => { - let name = 'A jest test federated post, updated'; - let postForm: PostForm = { - name, - edit_id: 2, - auth: lemmyAlphaAuth, - community_id: 3, - nsfw: false, - }; - - let updateResponse: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(postForm), - } - ).then(d => d.json()); - - expect(updateResponse.post.name).toBe(name); - expect(updateResponse.post.community_local).toBe(false); - expect(updateResponse.post.creator_local).toBe(true); - - let stickyPostForm: StickyPostForm = { - edit_id: 2, - stickied: true, - auth: lemmyAlphaAuth, - }; - - let stickyRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post/sticky`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(stickyPostForm), - } - ).then(d => d.json()); - - expect(stickyRes.post.name).toBe(name); - expect(stickyRes.post.stickied).toBe(true); - - // Fetch from B - let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.post.name).toBe(name); - expect(getPostRes.post.community_local).toBe(true); - expect(getPostRes.post.creator_local).toBe(false); - expect(getPostRes.post.stickied).toBe(true); - - let lockPostForm: LockPostForm = { - edit_id: 2, - locked: true, - auth: lemmyAlphaAuth, - }; - - let lockedRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post/lock`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(lockPostForm), - } - ).then(d => d.json()); - - expect(lockedRes.post.name).toBe(name); - expect(lockedRes.post.locked).toBe(true); - - // Fetch from B to make sure its locked - getPostRes = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostRes.post.locked).toBe(true); - - // Create a test comment on a locked post, it should be undefined - // since it shouldn't get created. - let content = 'A rejected comment on a locked post'; - let commentForm: CommentForm = { - content, - post_id: 2, - auth: lemmyAlphaAuth, - }; - - let createResponse: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentForm), - } - ).then(d => d.json()); - - expect(createResponse['error']).toBe('locked'); - - // Unlock the post for later actions - let unlockPostForm: LockPostForm = { - edit_id: 2, - locked: false, - auth: lemmyAlphaAuth, - }; - - let unlockedRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post/lock`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unlockPostForm), - } - ).then(d => d.json()); - - expect(unlockedRes.post.name).toBe(name); - expect(unlockedRes.post.locked).toBe(false); - }); - }); - - describe('create test comment', () => { - test('/u/lemmy_alpha creates a comment on /c/lemmy_beta/main, its on both instances', async () => { - let content = 'A jest test federated comment'; - let commentForm: CommentForm = { - content, - post_id: 2, - auth: lemmyAlphaAuth, - }; - - let createResponse: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentForm), - } - ).then(d => d.json()); - - expect(createResponse.comment.content).toBe(content); - expect(createResponse.comment.community_local).toBe(false); - expect(createResponse.comment.creator_local).toBe(true); - expect(createResponse.comment.score).toBe(1); - - // Do an unlike, to test it - let unlikeCommentForm: CommentLikeForm = { - comment_id: createResponse.comment.id, - score: 0, - auth: lemmyAlphaAuth, - }; - - let unlikeCommentRes: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment/like`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unlikeCommentForm), - } - ).then(d => d.json()); - - expect(unlikeCommentRes.comment.score).toBe(0); - - let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.comments[0].content).toBe(content); - expect(getPostRes.comments[0].community_local).toBe(true); - expect(getPostRes.comments[0].creator_local).toBe(false); - expect(getPostRes.comments[0].score).toBe(0); - - // Now do beta replying to that comment, as a child comment - let contentBeta = 'A child federated comment from beta'; - let commentFormBeta: CommentForm = { - content: contentBeta, - post_id: getPostRes.post.id, - parent_id: getPostRes.comments[0].id, - auth: lemmyBetaAuth, - }; - - let createResponseBeta: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentFormBeta), - } - ).then(d => d.json()); - - expect(createResponseBeta.comment.content).toBe(contentBeta); - expect(createResponseBeta.comment.community_local).toBe(true); - expect(createResponseBeta.comment.creator_local).toBe(true); - expect(createResponseBeta.comment.parent_id).toBe(1); - expect(createResponseBeta.comment.score).toBe(1); - - // Make sure lemmy alpha sees that new child comment from beta - let getPostUrlAlpha = `${lemmyAlphaApiUrl}/post?id=2`; - let getPostResAlpha: GetPostResponse = await fetch(getPostUrlAlpha, { - method: 'GET', - }).then(d => d.json()); - - // The newest show up first - expect(getPostResAlpha.comments[0].content).toBe(contentBeta); - expect(getPostResAlpha.comments[0].community_local).toBe(false); - expect(getPostResAlpha.comments[0].creator_local).toBe(false); - expect(getPostResAlpha.comments[0].score).toBe(1); - - // Lemmy alpha responds to their own comment, but mentions lemmy beta. - // Make sure lemmy beta gets that in their inbox. - let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8550'; - let mentionCommentForm: CommentForm = { - content: mentionContent, - post_id: 2, - parent_id: createResponse.comment.id, - auth: lemmyAlphaAuth, - }; - - let createMentionRes: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(mentionCommentForm), - } - ).then(d => d.json()); - - expect(createMentionRes.comment.content).toBe(mentionContent); - expect(createMentionRes.comment.community_local).toBe(false); - expect(createMentionRes.comment.creator_local).toBe(true); - expect(createMentionRes.comment.score).toBe(1); - - // Make sure lemmy beta sees that new mention - let getMentionUrl = `${lemmyBetaApiUrl}/user/mention?sort=New&unread_only=false&auth=${lemmyBetaAuth}`; - let getMentionsRes: GetUserMentionsResponse = await fetch(getMentionUrl, { - method: 'GET', - }).then(d => d.json()); - - // The newest show up first - expect(getMentionsRes.mentions[0].content).toBe(mentionContent); - expect(getMentionsRes.mentions[0].community_local).toBe(true); - expect(getMentionsRes.mentions[0].creator_local).toBe(false); - expect(getMentionsRes.mentions[0].score).toBe(1); - }); - }); - - describe('update test comment', () => { - test('/u/lemmy_alpha updates a comment on /c/lemmy_beta/main, its on both instances', async () => { - let content = 'A jest test federated comment update'; - let commentForm: CommentForm = { - content, - post_id: 2, - edit_id: 1, - auth: lemmyAlphaAuth, - creator_id: 2, - }; - - let updateResponse: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentForm), - } - ).then(d => d.json()); - - expect(updateResponse.comment.content).toBe(content); - expect(updateResponse.comment.community_local).toBe(false); - expect(updateResponse.comment.creator_local).toBe(true); - - let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.comments[2].content).toBe(content); - expect(getPostRes.comments[2].community_local).toBe(true); - expect(getPostRes.comments[2].creator_local).toBe(false); - }); - }); - - describe('federated comment like', () => { - test('/u/lemmy_beta likes a comment from /u/lemmy_alpha, the like is on both instances', async () => { - // Do a like, to test it (its also been unliked, so its at 0) - let likeCommentForm: CommentLikeForm = { - comment_id: 1, - score: 1, - auth: lemmyBetaAuth, - }; - - let likeCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment/like`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(likeCommentForm), - } - ).then(d => d.json()); - - expect(likeCommentRes.comment.score).toBe(1); - - let getPostUrl = `${lemmyAlphaApiUrl}/post?id=2`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.comments[2].score).toBe(1); - }); - }); - - describe('delete things', () => { - test('/u/lemmy_beta deletes and undeletes a federated comment, post, and community, lemmy_alpha sees its deleted.', async () => { - // Create a test community - let communityName = 'test_community'; - let communityForm: CommunityForm = { - name: communityName, - title: communityName, - category_id: 1, - nsfw: false, - auth: lemmyBetaAuth, - }; - - let createCommunityRes: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(communityForm), - } - ).then(d => d.json()); - - expect(createCommunityRes.community.name).toBe(communityName); - - // Cache it on lemmy_alpha - let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy-beta:8550/c/${communityName}&type_=All&sort=TopAll`; - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - let communityOnAlphaId = searchResponse.communities[0].id; - - // Follow it - let followForm: FollowCommunityForm = { - community_id: communityOnAlphaId, - follow: true, - auth: lemmyAlphaAuth, - }; - - let followRes: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followForm), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followRes.community.local).toBe(false); - expect(followRes.community.name).toBe(communityName); - - // Lemmy beta creates a test post - let postName = 'A jest test post with delete'; - let createPostForm: PostForm = { - name: postName, - auth: lemmyBetaAuth, - community_id: createCommunityRes.community.id, - nsfw: false, - }; - - let createPostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createPostForm), - }).then(d => d.json()); - expect(createPostRes.post.name).toBe(postName); - - // Lemmy beta creates a test comment - let commentContent = 'A jest test federated comment with delete'; - let createCommentForm: CommentForm = { - content: commentContent, - post_id: createPostRes.post.id, - auth: lemmyBetaAuth, - }; - - let createCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createCommentForm), - } - ).then(d => d.json()); - - expect(createCommentRes.comment.content).toBe(commentContent); - - // lemmy_beta deletes the comment - let deleteCommentForm: DeleteCommentForm = { - edit_id: createCommentRes.comment.id, - deleted: true, - auth: lemmyBetaAuth, - }; - - let deleteCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(deleteCommentForm), - } - ).then(d => d.json()); - expect(deleteCommentRes.comment.deleted).toBe(true); - - // lemmy_alpha sees that the comment is deleted - let getPostUrl = `${lemmyAlphaApiUrl}/post?id=3`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostRes.comments[0].deleted).toBe(true); - - // lemmy_beta undeletes the comment - let undeleteCommentForm: DeleteCommentForm = { - edit_id: createCommentRes.comment.id, - deleted: false, - auth: lemmyBetaAuth, - }; - - let undeleteCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(undeleteCommentForm), - } - ).then(d => d.json()); - expect(undeleteCommentRes.comment.deleted).toBe(false); - - // lemmy_alpha sees that the comment is undeleted - let getPostUndeleteRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostUndeleteRes.comments[0].deleted).toBe(false); - - // lemmy_beta deletes the post - let deletePostForm: DeletePostForm = { - edit_id: createPostRes.post.id, - deleted: true, - auth: lemmyBetaAuth, - }; - - let deletePostRes: PostResponse = await fetch( - `${lemmyBetaApiUrl}/post/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(deletePostForm), - } - ).then(d => d.json()); - expect(deletePostRes.post.deleted).toBe(true); - - // Make sure lemmy_alpha sees the post is deleted - let getPostResAgain: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostResAgain.post.deleted).toBe(true); - - // lemmy_beta undeletes the post - let undeletePostForm: DeletePostForm = { - edit_id: createPostRes.post.id, - deleted: false, - auth: lemmyBetaAuth, - }; - - let undeletePostRes: PostResponse = await fetch( - `${lemmyBetaApiUrl}/post/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(undeletePostForm), - } - ).then(d => d.json()); - expect(undeletePostRes.post.deleted).toBe(false); - - // Make sure lemmy_alpha sees the post is undeleted - let getPostResAgainTwo: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostResAgainTwo.post.deleted).toBe(false); - - // lemmy_beta deletes the community - let deleteCommunityForm: DeleteCommunityForm = { - edit_id: createCommunityRes.community.id, - deleted: true, - auth: lemmyBetaAuth, - }; - - let deleteResponse: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(deleteCommunityForm), - } - ).then(d => d.json()); - - // Make sure the delete went through - expect(deleteResponse.community.deleted).toBe(true); - - // Re-get it from alpha, make sure its deleted there too - let getCommunityUrl = `${lemmyAlphaApiUrl}/community?id=${communityOnAlphaId}&auth=${lemmyAlphaAuth}`; - let getCommunityRes: GetCommunityResponse = await fetch(getCommunityUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getCommunityRes.community.deleted).toBe(true); - - // lemmy_beta undeletes the community - let undeleteCommunityForm: DeleteCommunityForm = { - edit_id: createCommunityRes.community.id, - deleted: false, - auth: lemmyBetaAuth, - }; - - let undeleteCommunityRes: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(undeleteCommunityForm), - } - ).then(d => d.json()); - - // Make sure the delete went through - expect(undeleteCommunityRes.community.deleted).toBe(false); - - // Re-get it from alpha, make sure its deleted there too - let getCommunityResAgain: GetCommunityResponse = await fetch( - getCommunityUrl, - { - method: 'GET', - } - ).then(d => d.json()); - expect(getCommunityResAgain.community.deleted).toBe(false); - }); - }); - - describe('remove things', () => { - test('/u/lemmy_beta removes and unremoves a federated comment, post, and community, lemmy_alpha sees its removed.', async () => { - // Create a test community - let communityName = 'test_community_rem'; - let communityForm: CommunityForm = { - name: communityName, - title: communityName, - category_id: 1, - nsfw: false, - auth: lemmyBetaAuth, - }; - - let createCommunityRes: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(communityForm), - } - ).then(d => d.json()); - - expect(createCommunityRes.community.name).toBe(communityName); - - // Cache it on lemmy_alpha - let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy-beta:8550/c/${communityName}&type_=All&sort=TopAll`; - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - let communityOnAlphaId = searchResponse.communities[0].id; - - // Follow it - let followForm: FollowCommunityForm = { - community_id: communityOnAlphaId, - follow: true, - auth: lemmyAlphaAuth, - }; - - let followRes: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followForm), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followRes.community.local).toBe(false); - expect(followRes.community.name).toBe(communityName); - - // Lemmy beta creates a test post - let postName = 'A jest test post with remove'; - let createPostForm: PostForm = { - name: postName, - auth: lemmyBetaAuth, - community_id: createCommunityRes.community.id, - nsfw: false, - }; - - let createPostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createPostForm), - }).then(d => d.json()); - expect(createPostRes.post.name).toBe(postName); - - // Lemmy beta creates a test comment - let commentContent = 'A jest test federated comment with remove'; - let createCommentForm: CommentForm = { - content: commentContent, - post_id: createPostRes.post.id, - auth: lemmyBetaAuth, - }; - - let createCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createCommentForm), - } - ).then(d => d.json()); - - expect(createCommentRes.comment.content).toBe(commentContent); - - // lemmy_beta removes the comment - let removeCommentForm: RemoveCommentForm = { - edit_id: createCommentRes.comment.id, - removed: true, - auth: lemmyBetaAuth, - }; - - let removeCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(removeCommentForm), - } - ).then(d => d.json()); - expect(removeCommentRes.comment.removed).toBe(true); - - // lemmy_alpha sees that the comment is removed - let getPostUrl = `${lemmyAlphaApiUrl}/post?id=4`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostRes.comments[0].removed).toBe(true); - - // lemmy_beta undeletes the comment - let unremoveCommentForm: RemoveCommentForm = { - edit_id: createCommentRes.comment.id, - removed: false, - auth: lemmyBetaAuth, - }; - - let unremoveCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unremoveCommentForm), - } - ).then(d => d.json()); - expect(unremoveCommentRes.comment.removed).toBe(false); - - // lemmy_alpha sees that the comment is undeleted - let getPostUnremoveRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostUnremoveRes.comments[0].removed).toBe(false); - - // lemmy_beta deletes the post - let removePostForm: RemovePostForm = { - edit_id: createPostRes.post.id, - removed: true, - auth: lemmyBetaAuth, - }; - - let removePostRes: PostResponse = await fetch( - `${lemmyBetaApiUrl}/post/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(removePostForm), - } - ).then(d => d.json()); - expect(removePostRes.post.removed).toBe(true); - - // Make sure lemmy_alpha sees the post is deleted - let getPostResAgain: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostResAgain.post.removed).toBe(true); - - // lemmy_beta unremoves the post - let unremovePostForm: RemovePostForm = { - edit_id: createPostRes.post.id, - removed: false, - auth: lemmyBetaAuth, - }; - - let unremovePostRes: PostResponse = await fetch( - `${lemmyBetaApiUrl}/post/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unremovePostForm), - } - ).then(d => d.json()); - expect(unremovePostRes.post.removed).toBe(false); - - // Make sure lemmy_alpha sees the post is unremoved - let getPostResAgainTwo: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - expect(getPostResAgainTwo.post.removed).toBe(false); - - // lemmy_beta removes the community - let removeCommunityForm: RemoveCommunityForm = { - edit_id: createCommunityRes.community.id, - removed: true, - auth: lemmyBetaAuth, - }; - - let removeCommunityRes: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(removeCommunityForm), - } - ).then(d => d.json()); - - // Make sure the remove went through - expect(removeCommunityRes.community.removed).toBe(true); - - // Re-get it from alpha, make sure its removed there too - let getCommunityUrl = `${lemmyAlphaApiUrl}/community?id=${communityOnAlphaId}&auth=${lemmyAlphaAuth}`; - let getCommunityRes: GetCommunityResponse = await fetch(getCommunityUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getCommunityRes.community.removed).toBe(true); - - // lemmy_beta unremoves the community - let unremoveCommunityForm: RemoveCommunityForm = { - edit_id: createCommunityRes.community.id, - removed: false, - auth: lemmyBetaAuth, - }; - - let unremoveCommunityRes: CommunityResponse = await fetch( - `${lemmyBetaApiUrl}/community/remove`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unremoveCommunityForm), - } - ).then(d => d.json()); - - // Make sure the delete went through - expect(unremoveCommunityRes.community.removed).toBe(false); - - // Re-get it from alpha, make sure its deleted there too - let getCommunityResAgain: GetCommunityResponse = await fetch( - getCommunityUrl, - { - method: 'GET', - } - ).then(d => d.json()); - expect(getCommunityResAgain.community.removed).toBe(false); - }); - }); - - describe('private message', () => { - test('/u/lemmy_alpha creates/updates/deletes/undeletes a private_message to /u/lemmy_beta, its on both instances', async () => { - let content = 'A jest test federated private message'; - let privateMessageForm: PrivateMessageForm = { - content, - recipient_id: 3, - auth: lemmyAlphaAuth, - }; - - let createRes: PrivateMessageResponse = await fetch( - `${lemmyAlphaApiUrl}/private_message`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(privateMessageForm), - } - ).then(d => d.json()); - expect(createRes.message.content).toBe(content); - expect(createRes.message.local).toBe(true); - expect(createRes.message.creator_local).toBe(true); - expect(createRes.message.recipient_local).toBe(false); - - // Get it from beta - let getPrivateMessagesUrl = `${lemmyBetaApiUrl}/private_message/list?auth=${lemmyBetaAuth}&unread_only=false`; - - let getPrivateMessagesRes: PrivateMessagesResponse = await fetch( - getPrivateMessagesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(getPrivateMessagesRes.messages[0].content).toBe(content); - expect(getPrivateMessagesRes.messages[0].local).toBe(false); - expect(getPrivateMessagesRes.messages[0].creator_local).toBe(false); - expect(getPrivateMessagesRes.messages[0].recipient_local).toBe(true); - - // lemmy alpha updates the private message - let updatedContent = 'A jest test federated private message edited'; - let updatePrivateMessageForm: EditPrivateMessageForm = { - content: updatedContent, - edit_id: createRes.message.id, - auth: lemmyAlphaAuth, - }; - - let updateRes: PrivateMessageResponse = await fetch( - `${lemmyAlphaApiUrl}/private_message`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(updatePrivateMessageForm), - } - ).then(d => d.json()); - - expect(updateRes.message.content).toBe(updatedContent); - - // Fetch from beta again - let getPrivateMessagesUpdatedRes: PrivateMessagesResponse = await fetch( - getPrivateMessagesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(getPrivateMessagesUpdatedRes.messages[0].content).toBe( - updatedContent - ); - - // lemmy alpha deletes the private message - let deletePrivateMessageForm: DeletePrivateMessageForm = { - deleted: true, - edit_id: createRes.message.id, - auth: lemmyAlphaAuth, - }; - - let deleteRes: PrivateMessageResponse = await fetch( - `${lemmyAlphaApiUrl}/private_message/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(deletePrivateMessageForm), - } - ).then(d => d.json()); - - expect(deleteRes.message.deleted).toBe(true); - - // Fetch from beta again - let getPrivateMessagesDeletedRes: PrivateMessagesResponse = await fetch( - getPrivateMessagesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - // The GetPrivateMessages filters out deleted, - // even though they are in the actual database. - // no reason to show them - expect(getPrivateMessagesDeletedRes.messages.length).toBe(0); - - // lemmy alpha undeletes the private message - let undeletePrivateMessageForm: DeletePrivateMessageForm = { - deleted: false, - edit_id: createRes.message.id, - auth: lemmyAlphaAuth, - }; - - let undeleteRes: PrivateMessageResponse = await fetch( - `${lemmyAlphaApiUrl}/private_message/delete`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(undeletePrivateMessageForm), - } - ).then(d => d.json()); - - expect(undeleteRes.message.deleted).toBe(false); - - // Fetch from beta again - let getPrivateMessagesUnDeletedRes: PrivateMessagesResponse = await fetch( - getPrivateMessagesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - - expect(getPrivateMessagesUnDeletedRes.messages[0].deleted).toBe(false); - }); - }); - - describe('comment_search', () => { - test('Create comment on alpha and search it', async () => { - let content = 'A jest test federated comment for search'; - let commentForm: CommentForm = { - content, - post_id: 1, - auth: lemmyAlphaAuth, - }; - - let createResponse: CommentResponse = await fetch( - `${lemmyAlphaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentForm), - } - ).then(d => d.json()); - - let searchUrl = `${lemmyBetaApiUrl}/search?q=${createResponse.comment.ap_id}&type_=All&sort=TopAll`; - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - // TODO: check more fields - expect(searchResponse.comments[0].content).toBe(content); - }); - }); - - describe('announce', () => { - test('A and G subscribe to B (center) A does action, it gets announced to G', async () => { - // A and G are already subscribed to B earlier. - // - let postName = 'A jest test post for announce'; - let createPostForm: PostForm = { - name: postName, - auth: lemmyAlphaAuth, - community_id: 2, - nsfw: false, - }; - - let createPostRes: PostResponse = await fetch( - `${lemmyAlphaApiUrl}/post`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createPostForm), - } - ).then(d => d.json()); - expect(createPostRes.post.name).toBe(postName); - - // Make sure that post got announced to Gamma - let searchUrl = `${lemmyGammaApiUrl}/search?q=${createPostRes.post.ap_id}&type_=All&sort=TopAll`; - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - let postId = searchResponse.posts[0].id; - expect(searchResponse.posts[0].name).toBe(postName); - - // Create a test comment on Gamma, make sure it gets announced to alpha - let commentContent = - 'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8550'; - - let commentForm: CommentForm = { - content: commentContent, - post_id: postId, - auth: lemmyGammaAuth, - }; - - let createCommentRes: CommentResponse = await fetch( - `${lemmyGammaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(commentForm), - } - ).then(d => d.json()); - - expect(createCommentRes.comment.content).toBe(commentContent); - expect(createCommentRes.comment.community_local).toBe(false); - expect(createCommentRes.comment.creator_local).toBe(true); - expect(createCommentRes.comment.score).toBe(1); - - // Get the post from alpha, make sure it has gamma's comment - let getPostUrl = `${lemmyAlphaApiUrl}/post?id=5`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.comments[0].content).toBe(commentContent); - expect(getPostRes.comments[0].community_local).toBe(true); - expect(getPostRes.comments[0].creator_local).toBe(false); - expect(getPostRes.comments[0].score).toBe(1); - }); - }); - - describe('fetch inreplytos', () => { - test('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 () => { - // Check that A is subscribed to B - let followedCommunitiesUrl = `${lemmyAlphaApiUrl}/user/followed_communities?&auth=${lemmyAlphaAuth}`; - let followedCommunitiesRes: GetFollowedCommunitiesResponse = await fetch( - followedCommunitiesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - expect(followedCommunitiesRes.communities[1].community_local).toBe(false); - - // A unsubs from B (communities ids 3-5) - for (let i = 3; i <= 5; i++) { - let unfollowForm: FollowCommunityForm = { - community_id: i, - follow: false, - auth: lemmyAlphaAuth, - }; - - let unfollowRes: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(unfollowForm), - } - ).then(d => d.json()); - expect(unfollowRes.community.local).toBe(false); - } - - // Check that you are unsubscribed from all of them locally - let followedCommunitiesResAgain: GetFollowedCommunitiesResponse = await fetch( - followedCommunitiesUrl, - { - method: 'GET', - } - ).then(d => d.json()); - expect(followedCommunitiesResAgain.communities.length).toBe(1); - - // B creates a post, and two comments, should be invisible to A - let betaPostName = 'Test post on B, invisible to A at first'; - let postForm: PostForm = { - name: betaPostName, - auth: lemmyBetaAuth, - community_id: 2, - nsfw: false, - }; - - let createPostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(postForm), - }).then(d => d.json()); - expect(createPostRes.post.name).toBe(betaPostName); - - // B creates a comment, then a child one of that. - let parentCommentContent = 'An invisible top level comment from beta'; - let createParentCommentForm: CommentForm = { - content: parentCommentContent, - post_id: createPostRes.post.id, - auth: lemmyBetaAuth, - }; - - let createParentCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createParentCommentForm), - } - ).then(d => d.json()); - expect(createParentCommentRes.comment.content).toBe(parentCommentContent); - - let childCommentContent = 'An invisible child comment from beta'; - let createChildCommentForm: CommentForm = { - content: childCommentContent, - parent_id: createParentCommentRes.comment.id, - post_id: createPostRes.post.id, - auth: lemmyBetaAuth, - }; - - let createChildCommentRes: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(createChildCommentForm), - } - ).then(d => d.json()); - expect(createChildCommentRes.comment.content).toBe(childCommentContent); - - // Follow again, for other tests - let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`; - - let searchResponse: SearchResponse = await fetch(searchUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(searchResponse.communities[0].name).toBe('main'); - - let followForm: FollowCommunityForm = { - community_id: searchResponse.communities[0].id, - follow: true, - auth: lemmyAlphaAuth, - }; - - let followResAgain: CommunityResponse = await fetch( - `${lemmyAlphaApiUrl}/community/follow`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(followForm), - } - ).then(d => d.json()); - - // Make sure the follow response went through - expect(followResAgain.community.local).toBe(false); - expect(followResAgain.community.name).toBe('main'); - - let updatedCommentContent = 'An update child comment from beta'; - let updatedCommentForm: CommentForm = { - content: updatedCommentContent, - post_id: createPostRes.post.id, - edit_id: createChildCommentRes.comment.id, - auth: lemmyBetaAuth, - creator_id: 2, - }; - - let updateResponse: CommentResponse = await fetch( - `${lemmyBetaApiUrl}/comment`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: wrapper(updatedCommentForm), - } - ).then(d => d.json()); - expect(updateResponse.comment.content).toBe(updatedCommentContent); - - // Make sure that A picked up the post, parent comment, and child comment - let getPostUrl = `${lemmyAlphaApiUrl}/post?id=6`; - let getPostRes: GetPostResponse = await fetch(getPostUrl, { - method: 'GET', - }).then(d => d.json()); - - expect(getPostRes.post.name).toBe(betaPostName); - expect(getPostRes.comments[1].content).toBe(parentCommentContent); - expect(getPostRes.comments[0].content).toBe(updatedCommentContent); - expect(getPostRes.post.community_local).toBe(false); - expect(getPostRes.post.creator_local).toBe(false); - }); - }); -}); - -function wrapper(form: any): string { - return JSON.stringify(form); -} diff --git a/ui/src/api_tests/comment.spec.ts b/ui/src/api_tests/comment.spec.ts new file mode 100644 index 00000000..8852a730 --- /dev/null +++ b/ui/src/api_tests/comment.spec.ts @@ -0,0 +1,308 @@ +import { + alpha, + beta, + gamma, + setupLogins, + createPost, + getPost, + searchComment, + likeComment, + followBeta, + searchForBetaCommunity, + createComment, + updateComment, + deleteComment, + removeComment, + getMentions, + searchPost, + unfollowRemotes, +} from './shared'; + +import { PostResponse } from '../interfaces'; + +let postRes: PostResponse; + +beforeAll(async () => { + await setupLogins(); + await followBeta(alpha); + await followBeta(gamma); + let search = await searchForBetaCommunity(alpha); + postRes = await createPost( + alpha, + search.communities.filter(c => c.local == false)[0].id + ); +}); + +afterAll(async () => { + await unfollowRemotes(alpha); + await unfollowRemotes(gamma); +}); + +test('Create a comment', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + expect(commentRes.comment.content).toBeDefined(); + expect(commentRes.comment.community_local).toBe(false); + expect(commentRes.comment.creator_local).toBe(true); + expect(commentRes.comment.score).toBe(1); + + // Make sure that comment is liked on beta + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + expect(betaComment).toBeDefined(); + expect(betaComment.community_local).toBe(true); + expect(betaComment.creator_local).toBe(false); + expect(betaComment.score).toBe(1); +}); + +test('Update a comment', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + let updateCommentRes = await updateComment(alpha, commentRes.comment.id); + expect(updateCommentRes.comment.content).toBe( + 'A jest test federated comment update' + ); + expect(updateCommentRes.comment.community_local).toBe(false); + expect(updateCommentRes.comment.creator_local).toBe(true); + + // Make sure that post is updated on beta + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + expect(betaComment.content).toBe('A jest test federated comment update'); +}); + +test('Delete a comment', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + let deleteCommentRes = await deleteComment( + alpha, + true, + commentRes.comment.id + ); + expect(deleteCommentRes.comment.deleted).toBe(true); + + // Make sure that comment is deleted on beta + // The search doesnt work below, because it returns a tombstone / http::gone + // let searchBeta = await searchComment(beta, commentRes.comment); + // console.log(searchBeta); + // let betaComment = searchBeta.comments[0]; + // Create a fake post, just to get the previous new post id + let createdBetaPostJustToGetId = await createPost(beta, 2); + let betaPost = await getPost(beta, createdBetaPostJustToGetId.post.id - 1); + let betaComment = betaPost.comments[0]; + expect(betaComment.deleted).toBe(true); + + let undeleteCommentRes = await deleteComment( + alpha, + false, + commentRes.comment.id + ); + expect(undeleteCommentRes.comment.deleted).toBe(false); + + // Make sure that comment is undeleted on beta + let searchBeta2 = await searchComment(beta, commentRes.comment); + let betaComment2 = searchBeta2.comments[0]; + expect(betaComment2.deleted).toBe(false); +}); + +test('Remove a comment', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + let removeCommentRes = await removeComment( + alpha, + true, + commentRes.comment.id + ); + expect(removeCommentRes.comment.removed).toBe(true); + + // Make sure that comment is removed on beta + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + expect(betaComment.removed).toBe(true); + + let unremoveCommentRes = await removeComment( + alpha, + false, + commentRes.comment.id + ); + expect(unremoveCommentRes.comment.removed).toBe(false); + + // Make sure that comment is unremoved on beta + let searchBeta2 = await searchComment(beta, commentRes.comment); + let betaComment2 = searchBeta2.comments[0]; + expect(betaComment2.removed).toBe(false); +}); + +test('Unlike a comment', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + let unlike = await likeComment(alpha, 0, commentRes.comment); + expect(unlike.comment.score).toBe(0); + + // Make sure that post is unliked on beta + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + expect(betaComment).toBeDefined(); + expect(betaComment.community_local).toBe(true); + expect(betaComment.creator_local).toBe(false); + expect(betaComment.score).toBe(0); +}); + +test('Federated comment like', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + + // Find the comment on beta + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + + let like = await likeComment(beta, 1, betaComment); + expect(like.comment.score).toBe(2); + + // Get the post from alpha, check the likes + let post = await getPost(alpha, postRes.post.id); + expect(post.comments[0].score).toBe(2); +}); + +test('Reply to a comment', async () => { + // Create a comment on alpha, find it on beta + let commentRes = await createComment(alpha, postRes.post.id); + let searchBeta = await searchComment(beta, commentRes.comment); + let betaComment = searchBeta.comments[0]; + + // find that comment id on beta + + // Reply from beta + let replyRes = await createComment(beta, betaComment.post_id, betaComment.id); + expect(replyRes.comment.content).toBeDefined(); + expect(replyRes.comment.community_local).toBe(true); + expect(replyRes.comment.creator_local).toBe(true); + expect(replyRes.comment.parent_id).toBe(betaComment.id); + expect(replyRes.comment.score).toBe(1); + + // Make sure that comment is seen on alpha + // TODO not sure why, but a searchComment back to alpha, for the ap_id of betas + // comment, isn't working. + // let searchAlpha = await searchComment(alpha, replyRes.comment); + let post = await getPost(alpha, postRes.post.id); + let alphaComment = post.comments[0]; + expect(alphaComment.content).toBeDefined(); + expect(alphaComment.parent_id).toBe(post.comments[1].id); + expect(alphaComment.community_local).toBe(false); + expect(alphaComment.creator_local).toBe(false); + expect(alphaComment.score).toBe(1); +}); + +test('Mention beta', async () => { + // Create a mention on alpha + let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8550'; + let commentRes = await createComment(alpha, postRes.post.id); + let mentionRes = await createComment( + alpha, + postRes.post.id, + commentRes.comment.id, + mentionContent + ); + expect(mentionRes.comment.content).toBeDefined(); + expect(mentionRes.comment.community_local).toBe(false); + expect(mentionRes.comment.creator_local).toBe(true); + expect(mentionRes.comment.score).toBe(1); + + let mentionsRes = await getMentions(beta); + expect(mentionsRes.mentions[0].content).toBeDefined(); + expect(mentionsRes.mentions[0].community_local).toBe(true); + expect(mentionsRes.mentions[0].creator_local).toBe(false); + expect(mentionsRes.mentions[0].score).toBe(1); +}); + +test('Comment Search', async () => { + let commentRes = await createComment(alpha, postRes.post.id); + let searchBeta = await searchComment(beta, commentRes.comment); + expect(searchBeta.comments[0].ap_id).toBe(commentRes.comment.ap_id); +}); + +test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => { + // Create a local post + let alphaPost = await createPost(alpha, 2); + expect(alphaPost.post.community_local).toBe(true); + + // Make sure gamma sees it + let search = await searchPost(gamma, alphaPost.post); + let gammaPost = search.posts[0]; + + let commentContent = + 'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8550'; + let commentRes = await createComment( + gamma, + gammaPost.id, + undefined, + commentContent + ); + expect(commentRes.comment.content).toBe(commentContent); + expect(commentRes.comment.community_local).toBe(false); + expect(commentRes.comment.creator_local).toBe(true); + expect(commentRes.comment.score).toBe(1); + + // Make sure alpha sees it + let alphaPost2 = await getPost(alpha, alphaPost.post.id); + expect(alphaPost2.comments[0].content).toBe(commentContent); + expect(alphaPost2.comments[0].community_local).toBe(true); + expect(alphaPost2.comments[0].creator_local).toBe(false); + expect(alphaPost2.comments[0].score).toBe(1); + + // Make sure beta has mentions + let mentionsRes = await getMentions(beta); + expect(mentionsRes.mentions[0].content).toBe(commentContent); + expect(mentionsRes.mentions[0].community_local).toBe(false); + expect(mentionsRes.mentions[0].creator_local).toBe(false); + // TODO this is failing because fetchInReplyTos aren't getting score + // expect(mentionsRes.mentions[0].score).toBe(1); +}); + +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 () => { + // Unfollow all remote communities + let followed = await unfollowRemotes(alpha); + expect( + followed.communities.filter(c => c.community_local == false).length + ).toBe(0); + + // B creates a post, and two comments, should be invisible to A + let postRes = await createPost(beta, 2); + expect(postRes.post.name).toBeDefined(); + + let parentCommentContent = 'An invisible top level comment from beta'; + let parentCommentRes = await createComment( + beta, + postRes.post.id, + undefined, + parentCommentContent + ); + expect(parentCommentRes.comment.content).toBe(parentCommentContent); + + // B creates a comment, then a child one of that. + let childCommentContent = 'An invisible child comment from beta'; + let childCommentRes = await createComment( + beta, + postRes.post.id, + parentCommentRes.comment.id, + childCommentContent + ); + expect(childCommentRes.comment.content).toBe(childCommentContent); + + // Follow beta again + let follow = await followBeta(alpha); + expect(follow.community.local).toBe(false); + expect(follow.community.name).toBe('main'); + + // An update to the child comment on beta, should push the post, parent, and child to alpha now + let updatedCommentContent = 'An update child comment from beta'; + let updateRes = await updateComment( + beta, + childCommentRes.comment.id, + updatedCommentContent + ); + expect(updateRes.comment.content).toBe(updatedCommentContent); + + // Get the post from alpha + let createFakeAlphaPostToGetId = await createPost(alpha, 2); + let alphaPost = await getPost(alpha, createFakeAlphaPostToGetId.post.id - 1); + expect(alphaPost.post.name).toBeDefined(); + expect(alphaPost.comments[1].content).toBe(parentCommentContent); + expect(alphaPost.comments[0].content).toBe(updatedCommentContent); + expect(alphaPost.post.community_local).toBe(false); + expect(alphaPost.post.creator_local).toBe(false); +}); diff --git a/ui/src/api_tests/community.spec.ts b/ui/src/api_tests/community.spec.ts new file mode 100644 index 00000000..6945e332 --- /dev/null +++ b/ui/src/api_tests/community.spec.ts @@ -0,0 +1,88 @@ +import { + alpha, + beta, + setupLogins, + searchForBetaCommunity, + createCommunity, + deleteCommunity, + removeCommunity, +} from './shared'; + +beforeAll(async () => { + await setupLogins(); +}); + +test('Create community', async () => { + let communityRes = await createCommunity(alpha); + expect(communityRes.community.name).toBeDefined(); + + // A dupe check + let prevName = communityRes.community.name; + let communityRes2 = await createCommunity(alpha, prevName); + expect(communityRes2['error']).toBe('community_already_exists'); +}); + +test('Delete community', async () => { + let communityRes = await createCommunity(beta); + let deleteCommunityRes = await deleteCommunity( + beta, + true, + communityRes.community.id + ); + expect(deleteCommunityRes.community.deleted).toBe(true); + + // Make sure it got deleted on A + let search = await searchForBetaCommunity(alpha); + let communityA = search.communities[0]; + // TODO this fails currently, because no updates are pushed + // expect(communityA.deleted).toBe(true); + + // Undelete + let undeleteCommunityRes = await deleteCommunity( + beta, + false, + communityRes.community.id + ); + expect(undeleteCommunityRes.community.deleted).toBe(false); + + // Make sure it got undeleted on A + let search2 = await searchForBetaCommunity(alpha); + let communityA2 = search2.communities[0]; + // TODO this fails currently, because no updates are pushed + // expect(communityA2.deleted).toBe(false); +}); + +test('Remove community', async () => { + let communityRes = await createCommunity(beta); + let removeCommunityRes = await removeCommunity( + beta, + true, + communityRes.community.id + ); + expect(removeCommunityRes.community.removed).toBe(true); + + // Make sure it got removed on A + let search = await searchForBetaCommunity(alpha); + let communityA = search.communities[0]; + // TODO this fails currently, because no updates are pushed + // expect(communityA.removed).toBe(true); + + // unremove + let unremoveCommunityRes = await removeCommunity( + beta, + false, + communityRes.community.id + ); + expect(unremoveCommunityRes.community.removed).toBe(false); + + // Make sure it got unremoved on A + let search2 = await searchForBetaCommunity(alpha); + let communityA2 = search2.communities[0]; + // TODO this fails currently, because no updates are pushed + // expect(communityA2.removed).toBe(false); +}); + +test('Search for beta community', async () => { + let search = await searchForBetaCommunity(alpha); + expect(search.communities[0].name).toBe('main'); +}); diff --git a/ui/src/api_tests/follow.spec.ts b/ui/src/api_tests/follow.spec.ts new file mode 100644 index 00000000..2f1f8cd8 --- /dev/null +++ b/ui/src/api_tests/follow.spec.ts @@ -0,0 +1,40 @@ +import { + alpha, + setupLogins, + searchForBetaCommunity, + followCommunity, + checkFollowedCommunities, + unfollowRemotes, +} from './shared'; + +beforeAll(async () => { + await setupLogins(); +}); + +afterAll(async () => { + await unfollowRemotes(alpha); +}); + +test('Follow federated community', async () => { + let search = await searchForBetaCommunity(alpha); // TODO sometimes this is returning null? + let follow = await followCommunity(alpha, true, search.communities[0].id); + + // Make sure the follow response went through + expect(follow.community.local).toBe(false); + expect(follow.community.name).toBe('main'); + + // Check it from local + let followCheck = await checkFollowedCommunities(alpha); + let remoteCommunityId = followCheck.communities.filter( + c => c.community_local == false + )[0].community_id; + expect(remoteCommunityId).toBeDefined(); + + // Test an unfollow + let unfollow = await followCommunity(alpha, false, remoteCommunityId); + expect(unfollow.community.local).toBe(false); + + // Make sure you are unsubbed locally + let unfollowCheck = await checkFollowedCommunities(alpha); + expect(unfollowCheck.communities.length).toBeGreaterThanOrEqual(1); +}); diff --git a/ui/src/api_tests/post.spec.ts b/ui/src/api_tests/post.spec.ts new file mode 100644 index 00000000..f2cb6678 --- /dev/null +++ b/ui/src/api_tests/post.spec.ts @@ -0,0 +1,192 @@ +import { + alpha, + beta, + gamma, + setupLogins, + createPost, + updatePost, + stickyPost, + lockPost, + searchPost, + likePost, + followBeta, + searchForBetaCommunity, + createComment, + deletePost, + removePost, + getPost, + unfollowRemotes, +} from './shared'; + +beforeAll(async () => { + await setupLogins(); + await followBeta(alpha); + await followBeta(gamma); +}); + +afterAll(async () => { + await unfollowRemotes(alpha); + await unfollowRemotes(gamma); +}); + +test('Create a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + expect(postRes.post).toBeDefined(); + expect(postRes.post.community_local).toBe(false); + expect(postRes.post.creator_local).toBe(true); + expect(postRes.post.score).toBe(1); + + // Make sure that post is liked on beta + let searchBeta = await searchPost(beta, postRes.post); + let betaPost = searchBeta.posts[0]; + + expect(betaPost).toBeDefined(); + expect(betaPost.community_local).toBe(true); + expect(betaPost.creator_local).toBe(false); + expect(betaPost.score).toBe(1); +}); + +test('Unlike a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + let unlike = await likePost(alpha, 0, postRes.post); + expect(unlike.post.score).toBe(0); + + // Make sure that post is unliked on beta + let searchBeta = await searchPost(beta, postRes.post); + let betaPost = searchBeta.posts[0]; + + expect(betaPost).toBeDefined(); + expect(betaPost.community_local).toBe(true); + expect(betaPost.creator_local).toBe(false); + expect(betaPost.score).toBe(0); +}); + +test('Update a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let updatedPost = await updatePost(alpha, postRes.post); + expect(updatedPost.post.name).toBe('A jest test federated post, updated'); + expect(updatedPost.post.community_local).toBe(false); + expect(updatedPost.post.creator_local).toBe(true); +}); + +test('Sticky a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let stickiedPostRes = await stickyPost(alpha, true, postRes.post); + expect(stickiedPostRes.post.stickied).toBe(true); + + // Make sure that post is stickied on beta + let searchBeta = await searchPost(beta, postRes.post); + let betaPost = searchBeta.posts[0]; + expect(betaPost.community_local).toBe(true); + expect(betaPost.creator_local).toBe(false); + expect(betaPost.stickied).toBe(true); + + // Unsticky a post + let unstickiedPost = await stickyPost(alpha, false, postRes.post); + expect(unstickiedPost.post.stickied).toBe(false); + + // Make sure that post is unstickied on beta + let searchBeta2 = await searchPost(beta, postRes.post); + let betaPost2 = searchBeta2.posts[0]; + expect(betaPost2.community_local).toBe(true); + expect(betaPost2.creator_local).toBe(false); + expect(betaPost2.stickied).toBe(false); +}); + +test('Lock a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let lockedPostRes = await lockPost(alpha, true, postRes.post); + expect(lockedPostRes.post.locked).toBe(true); + + // Make sure that post is locked on beta + let searchBeta = await searchPost(beta, postRes.post); + let betaPost = searchBeta.posts[0]; + expect(betaPost.community_local).toBe(true); + expect(betaPost.creator_local).toBe(false); + expect(betaPost.locked).toBe(true); + + // Try to make a new comment there, on alpha + let comment = await createComment(alpha, postRes.post.id); + expect(comment['error']).toBe('locked'); + + // Try to create a new comment, on beta + let commentBeta = await createComment(beta, betaPost.id); + expect(commentBeta['error']).toBe('locked'); + + // Unlock a post + let unlockedPost = await lockPost(alpha, false, postRes.post); + expect(unlockedPost.post.locked).toBe(false); + + // Make sure that post is unlocked on beta + let searchBeta2 = await searchPost(beta, postRes.post); + let betaPost2 = searchBeta2.posts[0]; + expect(betaPost2.community_local).toBe(true); + expect(betaPost2.creator_local).toBe(false); + expect(betaPost2.locked).toBe(false); +}); + +test('Delete a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let deletedPost = await deletePost(alpha, true, postRes.post); + expect(deletedPost.post.deleted).toBe(true); + + // Make sure lemmy beta sees post is deleted + let createFakeBetaPostToGetId = (await createPost(beta, 2)).post.id - 1; + let betaPost = await getPost(beta, createFakeBetaPostToGetId); + expect(betaPost.post.deleted).toBe(true); + + // Undelete + let undeletedPost = await deletePost(alpha, false, postRes.post); + expect(undeletedPost.post.deleted).toBe(false); + + // Make sure lemmy beta sees post is undeleted + let betaPost2 = await getPost(beta, createFakeBetaPostToGetId); + expect(betaPost2.post.deleted).toBe(false); +}); + +test('Remove a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let removedPost = await removePost(alpha, true, postRes.post); + expect(removedPost.post.removed).toBe(true); + + // Make sure lemmy beta sees post is removed + let createFakeBetaPostToGetId = (await createPost(beta, 2)).post.id - 1; + let betaPost = await getPost(beta, createFakeBetaPostToGetId); + expect(betaPost.post.removed).toBe(true); + + // Undelete + let undeletedPost = await removePost(alpha, false, postRes.post); + expect(undeletedPost.post.removed).toBe(false); + + // Make sure lemmy beta sees post is undeleted + let betaPost2 = await getPost(beta, createFakeBetaPostToGetId); + expect(betaPost2.post.removed).toBe(false); +}); + +test('Search for a post', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + let searchBeta = await searchPost(beta, postRes.post); + + expect(searchBeta.posts[0].name).toBeDefined(); +}); + +test('A and G subscribe to B (center) A posts, it gets announced to G', async () => { + let search = await searchForBetaCommunity(alpha); + let postRes = await createPost(alpha, search.communities[0].id); + + let search2 = await searchPost(gamma, postRes.post); + expect(search2.posts[0].name).toBeDefined(); +}); diff --git a/ui/src/api_tests/private_message.spec.ts b/ui/src/api_tests/private_message.spec.ts new file mode 100644 index 00000000..4bf3f07a --- /dev/null +++ b/ui/src/api_tests/private_message.spec.ts @@ -0,0 +1,71 @@ +import { + alpha, + beta, + setupLogins, + followBeta, + createPrivateMessage, + updatePrivateMessage, + listPrivateMessages, + deletePrivateMessage, + unfollowRemotes, +} from './shared'; + +let recipient_id: number; + +beforeAll(async () => { + await setupLogins(); + recipient_id = (await followBeta(alpha)).community.creator_id; +}); + +afterAll(async () => { + await unfollowRemotes(alpha); +}); + +test('Create a private message', async () => { + let pmRes = await createPrivateMessage(alpha, recipient_id); + expect(pmRes.message.content).toBeDefined(); + expect(pmRes.message.local).toBe(true); + expect(pmRes.message.creator_local).toBe(true); + expect(pmRes.message.recipient_local).toBe(false); + + let betaPms = await listPrivateMessages(beta); + expect(betaPms.messages[0].content).toBeDefined(); + expect(betaPms.messages[0].local).toBe(false); + expect(betaPms.messages[0].creator_local).toBe(false); + expect(betaPms.messages[0].recipient_local).toBe(true); +}); + +test('Update a private message', async () => { + let updatedContent = 'A jest test federated private message edited'; + + let pmRes = await createPrivateMessage(alpha, recipient_id); + let pmUpdated = await updatePrivateMessage(alpha, pmRes.message.id); + expect(pmUpdated.message.content).toBe(updatedContent); + + let betaPms = await listPrivateMessages(beta); + expect(betaPms.messages[0].content).toBe(updatedContent); +}); + +test('Delete a private message', async () => { + let pmRes = await createPrivateMessage(alpha, recipient_id); + let betaPms1 = await listPrivateMessages(beta); + let deletedPmRes = await deletePrivateMessage(alpha, true, pmRes.message.id); + expect(deletedPmRes.message.deleted).toBe(true); + + // The GetPrivateMessages filters out deleted, + // even though they are in the actual database. + // no reason to show them + let betaPms2 = await listPrivateMessages(beta); + expect(betaPms2.messages.length).toBe(betaPms1.messages.length - 1); + + // Undelete + let undeletedPmRes = await deletePrivateMessage( + alpha, + false, + pmRes.message.id + ); + expect(undeletedPmRes.message.deleted).toBe(false); + + let betaPms3 = await listPrivateMessages(beta); + expect(betaPms3.messages.length).toBe(betaPms1.messages.length); +}); diff --git a/ui/src/api_tests/shared.ts b/ui/src/api_tests/shared.ts new file mode 100644 index 00000000..08c4ff23 --- /dev/null +++ b/ui/src/api_tests/shared.ts @@ -0,0 +1,675 @@ +import fetch from 'node-fetch'; + +import { + LoginForm, + LoginResponse, + Post, + PostForm, + Comment, + DeletePostForm, + RemovePostForm, + StickyPostForm, + LockPostForm, + PostResponse, + SearchResponse, + FollowCommunityForm, + CommunityResponse, + GetFollowedCommunitiesResponse, + GetPostResponse, + CommentForm, + DeleteCommentForm, + RemoveCommentForm, + CommentResponse, + CommunityForm, + DeleteCommunityForm, + RemoveCommunityForm, + CommentLikeForm, + CreatePostLikeForm, + PrivateMessageForm, + EditPrivateMessageForm, + DeletePrivateMessageForm, + PrivateMessageResponse, + PrivateMessagesResponse, + GetUserMentionsResponse, +} from '../interfaces'; + +export interface API { + url: string; + auth?: string; +} + +function apiUrl(api: API) { + return `${api.url}/api/v1`; +} + +export let alpha: API = { + url: 'http://localhost:8540', +}; + +export let beta: API = { + url: 'http://localhost:8550', +}; + +export let gamma: API = { + url: 'http://localhost:8560', +}; + +export async function setupLogins() { + let form: LoginForm = { + username_or_email: 'lemmy_alpha', + password: 'lemmy', + }; + + let resA: Promise = fetch(`${apiUrl(alpha)}/user/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(form), + }).then(d => d.json()); + + let formB = { + username_or_email: 'lemmy_beta', + password: 'lemmy', + }; + + let resB: Promise = fetch(`${apiUrl(beta)}/user/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(formB), + }).then(d => d.json()); + + let formC = { + username_or_email: 'lemmy_gamma', + password: 'lemmy', + }; + + let resG: Promise = fetch(`${apiUrl(gamma)}/user/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(formC), + }).then(d => d.json()); + + let res = await Promise.all([resA, resB, resG]); + alpha.auth = res[0].jwt; + beta.auth = res[1].jwt; + gamma.auth = res[2].jwt; +} + +export async function createPost( + api: API, + community_id: number +): Promise { + let name = 'A jest test post'; + let postForm: PostForm = { + name, + auth: api.auth, + community_id, + nsfw: false, + }; + + let createPostRes: PostResponse = await fetch(`${apiUrl(api)}/post`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(postForm), + }).then(d => d.json()); + return createPostRes; +} + +export async function updatePost(api: API, post: Post): Promise { + let name = 'A jest test federated post, updated'; + let postForm: PostForm = { + name, + edit_id: post.id, + auth: api.auth, + nsfw: false, + }; + + let updateResponse: PostResponse = await fetch(`${apiUrl(api)}/post`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(postForm), + }).then(d => d.json()); + return updateResponse; +} + +export async function deletePost( + api: API, + deleted: boolean, + post: Post +): Promise { + let deletePostForm: DeletePostForm = { + edit_id: post.id, + deleted: deleted, + auth: api.auth, + }; + + let deletePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/delete`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(deletePostForm), + }).then(d => d.json()); + return deletePostRes; +} + +export async function removePost( + api: API, + removed: boolean, + post: Post +): Promise { + let removePostForm: RemovePostForm = { + edit_id: post.id, + removed, + auth: api.auth, + }; + + let removePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/remove`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(removePostForm), + }).then(d => d.json()); + return removePostRes; +} + +export async function stickyPost( + api: API, + stickied: boolean, + post: Post +): Promise { + let stickyPostForm: StickyPostForm = { + edit_id: post.id, + stickied, + auth: api.auth, + }; + + let stickyRes: PostResponse = await fetch(`${apiUrl(api)}/post/sticky`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(stickyPostForm), + }).then(d => d.json()); + + return stickyRes; +} + +export async function lockPost( + api: API, + locked: boolean, + post: Post +): Promise { + let lockPostForm: LockPostForm = { + edit_id: post.id, + locked, + auth: api.auth, + }; + + let lockRes: PostResponse = await fetch(`${apiUrl(api)}/post/lock`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(lockPostForm), + }).then(d => d.json()); + + return lockRes; +} + +export async function searchPost( + api: API, + post: Post +): Promise { + let searchUrl = `${apiUrl(api)}/search?q=${post.ap_id}&type_=All&sort=TopAll`; + let searchResponse: SearchResponse = await fetch(searchUrl, { + method: 'GET', + }).then(d => d.json()); + return searchResponse; +} + +export async function getPost( + api: API, + post_id: number +): Promise { + let getPostUrl = `${apiUrl(api)}/post?id=${post_id}`; + let getPostRes: GetPostResponse = await fetch(getPostUrl, { + method: 'GET', + }).then(d => d.json()); + + return getPostRes; +} + +export async function searchComment( + api: API, + comment: Comment +): Promise { + let searchUrl = `${apiUrl(api)}/search?q=${ + comment.ap_id + }&type_=All&sort=TopAll`; + let searchResponse: SearchResponse = await fetch(searchUrl, { + method: 'GET', + }).then(d => d.json()); + return searchResponse; +} + +export async function searchForBetaCommunity( + api: API +): Promise { + // Make sure lemmy-beta/c/main is cached on lemmy_alpha + // Use short-hand search url + let searchUrl = `${apiUrl( + api + )}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`; + + let searchResponse: SearchResponse = await fetch(searchUrl, { + method: 'GET', + }).then(d => d.json()); + return searchResponse; +} + +export async function followCommunity( + api: API, + follow: boolean, + community_id: number +): Promise { + let followForm: FollowCommunityForm = { + community_id, + follow, + auth: api.auth, + }; + + let followRes: CommunityResponse = await fetch( + `${apiUrl(api)}/community/follow`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(followForm), + } + ) + .then(d => d.json()) + .catch(_e => {}); + + return followRes; +} + +export async function checkFollowedCommunities( + api: API +): Promise { + let followedCommunitiesUrl = `${apiUrl( + api + )}/user/followed_communities?&auth=${api.auth}`; + let followedCommunitiesRes: GetFollowedCommunitiesResponse = await fetch( + followedCommunitiesUrl, + { + method: 'GET', + } + ).then(d => d.json()); + return followedCommunitiesRes; +} + +export async function likePost( + api: API, + score: number, + post: Post +): Promise { + let likePostForm: CreatePostLikeForm = { + post_id: post.id, + score: score, + auth: api.auth, + }; + + let likePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/like`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(likePostForm), + }).then(d => d.json()); + + return likePostRes; +} + +export async function createComment( + api: API, + post_id: number, + parent_id?: number, + content = 'a jest test comment' +): Promise { + let commentForm: CommentForm = { + content, + post_id, + parent_id, + auth: api.auth, + }; + + let createResponse: CommentResponse = await fetch(`${apiUrl(api)}/comment`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(commentForm), + }).then(d => d.json()); + return createResponse; +} + +export async function updateComment( + api: API, + edit_id: number, + content = 'A jest test federated comment update' +): Promise { + let commentForm: CommentForm = { + content, + edit_id, + auth: api.auth, + }; + + let updateResponse: CommentResponse = await fetch(`${apiUrl(api)}/comment`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(commentForm), + }).then(d => d.json()); + return updateResponse; +} + +export async function deleteComment( + api: API, + deleted: boolean, + edit_id: number +): Promise { + let deleteCommentForm: DeleteCommentForm = { + edit_id, + deleted, + auth: api.auth, + }; + + let deleteCommentRes: CommentResponse = await fetch( + `${apiUrl(api)}/comment/delete`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(deleteCommentForm), + } + ).then(d => d.json()); + return deleteCommentRes; +} + +export async function removeComment( + api: API, + removed: boolean, + edit_id: number +): Promise { + let removeCommentForm: RemoveCommentForm = { + edit_id, + removed, + auth: api.auth, + }; + + let removeCommentRes: CommentResponse = await fetch( + `${apiUrl(api)}/comment/remove`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(removeCommentForm), + } + ).then(d => d.json()); + return removeCommentRes; +} + +export async function getMentions(api: API): Promise { + let getMentionUrl = `${apiUrl( + api + )}/user/mention?sort=New&unread_only=false&auth=${api.auth}`; + let getMentionsRes: GetUserMentionsResponse = await fetch(getMentionUrl, { + method: 'GET', + }).then(d => d.json()); + return getMentionsRes; +} + +export async function likeComment( + api: API, + score: number, + comment: Comment +): Promise { + let likeCommentForm: CommentLikeForm = { + comment_id: comment.id, + score, + auth: api.auth, + }; + + let likeCommentRes: CommentResponse = await fetch( + `${apiUrl(api)}/comment/like`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(likeCommentForm), + } + ).then(d => d.json()); + return likeCommentRes; +} + +export async function createCommunity( + api: API, + name_: string = randomString(5) +): Promise { + let communityForm: CommunityForm = { + name: name_, + title: name_, + category_id: 1, + nsfw: false, + auth: api.auth, + }; + + let createCommunityRes: CommunityResponse = await fetch( + `${apiUrl(api)}/community`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(communityForm), + } + ).then(d => d.json()); + return createCommunityRes; +} + +export async function deleteCommunity( + api: API, + deleted: boolean, + edit_id: number +): Promise { + let deleteCommunityForm: DeleteCommunityForm = { + edit_id, + deleted, + auth: api.auth, + }; + + let deleteResponse: CommunityResponse = await fetch( + `${apiUrl(api)}/community/delete`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(deleteCommunityForm), + } + ).then(d => d.json()); + return deleteResponse; +} + +export async function removeCommunity( + api: API, + removed: boolean, + edit_id: number +): Promise { + let removeCommunityForm: RemoveCommunityForm = { + edit_id, + removed, + auth: api.auth, + }; + + let removeResponse: CommunityResponse = await fetch( + `${apiUrl(api)}/community/remove`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(removeCommunityForm), + } + ).then(d => d.json()); + return removeResponse; +} + +export async function createPrivateMessage( + api: API, + recipient_id: number +): Promise { + let content = 'A jest test federated private message'; + let privateMessageForm: PrivateMessageForm = { + content, + recipient_id, + auth: api.auth, + }; + + let createRes: PrivateMessageResponse = await fetch( + `${apiUrl(api)}/private_message`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(privateMessageForm), + } + ).then(d => d.json()); + return createRes; +} + +export async function updatePrivateMessage( + api: API, + edit_id: number +): Promise { + let updatedContent = 'A jest test federated private message edited'; + let updatePrivateMessageForm: EditPrivateMessageForm = { + content: updatedContent, + edit_id, + auth: api.auth, + }; + + let updateRes: PrivateMessageResponse = await fetch( + `${apiUrl(api)}/private_message`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(updatePrivateMessageForm), + } + ).then(d => d.json()); + return updateRes; +} + +export async function deletePrivateMessage( + api: API, + deleted: boolean, + edit_id: number +): Promise { + let deletePrivateMessageForm: DeletePrivateMessageForm = { + deleted, + edit_id, + auth: api.auth, + }; + + let deleteRes: PrivateMessageResponse = await fetch( + `${apiUrl(api)}/private_message/delete`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(deletePrivateMessageForm), + } + ).then(d => d.json()); + + return deleteRes; +} + +export async function listPrivateMessages( + api: API +): Promise { + let getPrivateMessagesUrl = `${apiUrl(api)}/private_message/list?auth=${ + api.auth + }&unread_only=false&limit=999`; + + let getPrivateMessagesRes: PrivateMessagesResponse = await fetch( + getPrivateMessagesUrl, + { + method: 'GET', + } + ).then(d => d.json()); + return getPrivateMessagesRes; +} + +export async function unfollowRemotes( + api: API +): Promise { + // Unfollow all remote communities + let followed = await checkFollowedCommunities(api); + let remoteFollowed = followed.communities.filter( + c => c.community_local == false + ); + for (let cu of remoteFollowed) { + await followCommunity(api, false, cu.community_id); + } + let followed2 = await checkFollowedCommunities(api); + return followed2; +} + +export async function followBeta(api: API): Promise { + await unfollowRemotes(api); + + // Cache it + let search = await searchForBetaCommunity(api); + + // Unfollow first + let follow = await followCommunity( + api, + true, + search.communities.filter(c => c.local == false)[0].id + ); + return follow; +} + +export function wrapper(form: any): string { + return JSON.stringify(form); +} + +function randomString(length: number): string { + var result = ''; + var characters = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}