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"] 89 .as_str() 90 .unwrap() 91 .contains("reasonType")); 92} 93 94#[tokio::test] 95async fn test_moderation_report_unauthenticated() { 96 let client = client(); 97 let report_payload = json!({ 98 "reasonType": "com.atproto.moderation.defs#reasonSpam", 99 "reason": "Spam report", 100 "subject": { 101 "$type": "com.atproto.admin.defs#repoRef", 102 "did": "did:plc:test" 103 } 104 }); 105 let res = client 106 .post(format!( 107 "{}/xrpc/com.atproto.moderation.createReport", 108 base_url().await 109 )) 110 .json(&report_payload) 111 .send() 112 .await 113 .expect("Failed to send request"); 114 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 115} 116 117#[tokio::test] 118async fn test_moderation_report_all_reason_types() { 119 let client = client(); 120 let (alice_did, alice_jwt) = setup_new_user("alice-all-reasons").await; 121 let (bob_did, _) = setup_new_user("bob-all-reasons").await; 122 let reason_types = [ 123 "com.atproto.moderation.defs#reasonSpam", 124 "com.atproto.moderation.defs#reasonViolation", 125 "com.atproto.moderation.defs#reasonMisleading", 126 "com.atproto.moderation.defs#reasonSexual", 127 "com.atproto.moderation.defs#reasonRude", 128 "com.atproto.moderation.defs#reasonOther", 129 "com.atproto.moderation.defs#reasonAppeal", 130 ]; 131 for reason_type in reason_types { 132 let report_payload = json!({ 133 "reasonType": reason_type, 134 "subject": { 135 "$type": "com.atproto.admin.defs#repoRef", 136 "did": bob_did 137 } 138 }); 139 let res = client 140 .post(format!( 141 "{}/xrpc/com.atproto.moderation.createReport", 142 base_url().await 143 )) 144 .bearer_auth(&alice_jwt) 145 .json(&report_payload) 146 .send() 147 .await 148 .expect("Failed to send request"); 149 assert_eq!( 150 res.status(), 151 StatusCode::OK, 152 "Failed for reason type: {}", 153 reason_type 154 ); 155 let body: Value = res.json().await.unwrap(); 156 assert_eq!(body["reasonType"], reason_type); 157 assert_eq!(body["reportedBy"], alice_did); 158 } 159} 160 161#[tokio::test] 162async fn test_moderation_report_takendown_user_can_appeal() { 163 let client = client(); 164 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 165 let (target_jwt, target_did) = create_account_and_login(&client).await; 166 let takedown_payload = json!({ 167 "subject": { 168 "$type": "com.atproto.admin.defs#repoRef", 169 "did": target_did 170 }, 171 "takedown": { 172 "applied": true, 173 "ref": "mod-action-test" 174 } 175 }); 176 let takedown_res = client 177 .post(format!( 178 "{}/xrpc/com.atproto.admin.updateSubjectStatus", 179 base_url().await 180 )) 181 .bearer_auth(&admin_jwt) 182 .json(&takedown_payload) 183 .send() 184 .await 185 .expect("Failed to takedown"); 186 assert_eq!(takedown_res.status(), StatusCode::OK); 187 let appeal_payload = json!({ 188 "reasonType": "com.atproto.moderation.defs#reasonAppeal", 189 "reason": "I believe this takedown was a mistake", 190 "subject": { 191 "$type": "com.atproto.admin.defs#repoRef", 192 "did": target_did 193 } 194 }); 195 let appeal_res = client 196 .post(format!( 197 "{}/xrpc/com.atproto.moderation.createReport", 198 base_url().await 199 )) 200 .bearer_auth(&target_jwt) 201 .json(&appeal_payload) 202 .send() 203 .await 204 .expect("Failed to send appeal"); 205 assert_eq!( 206 appeal_res.status(), 207 StatusCode::OK, 208 "Takendown user should be able to file appeal reports" 209 ); 210 let appeal_body: Value = appeal_res.json().await.unwrap(); 211 assert_eq!( 212 appeal_body["reasonType"], 213 "com.atproto.moderation.defs#reasonAppeal" 214 ); 215 assert_eq!(appeal_body["reportedBy"], target_did); 216} 217 218#[tokio::test] 219async fn test_moderation_report_takendown_user_cannot_file_non_appeal() { 220 let client = client(); 221 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 222 let (target_jwt, target_did) = create_account_and_login(&client).await; 223 let takedown_payload = json!({ 224 "subject": { 225 "$type": "com.atproto.admin.defs#repoRef", 226 "did": target_did 227 }, 228 "takedown": { 229 "applied": true, 230 "ref": "mod-action-test-non-appeal" 231 } 232 }); 233 let takedown_res = client 234 .post(format!( 235 "{}/xrpc/com.atproto.admin.updateSubjectStatus", 236 base_url().await 237 )) 238 .bearer_auth(&admin_jwt) 239 .json(&takedown_payload) 240 .send() 241 .await 242 .expect("Failed to takedown"); 243 assert_eq!(takedown_res.status(), StatusCode::OK); 244 let report_payload = json!({ 245 "reasonType": "com.atproto.moderation.defs#reasonSpam", 246 "reason": "Trying to report spam", 247 "subject": { 248 "$type": "com.atproto.admin.defs#repoRef", 249 "did": "did:plc:test" 250 } 251 }); 252 let report_res = client 253 .post(format!( 254 "{}/xrpc/com.atproto.moderation.createReport", 255 base_url().await 256 )) 257 .bearer_auth(&target_jwt) 258 .json(&report_payload) 259 .send() 260 .await 261 .expect("Failed to send report"); 262 assert_eq!( 263 report_res.status(), 264 StatusCode::BAD_REQUEST, 265 "Takendown user should not be able to file non-appeal reports" 266 ); 267 let body: Value = report_res.json().await.unwrap(); 268 assert_eq!(body["error"], "InvalidRequest"); 269 assert!(body["message"] 270 .as_str() 271 .unwrap() 272 .contains("takendown")); 273}