diff --git a/api_tests/src/private_community.spec.ts b/api_tests/src/private_community.spec.ts index 76faf800f..564fed196 100644 --- a/api_tests/src/private_community.spec.ts +++ b/api_tests/src/private_community.spec.ts @@ -21,6 +21,9 @@ import { resolveComment, likeComment, waitUntil, + gamma, + getPosts, + getComments, } from "./shared"; beforeAll(setupLogins); @@ -47,6 +50,7 @@ test("Follow a private community", async () => { await resolveCommunity(user, community.community_view.community.actor_id) ).community; expect(betaCommunity).toBeDefined(); + expect(betaCommunity?.community.visibility).toBe('Private'); const betaCommunityId = betaCommunity!.community.id; const follow_form: FollowCommunity = { community_id: betaCommunityId, @@ -212,3 +216,136 @@ test("Reject follower", async () => { c => c.community_view.subscribed == "NotSubscribed", ); }); + +test("Follow a private community and receive activities", async () => { + // create private community + const community = await createCommunity(alpha, randomString(10), "Private"); + expect(community.community_view.community.visibility).toBe("Private"); + const alphaCommunityId = community.community_view.community.id; + + // follow with users from beta and gamma + const user = await registerUser(beta, betaUrl); + const betaCommunity = ( + await resolveCommunity(user, community.community_view.community.actor_id) + ).community; + expect(betaCommunity).toBeDefined(); + const betaCommunityId = betaCommunity!.community.id; + const follow_form_beta: FollowCommunity = { + community_id: betaCommunityId, + follow: true, + }; + await user.followCommunity(follow_form_beta); + + const gammaCommunityId = ( + await resolveCommunity(gamma, community.community_view.community.actor_id) + ).community!.community.id; + const follow_form_gamma: FollowCommunity = { + community_id: gammaCommunityId, + follow: true, + }; + await gamma.followCommunity(follow_form_gamma); + + // Wait for follow to federate, shown as pending + let pendingFollows1 = await waitUntil( + () => listCommunityPendingFollows(alpha), + f => f.items.length == 2, + ); + + // Approve the follow + const approve1 = await approveCommunityPendingFollow( + alpha, + alphaCommunityId, + pendingFollows1.items[0].person.id, + ); + expect(approve1.success).toBe(true); + const approve2 = await approveCommunityPendingFollow( + alpha, + alphaCommunityId, + pendingFollows1.items[1].person.id, + ); + expect(approve2.success).toBe(true); + + // Follow is confirmed + // Follow is confirmed + await waitUntil( + () => getCommunity(user, betaCommunityId), + c => c.community_view.subscribed == "Subscribed", + ); + await waitUntil( + () => getCommunity(gamma, gammaCommunityId), + c => c.community_view.subscribed == "Subscribed", + ); + + // create a post and comment from gamma + const post = await createPost(gamma, gammaCommunityId); + const post_id = post.post_view.post.id; + expect(post_id).toBeDefined(); + const comment = await createComment(gamma, post_id); + const comment_id = comment.comment_view.comment.id; + expect(comment_id).toBeDefined(); + + // post and comment were federated to beta + let posts = await waitUntil( + () => getPosts(user, 'All', betaCommunityId), + c => c.posts.length == 1, + ); + expect(posts.posts[0].post.ap_id).toBe(post.post_view.post.ap_id); + expect(posts.posts[0].post.name).toBe(post.post_view.post.name); + let comments = await waitUntil( + () => getComments(user, posts.posts[0].post.id), + c => c.comments.length == 1, + ); + expect(comments.comments[0].comment.ap_id).toBe(comment.comment_view.comment.ap_id); + expect(comments.comments[0].comment.content).toBe(comment.comment_view.comment.content); +}); + +test("Fetch remote content in private community", async () => { + // create private community + const community = await createCommunity(alpha, randomString(10), "Private"); + expect(community.community_view.community.visibility).toBe("Private"); + const alphaCommunityId = community.community_view.community.id; + + // create post and comment + const betaCommunityId = ( + await resolveCommunity(beta, community.community_view.community.actor_id) + ).community!.community.id; + const post = await createPost(beta, betaCommunityId); + const post_id = post.post_view.post.id; + expect(post_id).toBeDefined(); + const comment = await createComment(beta, post_id); + const comment_id = comment.comment_view.comment.id; + expect(comment_id).toBeDefined(); + + // user is not following the community and cannot view nor create posts + const user = await registerUser(gamma, betaUrl); + const gammaCommunityId = ( + await resolveCommunity(user, community.community_view.community.actor_id) + ).community!.community.id; + + // follow the community and approve + const follow_form: FollowCommunity = { + community_id: gammaCommunityId, + follow: true, + }; + await user.followCommunity(follow_form); + const pendingFollows1 = await waitUntil( + () => listCommunityPendingFollows(alpha), + f => f.items.length == 1, + ); + const approve = await approveCommunityPendingFollow( + alpha, + alphaCommunityId, + pendingFollows1.items[0].person.id, + ); + expect(approve.success).toBe(true); + + // now user can fetch posts and comments in community (using signed fetch), and create posts + await waitUntil( + () => resolvePost(user, post.post_view.post), + p => p?.post?.post.id != undefined, + ); + const resolvedComment = ( + await resolveComment(user, comment.comment_view.comment) + ).comment; + expect(resolvedComment?.comment.id).toBeDefined(); +}); diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index b31e4b74f..950f4861d 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -215,7 +215,7 @@ async fn can_accept_activity_in_community( ) -> LemmyResult<()> { if let Some(community) = community { // Local only community can't federate - if community.visibility != CommunityVisibility::Public { + if community.visibility == CommunityVisibility::LocalOnly { return Err(LemmyErrorType::NotFound.into()); } if !community.local { diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 59b8fadbb..93c6e5c77 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -42,7 +42,7 @@ pub(crate) async fn send_activity_in_community( context: &Data, ) -> LemmyResult<()> { // If community is local only, don't send anything out - if community.visibility != CommunityVisibility::Public { + if community.visibility == CommunityVisibility::LocalOnly { return Ok(()); } diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 90ab0153f..cec56199d 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -171,7 +171,12 @@ impl ActivityHandler for CreateOrUpdateNote { // TODO: for compatibility with other projects, it would be much better to read this from cc or // tags let mentions = scrape_text_for_mentions(&comment.content); - send_local_notifs(mentions, comment.id, &actor, do_send_email, context, None).await?; + + // TODO: this fails in local community comment as CommentView::read() returns nothing + // without passing LocalUser + send_local_notifs(mentions, comment.id, &actor, do_send_email, context, None) + .await + .ok(); Ok(()) } }