use super::utils::{
check_member_global_timeline_user_post, check_member_total_engagement_user_posts,
check_member_user_post_timeline, check_member_user_replies_timeline, find_post_counts,
find_post_details, find_reply_relationship_parent_uri,
};
use crate::users::utils::find_user_counts;
use crate::utils::watcher::WatcherTest;
use anyhow::Result;
use nexus_common::{
db::{kv::SortOrder, RedisOps},
models::post::{PostDetails, PostRelationships, PostStream},
};
use pubky::Keypair;
use pubky_app_specs::{PubkyAppPost, PubkyAppPostKind, PubkyAppUser};
#[tokio_shared_rt::test(shared)]
async fn test_homeserver_post_reply() -> Result<()> {
let mut test = WatcherTest::setup().await?;
let keypair = Keypair::random();
let user = PubkyAppUser {
bio: Some("test_homeserver_post_reply".to_string()),
image: None,
links: None,
name: "Watcher:PostReply:User".to_string(),
status: None,
};
let user_id = test.create_user(&keypair, &user).await?;
let parent_post = PubkyAppPost {
content: "Watcher:PostReply:User:Post".to_string(),
kind: PubkyAppPostKind::Short,
parent: None,
embed: None,
attachments: None,
};
let parent_post_id = test.create_post(&user_id, &parent_post).await?;
let parent_uri = format!("pubky://{user_id}/pub/pubky.app/posts/{parent_post_id}");
let reply_post = PubkyAppPost {
content: "Watcher:PostReply:User:Reply".to_string(),
kind: PubkyAppPostKind::Short,
parent: Some(parent_uri.clone()),
embed: None,
attachments: None,
};
let reply_id = test.create_post(&user_id, &reply_post).await?;
let reply_post_details = find_post_details(&user_id, &reply_id).await.unwrap();
assert_eq!(reply_post_details.id, reply_id);
assert_eq!(reply_post_details.content, reply_post.content);
assert_eq!(
reply_post_details.uri,
format!("pubky://{user_id}/pub/pubky.app/posts/{reply_id}")
);
assert!(reply_post_details.indexed_at > 0);
let reply_parent_uri = find_reply_relationship_parent_uri(&user_id, &reply_id)
.await
.unwrap();
assert_eq!(reply_parent_uri, parent_uri);
let post_replies = PostStream::get_post_replies(
&user_id,
&parent_post_id,
SortOrder::Descending,
None,
None,
None,
None,
)
.await
.unwrap();
assert_eq!(post_replies.len(), 1);
let post_key = format!("{user_id}:{reply_id}");
assert_eq!(post_replies[0], post_key);
let post_count = find_post_counts(&user_id, &parent_post_id).await;
assert_eq!(post_count.replies, 1);
let total_engagement = check_member_total_engagement_user_posts(&[&user_id, &parent_post_id])
.await
.unwrap();
assert!(total_engagement.is_some());
assert_eq!(total_engagement.unwrap(), 1);
let user_count = find_user_counts(&user_id).await;
assert_eq!(user_count.posts, 2);
assert_eq!(user_count.replies, 1);
let post_detail_cache: PostDetails = PostDetails::get_from_index(&user_id, &reply_id)
.await
.unwrap()
.expect("The new post detail was not served from Nexus cache");
assert_eq!(reply_post_details.id, post_detail_cache.id);
assert_eq!(reply_post_details.content, post_detail_cache.content);
assert_eq!(reply_post_details.uri, post_detail_cache.uri);
assert_eq!(reply_post_details.indexed_at, post_detail_cache.indexed_at);
let reply_post_counts = find_post_counts(&user_id, &reply_id).await;
assert_eq!(reply_post_counts.reposts, 0);
assert_eq!(reply_post_counts.tags, 0);
assert_eq!(reply_post_counts.replies, 0);
let post_relationships = PostRelationships::try_from_index_json(&[&user_id, &reply_id], None)
.await
.unwrap();
assert!(
post_relationships.is_some(),
"Reply should have some relationship"
);
let relationships = post_relationships.unwrap();
assert!(
relationships.replied.is_some(),
"Reply should have parent post URI"
);
assert_eq!(
relationships.replied.unwrap(),
parent_uri,
"The parent URIs does not match"
);
let user_timeline_timestamp = check_member_user_post_timeline(&user_id, &reply_id)
.await
.unwrap_or_default();
assert!(
user_timeline_timestamp.is_none(),
"Replies should not be in the user's main timeline"
);
let user_replies_timeline_timestamp = check_member_user_replies_timeline(&user_id, &reply_id)
.await
.unwrap_or_default();
assert!(
user_replies_timeline_timestamp.is_some(),
"Replies should be in the user's main timeline"
);
let global_timeline_timestamp = check_member_global_timeline_user_post(&user_id, &reply_id)
.await
.unwrap_or_default();
assert!(
global_timeline_timestamp.is_none(),
"Replies should not be in the global timeline"
);
let reply_key = format!("{}:{}", user_id, &reply_id);
let global_total_engagement = check_member_total_engagement_user_posts(&[&reply_key])
.await
.unwrap_or_default();
assert!(
global_total_engagement.is_none(),
"Replies should not be in the global total engagement sorted set"
);
test.cleanup_post(&user_id, &reply_id).await?;
test.cleanup_user(&user_id).await?;
test.cleanup_post(&user_id, &parent_post_id).await?;
Ok(())
}