use crate::harness::TestHarness; #[tokio::test] async fn track_thread_happy_path() { let mut h = TestHarness::new().await; let user_id = h.login_as("tracker").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let thread_id = h .create_thread_with_post(cat_id, user_id, "Track Me", "Content") .await; let thread_url = format!("/p/test/general/{}", thread_id); h.client.get(&thread_url).await; let track_url = format!("/p/test/general/{}/track", thread_id); let resp = h.client.post_form(&track_url, "").await; assert!(resp.status.is_redirection(), "Expected redirect, got {}", resp.status); let tracked = mt_db::queries::is_thread_tracked(&h.db, user_id, thread_id) .await .unwrap(); assert!(tracked, "Thread should be tracked"); } #[tokio::test] async fn untrack_thread() { let mut h = TestHarness::new().await; let user_id = h.login_as("untracker").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let thread_id = h .create_thread_with_post(cat_id, user_id, "Untrack Me", "Content") .await; // Track first mt_db::mutations::track_thread(&h.db, user_id, thread_id).await.unwrap(); let thread_url = format!("/p/test/general/{}", thread_id); h.client.get(&thread_url).await; let untrack_url = format!("/p/test/general/{}/untrack", thread_id); let resp = h.client.post_form(&untrack_url, "").await; assert!(resp.status.is_redirection(), "Expected redirect, got {}", resp.status); let tracked = mt_db::queries::is_thread_tracked(&h.db, user_id, thread_id) .await .unwrap(); assert!(!tracked, "Thread should not be tracked"); } #[tokio::test] async fn read_position_updates_on_view() { let mut h = TestHarness::new().await; let user_id = h.login_as("readpos").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let thread_id = h .create_thread_with_post(cat_id, user_id, "Read Pos", "First post") .await; // Track the thread mt_db::mutations::track_thread(&h.db, user_id, thread_id).await.unwrap(); // View thread — should update read position let thread_url = format!("/p/test/general/{}", thread_id); h.client.get(&thread_url).await; // Check last_read_post_id is set let row: Option<(Option,)> = sqlx::query_as( "SELECT last_read_post_id FROM tracked_threads WHERE user_id = $1 AND thread_id = $2", ) .bind(user_id) .bind(thread_id) .fetch_optional(&h.db) .await .unwrap(); assert!(row.is_some(), "Tracking row should exist"); assert!(row.unwrap().0.is_some(), "last_read_post_id should be set after viewing"); } #[tokio::test] async fn unread_count_tracking() { let mut h = TestHarness::new().await; let user_id = h.login_as("unreadcount").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let thread_id = h .create_thread_with_post(cat_id, user_id, "Unread Count", "First post") .await; // Track and view to set read position mt_db::mutations::track_thread(&h.db, user_id, thread_id).await.unwrap(); let thread_url = format!("/p/test/general/{}", thread_id); h.client.get(&thread_url).await; // Add a new post by another user let other_id = uuid::Uuid::new_v4(); sqlx::query("INSERT INTO users (mnw_account_id, username, display_name) VALUES ($1, $2, $2)") .bind(other_id) .bind("poster2") .execute(&h.db) .await .unwrap(); mt_db::mutations::create_post(&h.db, thread_id, other_id, "New reply", "

New reply

", true) .await .unwrap(); // Check tracked threads — should show unread let tracked = mt_db::queries::list_tracked_threads(&h.db, user_id).await.unwrap(); assert_eq!(tracked.len(), 1); assert!(tracked[0].unread_count > 0, "Should have unread posts"); } #[tokio::test] async fn stop_tracking_all() { let mut h = TestHarness::new().await; let user_id = h.login_as("stopall").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let t1 = h.create_thread_with_post(cat_id, user_id, "Thread 1", "content").await; let t2 = h.create_thread_with_post(cat_id, user_id, "Thread 2", "content").await; mt_db::mutations::track_thread(&h.db, user_id, t1).await.unwrap(); mt_db::mutations::track_thread(&h.db, user_id, t2).await.unwrap(); h.client.get("/tracked").await; let resp = h.client.post_form("/tracked/stop-all", "").await; assert!(resp.status.is_redirection(), "Expected redirect, got {}", resp.status); let tracked = mt_db::queries::list_tracked_threads(&h.db, user_id).await.unwrap(); assert_eq!(tracked.len(), 0, "All tracked threads should be removed"); } #[tokio::test] async fn track_requires_login() { let mut h = TestHarness::new().await; let user_id = h.login_as("trackloginuser").await; let comm_id = h.create_community("Test", "test").await; let cat_id = h.create_category(comm_id, "General", "general").await; h.add_membership(user_id, comm_id, "member").await; let thread_id = h .create_thread_with_post(cat_id, user_id, "Login Track", "content") .await; // New harness without login let mut h2 = TestHarness::new().await; h2.client.get("/").await; let track_url = format!("/p/test/general/{}/track", thread_id); let resp = h2.client.post_form(&track_url, "").await; assert!( resp.status.is_redirection(), "Expected redirect to login, got {}", resp.status ); }