use crate::harness::TestHarness; #[tokio::test] async fn profile_page_shows_user_info() { let mut h = TestHarness::new().await; let user_id = h.login_as("profileuser").await; let comm_id = h.create_community("TestCommunity", "test-comm").await; h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let resp = h.client.get("/p/test-comm/u/profileuser").await; assert_eq!(resp.status.as_u16(), 200, "profile page should load"); assert!(resp.text.contains("profileuser"), "should show username"); assert!(resp.text.contains("member"), "should show role badge"); assert!(resp.text.contains("Joined"), "should show join date"); } #[tokio::test] async fn profile_page_shows_activity() { let mut h = TestHarness::new().await; let user_id = h.login_as("activeuser").await; let comm_id = h.create_community("ActivityComm", "activity-comm").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; // Create a thread (user is thread author) let thread_id = h .create_thread_with_post(cat_id, user_id, "My Thread", "Hello world") .await; // Create a reply in another thread let thread_id_2 = h .create_thread_with_post(cat_id, user_id, "Another Thread", "Second post") .await; mt_db::mutations::create_post( &h.db, thread_id_2, user_id, "A reply", "

A reply

", ) .await .unwrap(); let resp = h.client.get("/p/activity-comm/u/activeuser").await; assert_eq!(resp.status.as_u16(), 200); assert!(resp.text.contains("My Thread"), "should list thread activity"); assert!(resp.text.contains("Another Thread"), "should list reply activity"); // Verify thread_id link is present assert!( resp.text.contains(&thread_id.to_string()), "should link to thread" ); } #[tokio::test] async fn profile_nonmember_returns_404() { let mut h = TestHarness::new().await; h.login_as("outsider").await; let _comm_id = h.create_community("ClosedComm", "closed-comm").await; // User exists but is not a member let resp = h.client.get("/p/closed-comm/u/outsider").await; assert_eq!(resp.status.as_u16(), 404, "non-member should get 404"); } #[tokio::test] async fn profile_nonexistent_user_404() { let mut h = TestHarness::new().await; h.login_as("someuser").await; let _comm_id = h.create_community("SomeComm", "some-comm").await; let resp = h.client.get("/p/some-comm/u/nobodyhere").await; assert_eq!(resp.status.as_u16(), 404, "nonexistent user should get 404"); } #[tokio::test] async fn profile_suspended_community_blocked() { let mut h = TestHarness::new().await; let user_id = h.login_as("suspendedfan").await; let comm_id = h.create_community("SuspendedComm", "suspended-comm").await; h.add_membership(user_id, comm_id, "member").await; // Suspend the community sqlx::query("UPDATE communities SET suspended_at = now() WHERE id = $1") .bind(comm_id) .execute(&h.db) .await .unwrap(); let resp = h.client.get("/p/suspended-comm/u/suspendedfan").await; assert_eq!( resp.status.as_u16(), 403, "suspended community should return 403" ); } #[tokio::test] async fn api_summary_requires_auth() { let mut h = TestHarness::new().await; // Don't log in — make unauthenticated request h.client.get("/").await; // establish session let user_id = uuid::Uuid::new_v4(); let resp = h .client .get(&format!("/api/user/{}/summary", user_id)) .await; assert_eq!(resp.status.as_u16(), 401, "unauthenticated should get 401"); } #[tokio::test] async fn api_summary_returns_memberships() { let mut h = TestHarness::new().await; let user_id = h.login_as("summaryuser").await; let comm_id = h.create_community("SummaryComm", "summary-comm").await; h.add_membership(user_id, comm_id, "member").await; let resp = h .client .get(&format!("/api/user/{}/summary", user_id)) .await; assert_eq!(resp.status.as_u16(), 200, "should return 200"); let json: serde_json::Value = resp.json(); let memberships = json["memberships"].as_array().unwrap(); assert_eq!(memberships.len(), 1, "should have one membership"); assert_eq!(memberships[0]["community_name"], "SummaryComm"); assert_eq!(memberships[0]["community_slug"], "summary-comm"); assert_eq!(memberships[0]["role"], "member"); } #[tokio::test] async fn api_summary_only_own_data() { let mut h = TestHarness::new().await; let _user_id = h.login_as("snoop").await; // Try to access another user's summary let other_id = uuid::Uuid::new_v4(); let resp = h .client .get(&format!("/api/user/{}/summary", other_id)) .await; assert_eq!( resp.status.as_u16(), 403, "accessing other user's data should return 403" ); } #[tokio::test] async fn profile_shows_endorsement_count() { let mut h = TestHarness::new().await; let author_id = h.login_as("endorsedauthor").await; let comm_id = h.create_community("EndorseComm", "endorse-comm").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(author_id, comm_id, "member").await; // Author creates a thread with a post let thread_id = h .create_thread_with_post(cat_id, author_id, "Great Thread", "Great content") .await; // Get the first post ID let posts = mt_db::queries::list_posts_in_thread(&h.db, thread_id).await.unwrap(); let post_id = posts[0].id; // Another user endorses the post let endorser_id = h.login_as("endorser1").await; h.add_membership(endorser_id, comm_id, "member").await; mt_db::mutations::toggle_endorsement(&h.db, post_id, endorser_id) .await .unwrap(); // Check the author's profile shows endorsement count let resp = h.client.get("/p/endorse-comm/u/endorsedauthor").await; assert_eq!(resp.status.as_u16(), 200); assert!( resp.text.contains("1 endorsement received"), "Profile should show endorsement count. Body: {}", &resp.text[..500.min(resp.text.len())] ); } #[tokio::test] async fn tracking_info_page_loads() { let mut h = TestHarness::new().await; h.login_as("infouser").await; let resp = h.client.get("/about/tracking").await; assert_eq!(resp.status.as_u16(), 200); assert!( resp.text.contains("How Tracking Works"), "Should show tracking info heading" ); assert!( resp.text.contains("localStorage"), "Should explain localStorage tracking" ); assert!( resp.text.contains("No third-party"), "Should mention no third-party tracking" ); }