this repo has no description
1mod common;
2mod helpers;
3use common::*;
4use helpers::*;
5use reqwest::StatusCode;
6use serde_json::Value;
7
8#[tokio::test]
9async fn test_get_head_comprehensive() {
10 let client = client();
11 let (did, jwt) = setup_new_user("gethead").await;
12 let res = client
13 .get(format!(
14 "{}/xrpc/com.atproto.sync.getHead",
15 base_url().await
16 ))
17 .query(&[("did", did.as_str())])
18 .send()
19 .await
20 .expect("Failed to send request");
21 assert_eq!(res.status(), StatusCode::OK);
22 let body: Value = res.json().await.expect("Response was not valid JSON");
23 assert!(body["root"].is_string());
24 let root1 = body["root"].as_str().unwrap().to_string();
25 assert!(root1.starts_with("bafy"), "Root CID should be a CID");
26 let latest_res = client
27 .get(format!(
28 "{}/xrpc/com.atproto.sync.getLatestCommit",
29 base_url().await
30 ))
31 .query(&[("did", did.as_str())])
32 .send()
33 .await
34 .expect("Failed to get latest commit");
35 let latest_body: Value = latest_res.json().await.unwrap();
36 let latest_cid = latest_body["cid"].as_str().unwrap();
37 assert_eq!(
38 root1, latest_cid,
39 "getHead root should match getLatestCommit cid"
40 );
41 create_post(&client, &did, &jwt, "Post to change head").await;
42 let res2 = client
43 .get(format!(
44 "{}/xrpc/com.atproto.sync.getHead",
45 base_url().await
46 ))
47 .query(&[("did", did.as_str())])
48 .send()
49 .await
50 .expect("Failed to get head after record");
51 let body2: Value = res2.json().await.unwrap();
52 let root2 = body2["root"].as_str().unwrap().to_string();
53 assert_ne!(root1, root2, "Head CID should change after record creation");
54 let not_found_res = client
55 .get(format!(
56 "{}/xrpc/com.atproto.sync.getHead",
57 base_url().await
58 ))
59 .query(&[("did", "did:plc:nonexistent12345")])
60 .send()
61 .await
62 .expect("Failed to send request");
63 assert_eq!(not_found_res.status(), StatusCode::BAD_REQUEST);
64 let error_body: Value = not_found_res.json().await.unwrap();
65 assert_eq!(error_body["error"], "RepoNotFound");
66 let missing_res = client
67 .get(format!(
68 "{}/xrpc/com.atproto.sync.getHead",
69 base_url().await
70 ))
71 .send()
72 .await
73 .expect("Failed to send request");
74 assert_eq!(missing_res.status(), StatusCode::BAD_REQUEST);
75 let empty_res = client
76 .get(format!(
77 "{}/xrpc/com.atproto.sync.getHead",
78 base_url().await
79 ))
80 .query(&[("did", "")])
81 .send()
82 .await
83 .expect("Failed to send request");
84 assert_eq!(empty_res.status(), StatusCode::BAD_REQUEST);
85 let whitespace_res = client
86 .get(format!(
87 "{}/xrpc/com.atproto.sync.getHead",
88 base_url().await
89 ))
90 .query(&[("did", " ")])
91 .send()
92 .await
93 .expect("Failed to send request");
94 assert_eq!(whitespace_res.status(), StatusCode::BAD_REQUEST);
95}
96
97#[tokio::test]
98async fn test_get_checkout_comprehensive() {
99 let client = client();
100 let (did, jwt) = setup_new_user("getcheckout").await;
101 let empty_res = client
102 .get(format!(
103 "{}/xrpc/com.atproto.sync.getCheckout",
104 base_url().await
105 ))
106 .query(&[("did", did.as_str())])
107 .send()
108 .await
109 .expect("Failed to send request");
110 assert_eq!(empty_res.status(), StatusCode::OK);
111 let empty_body = empty_res.bytes().await.expect("Failed to get body");
112 assert!(
113 !empty_body.is_empty(),
114 "Even empty repo should return CAR header"
115 );
116 create_post(&client, &did, &jwt, "Post for checkout test").await;
117 let res = client
118 .get(format!(
119 "{}/xrpc/com.atproto.sync.getCheckout",
120 base_url().await
121 ))
122 .query(&[("did", did.as_str())])
123 .send()
124 .await
125 .expect("Failed to send request");
126 assert_eq!(res.status(), StatusCode::OK);
127 assert_eq!(
128 res.headers()
129 .get("content-type")
130 .and_then(|h| h.to_str().ok()),
131 Some("application/vnd.ipld.car")
132 );
133 let body = res.bytes().await.expect("Failed to get body");
134 assert!(!body.is_empty(), "CAR file should not be empty");
135 assert!(body.len() > 50, "CAR file should contain actual data");
136 assert!(
137 body.len() >= 2,
138 "CAR file should have at least header length"
139 );
140 for i in 0..4 {
141 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
142 create_post(&client, &did, &jwt, &format!("Checkout post {}", i)).await;
143 }
144 let multi_res = client
145 .get(format!(
146 "{}/xrpc/com.atproto.sync.getCheckout",
147 base_url().await
148 ))
149 .query(&[("did", did.as_str())])
150 .send()
151 .await
152 .expect("Failed to send request");
153 assert_eq!(multi_res.status(), StatusCode::OK);
154 let multi_body = multi_res.bytes().await.expect("Failed to get body");
155 assert!(
156 multi_body.len() > 500,
157 "CAR file with 5 records should be larger"
158 );
159 let not_found_res = client
160 .get(format!(
161 "{}/xrpc/com.atproto.sync.getCheckout",
162 base_url().await
163 ))
164 .query(&[("did", "did:plc:nonexistent12345")])
165 .send()
166 .await
167 .expect("Failed to send request");
168 assert_eq!(not_found_res.status(), StatusCode::BAD_REQUEST);
169 let error_body: Value = not_found_res.json().await.unwrap();
170 assert_eq!(error_body["error"], "RepoNotFound");
171 let missing_res = client
172 .get(format!(
173 "{}/xrpc/com.atproto.sync.getCheckout",
174 base_url().await
175 ))
176 .send()
177 .await
178 .expect("Failed to send request");
179 assert_eq!(missing_res.status(), StatusCode::BAD_REQUEST);
180 let empty_did_res = client
181 .get(format!(
182 "{}/xrpc/com.atproto.sync.getCheckout",
183 base_url().await
184 ))
185 .query(&[("did", "")])
186 .send()
187 .await
188 .expect("Failed to send request");
189 assert_eq!(empty_did_res.status(), StatusCode::BAD_REQUEST);
190}
191
192#[tokio::test]
193async fn test_get_head_deactivated_account_returns_error() {
194 let client = client();
195 let base = base_url().await;
196 let (did, jwt) = setup_new_user("deactheadtest").await;
197 let res = client
198 .get(format!("{}/xrpc/com.atproto.sync.getHead", base))
199 .query(&[("did", did.as_str())])
200 .send()
201 .await
202 .unwrap();
203 assert_eq!(res.status(), StatusCode::OK);
204 client
205 .post(format!(
206 "{}/xrpc/com.atproto.server.deactivateAccount",
207 base
208 ))
209 .bearer_auth(&jwt)
210 .json(&serde_json::json!({}))
211 .send()
212 .await
213 .unwrap();
214 let deact_res = client
215 .get(format!("{}/xrpc/com.atproto.sync.getHead", base))
216 .query(&[("did", did.as_str())])
217 .send()
218 .await
219 .unwrap();
220 assert_eq!(deact_res.status(), StatusCode::BAD_REQUEST);
221 let body: Value = deact_res.json().await.unwrap();
222 assert_eq!(body["error"], "RepoDeactivated");
223}
224
225#[tokio::test]
226async fn test_get_head_takendown_account_returns_error() {
227 let client = client();
228 let base = base_url().await;
229 let (admin_jwt, _) = create_admin_account_and_login(&client).await;
230 let (_, target_did) = create_account_and_login(&client).await;
231 let res = client
232 .get(format!("{}/xrpc/com.atproto.sync.getHead", base))
233 .query(&[("did", target_did.as_str())])
234 .send()
235 .await
236 .unwrap();
237 assert_eq!(res.status(), StatusCode::OK);
238 client
239 .post(format!(
240 "{}/xrpc/com.atproto.admin.updateSubjectStatus",
241 base
242 ))
243 .bearer_auth(&admin_jwt)
244 .json(&serde_json::json!({
245 "subject": {
246 "$type": "com.atproto.admin.defs#repoRef",
247 "did": target_did
248 },
249 "takedown": {
250 "applied": true,
251 "ref": "test-takedown"
252 }
253 }))
254 .send()
255 .await
256 .unwrap();
257 let takedown_res = client
258 .get(format!("{}/xrpc/com.atproto.sync.getHead", base))
259 .query(&[("did", target_did.as_str())])
260 .send()
261 .await
262 .unwrap();
263 assert_eq!(takedown_res.status(), StatusCode::BAD_REQUEST);
264 let body: Value = takedown_res.json().await.unwrap();
265 assert_eq!(body["error"], "RepoTakendown");
266}
267
268#[tokio::test]
269async fn test_get_head_admin_can_access_deactivated() {
270 let client = client();
271 let base = base_url().await;
272 let (admin_jwt, _) = create_admin_account_and_login(&client).await;
273 let (user_jwt, did) = create_account_and_login(&client).await;
274 client
275 .post(format!(
276 "{}/xrpc/com.atproto.server.deactivateAccount",
277 base
278 ))
279 .bearer_auth(&user_jwt)
280 .json(&serde_json::json!({}))
281 .send()
282 .await
283 .unwrap();
284 let res = client
285 .get(format!("{}/xrpc/com.atproto.sync.getHead", base))
286 .bearer_auth(&admin_jwt)
287 .query(&[("did", did.as_str())])
288 .send()
289 .await
290 .unwrap();
291 assert_eq!(res.status(), StatusCode::OK);
292}
293
294#[tokio::test]
295async fn test_get_checkout_deactivated_account_returns_error() {
296 let client = client();
297 let base = base_url().await;
298 let (did, jwt) = setup_new_user("deactcheckouttest").await;
299 let res = client
300 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base))
301 .query(&[("did", did.as_str())])
302 .send()
303 .await
304 .unwrap();
305 assert_eq!(res.status(), StatusCode::OK);
306 client
307 .post(format!(
308 "{}/xrpc/com.atproto.server.deactivateAccount",
309 base
310 ))
311 .bearer_auth(&jwt)
312 .json(&serde_json::json!({}))
313 .send()
314 .await
315 .unwrap();
316 let deact_res = client
317 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base))
318 .query(&[("did", did.as_str())])
319 .send()
320 .await
321 .unwrap();
322 assert_eq!(deact_res.status(), StatusCode::BAD_REQUEST);
323 let body: Value = deact_res.json().await.unwrap();
324 assert_eq!(body["error"], "RepoDeactivated");
325}
326
327#[tokio::test]
328async fn test_get_checkout_takendown_account_returns_error() {
329 let client = client();
330 let base = base_url().await;
331 let (admin_jwt, _) = create_admin_account_and_login(&client).await;
332 let (_, target_did) = create_account_and_login(&client).await;
333 let res = client
334 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base))
335 .query(&[("did", target_did.as_str())])
336 .send()
337 .await
338 .unwrap();
339 assert_eq!(res.status(), StatusCode::OK);
340 client
341 .post(format!(
342 "{}/xrpc/com.atproto.admin.updateSubjectStatus",
343 base
344 ))
345 .bearer_auth(&admin_jwt)
346 .json(&serde_json::json!({
347 "subject": {
348 "$type": "com.atproto.admin.defs#repoRef",
349 "did": target_did
350 },
351 "takedown": {
352 "applied": true,
353 "ref": "test-takedown"
354 }
355 }))
356 .send()
357 .await
358 .unwrap();
359 let takedown_res = client
360 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base))
361 .query(&[("did", target_did.as_str())])
362 .send()
363 .await
364 .unwrap();
365 assert_eq!(takedown_res.status(), StatusCode::BAD_REQUEST);
366 let body: Value = takedown_res.json().await.unwrap();
367 assert_eq!(body["error"], "RepoTakendown");
368}