//! Broadcast workflow tests: creator sends email to followers. //! //! Note: broadcast validation errors return 200 with error HTML (HTMX pattern), //! so we check body content for "form-status error" instead of status codes. use crate::harness::TestHarness; #[tokio::test] async fn broadcast_requires_auth() { let mut h = TestHarness::new().await; let resp = h .client .post_form( "/api/broadcast", "subject=Hello&body=Test+broadcast+message", ) .await; assert!( resp.status.is_client_error() || resp.status.is_redirection(), "Unauthenticated broadcast should be rejected: {} {}", resp.status, resp.text ); } #[tokio::test] async fn broadcast_requires_creator() { let mut h = TestHarness::new().await; let _user_id = h.signup("bcastuser", "bcastuser@test.com", "password123").await; // Regular user (not a creator) — returns 200 with error HTML let resp = h .client .post_form( "/api/broadcast", "subject=Hello&body=Test+broadcast+message", ) .await; assert!( resp.text.contains("form-status error"), "Non-creator broadcast should return error: {}", resp.text ); } #[tokio::test] async fn broadcast_empty_subject_rejected() { let mut h = TestHarness::new().await; let user_id = h.signup("bcastempty", "bcastempty@test.com", "password123").await; h.grant_creator(user_id).await; h.client.post_form("/logout", "").await; h.login("bcastempty", "password123").await; let resp = h .client .post_form("/api/broadcast", "subject=&body=Some+body+text") .await; assert!( resp.text.contains("form-status error"), "Empty subject broadcast should return error: {}", resp.text ); } #[tokio::test] async fn broadcast_empty_body_rejected() { let mut h = TestHarness::new().await; let user_id = h.signup("bcastnobody", "bcastnobody@test.com", "password123").await; h.grant_creator(user_id).await; h.client.post_form("/logout", "").await; h.login("bcastnobody", "password123").await; let resp = h .client .post_form("/api/broadcast", "subject=Hello&body=") .await; assert!( resp.text.contains("form-status error"), "Empty body broadcast should return error: {}", resp.text ); } #[tokio::test] async fn broadcast_succeeds_for_creator() { let mut h = TestHarness::new().await; let user_id = h.signup("bcastok", "bcastok@test.com", "password123").await; h.grant_creator(user_id).await; h.client.post_form("/logout", "").await; h.login("bcastok", "password123").await; let resp = h .client .post_form( "/api/broadcast", "subject=Test+Broadcast&body=Hello+followers+this+is+a+test", ) .await; assert!( resp.status.is_success(), "Creator broadcast should succeed: {} {}", resp.status, resp.text ); // Success response should contain "form-status success", not error assert!( !resp.text.contains("form-status error"), "Successful broadcast should not contain error: {}", resp.text ); } #[tokio::test] async fn broadcast_subject_too_long_rejected() { let mut h = TestHarness::new().await; let user_id = h.signup("bcastlong", "bcastlong@test.com", "password123").await; h.grant_creator(user_id).await; h.client.post_form("/logout", "").await; h.login("bcastlong", "password123").await; let long_subject = "a".repeat(201); let resp = h .client .post_form( "/api/broadcast", &format!("subject={}&body=Some+body", long_subject), ) .await; assert!( resp.text.contains("form-status error"), "Subject >200 chars should return error: {}", resp.text ); }