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}