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}