this repo has no description
1mod common; 2mod helpers; 3use common::*; 4use helpers::*; 5use reqwest::StatusCode; 6use serde_json::{Value, json}; 7 8#[tokio::test] 9async fn test_moderation_report_lifecycle() { 10 let client = client(); 11 let (alice_did, alice_jwt) = setup_new_user("alice-report").await; 12 let (bob_did, bob_jwt) = setup_new_user("bob-report").await; 13 let (post_uri, post_cid) = 14 create_post(&client, &bob_did, &bob_jwt, "This is a reportable post").await; 15 let report_payload = json!({ 16 "reasonType": "com.atproto.moderation.defs#reasonSpam", 17 "reason": "This looks like spam to me", 18 "subject": { 19 "$type": "com.atproto.repo.strongRef", 20 "uri": post_uri, 21 "cid": post_cid 22 } 23 }); 24 let report_res = client 25 .post(format!( 26 "{}/xrpc/com.atproto.moderation.createReport", 27 base_url().await 28 )) 29 .bearer_auth(&alice_jwt) 30 .json(&report_payload) 31 .send() 32 .await 33 .expect("Failed to create report"); 34 assert_eq!(report_res.status(), StatusCode::OK); 35 let report_body: Value = report_res.json().await.unwrap(); 36 assert!(report_body["id"].is_number(), "Report should have an ID"); 37 assert_eq!( 38 report_body["reasonType"], 39 "com.atproto.moderation.defs#reasonSpam" 40 ); 41 assert_eq!(report_body["reportedBy"], alice_did); 42 let account_report_payload = json!({ 43 "reasonType": "com.atproto.moderation.defs#reasonOther", 44 "reason": "Suspicious account activity", 45 "subject": { 46 "$type": "com.atproto.admin.defs#repoRef", 47 "did": bob_did 48 } 49 }); 50 let account_report_res = client 51 .post(format!( 52 "{}/xrpc/com.atproto.moderation.createReport", 53 base_url().await 54 )) 55 .bearer_auth(&alice_jwt) 56 .json(&account_report_payload) 57 .send() 58 .await 59 .expect("Failed to create account report"); 60 assert_eq!(account_report_res.status(), StatusCode::OK); 61} 62 63#[tokio::test] 64async fn test_moderation_report_invalid_reason_type() { 65 let client = client(); 66 let (alice_did, alice_jwt) = setup_new_user("alice-invalid-reason").await; 67 let report_payload = json!({ 68 "reasonType": "invalid.reason.type", 69 "reason": "Testing invalid reason", 70 "subject": { 71 "$type": "com.atproto.admin.defs#repoRef", 72 "did": alice_did 73 } 74 }); 75 let res = client 76 .post(format!( 77 "{}/xrpc/com.atproto.moderation.createReport", 78 base_url().await 79 )) 80 .bearer_auth(&alice_jwt) 81 .json(&report_payload) 82 .send() 83 .await 84 .expect("Failed to send request"); 85 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 86 let body: Value = res.json().await.unwrap(); 87 assert_eq!(body["error"], "InvalidRequest"); 88 assert!(body["message"].as_str().unwrap().contains("reasonType")); 89} 90 91#[tokio::test] 92async fn test_moderation_report_unauthenticated() { 93 let client = client(); 94 let report_payload = json!({ 95 "reasonType": "com.atproto.moderation.defs#reasonSpam", 96 "reason": "Spam report", 97 "subject": { 98 "$type": "com.atproto.admin.defs#repoRef", 99 "did": "did:plc:test" 100 } 101 }); 102 let res = client 103 .post(format!( 104 "{}/xrpc/com.atproto.moderation.createReport", 105 base_url().await 106 )) 107 .json(&report_payload) 108 .send() 109 .await 110 .expect("Failed to send request"); 111 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 112} 113 114#[tokio::test] 115async fn test_moderation_report_all_reason_types() { 116 let client = client(); 117 let (alice_did, alice_jwt) = setup_new_user("alice-all-reasons").await; 118 let (bob_did, _) = setup_new_user("bob-all-reasons").await; 119 let reason_types = [ 120 "com.atproto.moderation.defs#reasonSpam", 121 "com.atproto.moderation.defs#reasonViolation", 122 "com.atproto.moderation.defs#reasonMisleading", 123 "com.atproto.moderation.defs#reasonSexual", 124 "com.atproto.moderation.defs#reasonRude", 125 "com.atproto.moderation.defs#reasonOther", 126 "com.atproto.moderation.defs#reasonAppeal", 127 ]; 128 for reason_type in reason_types { 129 let report_payload = json!({ 130 "reasonType": reason_type, 131 "subject": { 132 "$type": "com.atproto.admin.defs#repoRef", 133 "did": bob_did 134 } 135 }); 136 let res = client 137 .post(format!( 138 "{}/xrpc/com.atproto.moderation.createReport", 139 base_url().await 140 )) 141 .bearer_auth(&alice_jwt) 142 .json(&report_payload) 143 .send() 144 .await 145 .expect("Failed to send request"); 146 assert_eq!( 147 res.status(), 148 StatusCode::OK, 149 "Failed for reason type: {}", 150 reason_type 151 ); 152 let body: Value = res.json().await.unwrap(); 153 assert_eq!(body["reasonType"], reason_type); 154 assert_eq!(body["reportedBy"], alice_did); 155 } 156} 157 158#[tokio::test] 159async fn test_moderation_report_takendown_user_can_appeal() { 160 let client = client(); 161 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 162 let (target_jwt, target_did) = create_account_and_login(&client).await; 163 let takedown_payload = json!({ 164 "subject": { 165 "$type": "com.atproto.admin.defs#repoRef", 166 "did": target_did 167 }, 168 "takedown": { 169 "applied": true, 170 "ref": "mod-action-test" 171 } 172 }); 173 let takedown_res = client 174 .post(format!( 175 "{}/xrpc/com.atproto.admin.updateSubjectStatus", 176 base_url().await 177 )) 178 .bearer_auth(&admin_jwt) 179 .json(&takedown_payload) 180 .send() 181 .await 182 .expect("Failed to takedown"); 183 assert_eq!(takedown_res.status(), StatusCode::OK); 184 let appeal_payload = json!({ 185 "reasonType": "com.atproto.moderation.defs#reasonAppeal", 186 "reason": "I believe this takedown was a mistake", 187 "subject": { 188 "$type": "com.atproto.admin.defs#repoRef", 189 "did": target_did 190 } 191 }); 192 let appeal_res = client 193 .post(format!( 194 "{}/xrpc/com.atproto.moderation.createReport", 195 base_url().await 196 )) 197 .bearer_auth(&target_jwt) 198 .json(&appeal_payload) 199 .send() 200 .await 201 .expect("Failed to send appeal"); 202 assert_eq!( 203 appeal_res.status(), 204 StatusCode::OK, 205 "Takendown user should be able to file appeal reports" 206 ); 207 let appeal_body: Value = appeal_res.json().await.unwrap(); 208 assert_eq!( 209 appeal_body["reasonType"], 210 "com.atproto.moderation.defs#reasonAppeal" 211 ); 212 assert_eq!(appeal_body["reportedBy"], target_did); 213} 214 215#[tokio::test] 216async fn test_moderation_report_takendown_user_cannot_file_non_appeal() { 217 let client = client(); 218 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 219 let (target_jwt, target_did) = create_account_and_login(&client).await; 220 let takedown_payload = json!({ 221 "subject": { 222 "$type": "com.atproto.admin.defs#repoRef", 223 "did": target_did 224 }, 225 "takedown": { 226 "applied": true, 227 "ref": "mod-action-test-non-appeal" 228 } 229 }); 230 let takedown_res = client 231 .post(format!( 232 "{}/xrpc/com.atproto.admin.updateSubjectStatus", 233 base_url().await 234 )) 235 .bearer_auth(&admin_jwt) 236 .json(&takedown_payload) 237 .send() 238 .await 239 .expect("Failed to takedown"); 240 assert_eq!(takedown_res.status(), StatusCode::OK); 241 let report_payload = json!({ 242 "reasonType": "com.atproto.moderation.defs#reasonSpam", 243 "reason": "Trying to report spam", 244 "subject": { 245 "$type": "com.atproto.admin.defs#repoRef", 246 "did": "did:plc:test" 247 } 248 }); 249 let report_res = client 250 .post(format!( 251 "{}/xrpc/com.atproto.moderation.createReport", 252 base_url().await 253 )) 254 .bearer_auth(&target_jwt) 255 .json(&report_payload) 256 .send() 257 .await 258 .expect("Failed to send report"); 259 assert_eq!( 260 report_res.status(), 261 StatusCode::BAD_REQUEST, 262 "Takendown user should not be able to file non-appeal reports" 263 ); 264 let body: Value = report_res.json().await.unwrap(); 265 assert_eq!(body["error"], "InvalidRequest"); 266 assert!(body["message"].as_str().unwrap().contains("takendown")); 267}