From 77cebd3d58a85d8db94cb863a8a3cb9ab11096c6 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 19 Nov 2024 11:40:39 +0100 Subject: [PATCH] api tests passing --- api_tests/run-federation-test.sh | 2 +- api_tests/src/private_community.spec.ts | 31 +++---- crates/apub/src/http/community.rs | 107 +++++++++++++++++++----- 3 files changed, 102 insertions(+), 38 deletions(-) diff --git a/api_tests/run-federation-test.sh b/api_tests/run-federation-test.sh index 969a95b3e..27d2afc26 100755 --- a/api_tests/run-federation-test.sh +++ b/api_tests/run-federation-test.sh @@ -11,7 +11,7 @@ killall -s1 lemmy_server || true popd pnpm i -pnpm api-test || true +pnpm api-test-private-community || true killall -s1 lemmy_server || true killall -s1 pict-rs || true diff --git a/api_tests/src/private_community.spec.ts b/api_tests/src/private_community.spec.ts index dc2c4fe35..08a4e5876 100644 --- a/api_tests/src/private_community.spec.ts +++ b/api_tests/src/private_community.spec.ts @@ -274,7 +274,7 @@ test("Follow a private community and receive activities", async () => { ); }); -test.only("Fetch remote content in private community", async () => { +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"); @@ -290,6 +290,12 @@ test.only("Fetch remote content in private community", async () => { await beta.followCommunity(follow_form_beta); await approveFollower(alpha, alphaCommunityId); + // Follow is confirmed + await waitUntil( + () => getCommunity(beta, betaCommunityId), + c => c.community_view.subscribed == "Subscribed", + ); + // beta creates post and comment const post = await createPost(beta, betaCommunityId); const post_id = post.post_view.post.id; @@ -298,19 +304,11 @@ test.only("Fetch remote content in private community", async () => { const comment_id = comment.comment_view.comment.id; expect(comment_id).toBeDefined(); - // Wait for post and comment to federate - /* - console.log('a'); + // Wait for it to federate await waitUntil( - () => resolvePost(alpha, post.post_view.post), - p => p?.post?.post.id != undefined, + () => resolveComment(alpha, comment.comment_view.comment), + p => p?.comment?.comment.id != undefined, ); - await waitUntil( - () => resolveComment(gamma, comment.comment_view.comment), - p => p?.post?.post.id != undefined, - ); - */ - console.log("b"); // create gamma user and follow community const gammaCommunityId = ( @@ -323,23 +321,20 @@ test.only("Fetch remote content in private community", async () => { await gamma.followCommunity(follow_form); await approveFollower(alpha, alphaCommunityId); - // now user can fetch posts and comments in community (using signed fetch), and create posts - // TODO: this fails because beta doesnt know if the gamma user was approved by alpha community - console.log(0); + // now user can fetch posts and comments in community (using signed fetch), and create posts. + // for this to work, beta checks with alpha if gamma is really an approved follower. let resolvedPost = await waitUntil( () => resolvePost(gamma, post.post_view.post), p => p?.post?.post.id != undefined, ); expect(resolvedPost.post?.post.ap_id).toBe(post.post_view.post.ap_id); - console.log(1); const resolvedComment = await waitUntil( () => resolveComment(gamma, comment.comment_view.comment), - p => p?.post?.post.id != undefined, + p => p?.comment?.comment.id != undefined, ); expect(resolvedComment?.comment?.comment.ap_id).toBe( comment.comment_view.comment.ap_id, ); - console.log(2); }); async function approveFollower(user: LemmyHttp, community_id: number) { diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index cdb9bc0bc..25f6ae0bf 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -21,7 +21,7 @@ use actix_web::{ HttpResponse, }; use lemmy_api_common::context::LemmyContext; -use lemmy_db_schema::{source::community::Community, traits::ApubActor}; +use lemmy_db_schema::{source::community::Community, traits::ApubActor, CommunityVisibility}; use lemmy_db_views_actor::structs::CommunityFollowerView; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serde::Deserialize; @@ -31,7 +31,7 @@ pub(crate) struct CommunityPath { community_name: String, } -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] pub struct CommunityIsFollowerQuery { is_follower: Option>, } @@ -66,21 +66,22 @@ pub(crate) async fn get_apub_community_followers( let community = Community::read_from_name(&mut context.pool(), &info.community_name, false) .await? .ok_or(LemmyErrorType::NotFound)?; - if let Some(is_follower) = &query.is_follower { - // TODO: check for http sig - // TODO: only in private community - let instance_id = is_follower.dereference(&context).await?.instance_id(); - let has_followers = CommunityFollowerView::check_has_followers_from_instance( - community.id, - instance_id, - &mut context.pool(), - ) - .await; - return if has_followers.is_ok() { - Ok(HttpResponse::Ok().finish()) - } else { - Ok(HttpResponse::NotFound().finish()) - }; + if community.visibility == CommunityVisibility::Private { + if let Some(is_follower) = &query.is_follower { + // TODO: also check for http sig + let instance_id = is_follower.dereference(&context).await?.instance_id(); + let has_followers = CommunityFollowerView::check_has_followers_from_instance( + community.id, + instance_id, + &mut context.pool(), + ) + .await; + return if has_followers.is_ok() { + Ok(HttpResponse::Ok().finish()) + } else { + Ok(HttpResponse::NotFound().finish()) + }; + } } check_community_fetchable(&community)?; let followers = ApubCommunityFollower::read_local(&community.into(), &context).await?; @@ -144,13 +145,19 @@ pub(crate) mod tests { use lemmy_db_schema::{ newtypes::InstanceId, source::{ - community::CommunityInsertForm, + community::{ + CommunityFollower, + CommunityFollowerForm, + CommunityFollowerState, + CommunityInsertForm, + }, instance::Instance, local_site::{LocalSite, LocalSiteInsertForm}, local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitInsertForm}, + person::{Person, PersonInsertForm}, site::{Site, SiteInsertForm}, }, - traits::Crud, + traits::{Crud, Followable}, CommunityVisibility, }; use serde::de::DeserializeOwned; @@ -322,4 +329,66 @@ pub(crate) mod tests { Instance::delete(&mut context.pool(), instance.id).await?; Ok(()) } + + #[tokio::test] + #[serial] + async fn test_is_follower() -> LemmyResult<()> { + let context = LemmyContext::init_test_context().await; + let pool = &mut context.pool(); + + // insert local community + let local_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; + let community_form = CommunityInsertForm { + visibility: Some(CommunityVisibility::Private), + ..CommunityInsertForm::new( + local_instance.id, + "test_community_4".to_string(), + "nada".to_owned(), + "pubkey".to_string(), + ) + }; + let community = Community::create(pool, &community_form).await?; + + // insert remote user + let remote_instance = Instance::read_or_create(pool, "other_domain.tld".to_string()).await?; + let person_form = + PersonInsertForm::new("name".to_string(), "pubkey".to_string(), remote_instance.id); + let person = Person::create(pool, &person_form).await?; + + // community has no follower from remote instance, returns error + let path: Path = CommunityPath { + community_name: community.name.clone(), + } + .into(); + let query = Query(CommunityIsFollowerQuery { + is_follower: Some(person.actor_id.clone().into()), + }); + let res = get_apub_community_followers( + path.clone().into(), + query.clone().into(), + context.reset_request_count(), + ) + .await; + assert_eq!(404, res?.status()); + + // insert approved follower + let follower_form = CommunityFollowerForm { + state: Some(CommunityFollowerState::Accepted), + ..CommunityFollowerForm::new(community.id, person.id) + }; + CommunityFollower::follow(pool, &follower_form).await?; + + // now returns ok + let res = get_apub_community_followers( + path.clone().into(), + query.into(), + context.reset_request_count(), + ) + .await; + assert_eq!(200, res?.status()); + + Instance::delete(pool, local_instance.id).await?; + Instance::delete(pool, remote_instance.id).await?; + Ok(()) + } }