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!("{}/xrpc/com.atproto.server.deactivateAccount", base)) 206 .bearer_auth(&jwt) 207 .json(&serde_json::json!({})) 208 .send() 209 .await 210 .unwrap(); 211 let deact_res = client 212 .get(format!("{}/xrpc/com.atproto.sync.getHead", base)) 213 .query(&[("did", did.as_str())]) 214 .send() 215 .await 216 .unwrap(); 217 assert_eq!(deact_res.status(), StatusCode::BAD_REQUEST); 218 let body: Value = deact_res.json().await.unwrap(); 219 assert_eq!(body["error"], "RepoDeactivated"); 220} 221 222#[tokio::test] 223async fn test_get_head_takendown_account_returns_error() { 224 let client = client(); 225 let base = base_url().await; 226 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 227 let (_, target_did) = create_account_and_login(&client).await; 228 let res = client 229 .get(format!("{}/xrpc/com.atproto.sync.getHead", base)) 230 .query(&[("did", target_did.as_str())]) 231 .send() 232 .await 233 .unwrap(); 234 assert_eq!(res.status(), StatusCode::OK); 235 client 236 .post(format!("{}/xrpc/com.atproto.admin.updateSubjectStatus", base)) 237 .bearer_auth(&admin_jwt) 238 .json(&serde_json::json!({ 239 "subject": { 240 "$type": "com.atproto.admin.defs#repoRef", 241 "did": target_did 242 }, 243 "takedown": { 244 "applied": true, 245 "ref": "test-takedown" 246 } 247 })) 248 .send() 249 .await 250 .unwrap(); 251 let takedown_res = client 252 .get(format!("{}/xrpc/com.atproto.sync.getHead", base)) 253 .query(&[("did", target_did.as_str())]) 254 .send() 255 .await 256 .unwrap(); 257 assert_eq!(takedown_res.status(), StatusCode::BAD_REQUEST); 258 let body: Value = takedown_res.json().await.unwrap(); 259 assert_eq!(body["error"], "RepoTakendown"); 260} 261 262#[tokio::test] 263async fn test_get_head_admin_can_access_deactivated() { 264 let client = client(); 265 let base = base_url().await; 266 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 267 let (user_jwt, did) = create_account_and_login(&client).await; 268 client 269 .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base)) 270 .bearer_auth(&user_jwt) 271 .json(&serde_json::json!({})) 272 .send() 273 .await 274 .unwrap(); 275 let res = client 276 .get(format!("{}/xrpc/com.atproto.sync.getHead", base)) 277 .bearer_auth(&admin_jwt) 278 .query(&[("did", did.as_str())]) 279 .send() 280 .await 281 .unwrap(); 282 assert_eq!(res.status(), StatusCode::OK); 283} 284 285#[tokio::test] 286async fn test_get_checkout_deactivated_account_returns_error() { 287 let client = client(); 288 let base = base_url().await; 289 let (did, jwt) = setup_new_user("deactcheckouttest").await; 290 let res = client 291 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base)) 292 .query(&[("did", did.as_str())]) 293 .send() 294 .await 295 .unwrap(); 296 assert_eq!(res.status(), StatusCode::OK); 297 client 298 .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base)) 299 .bearer_auth(&jwt) 300 .json(&serde_json::json!({})) 301 .send() 302 .await 303 .unwrap(); 304 let deact_res = client 305 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base)) 306 .query(&[("did", did.as_str())]) 307 .send() 308 .await 309 .unwrap(); 310 assert_eq!(deact_res.status(), StatusCode::BAD_REQUEST); 311 let body: Value = deact_res.json().await.unwrap(); 312 assert_eq!(body["error"], "RepoDeactivated"); 313} 314 315#[tokio::test] 316async fn test_get_checkout_takendown_account_returns_error() { 317 let client = client(); 318 let base = base_url().await; 319 let (admin_jwt, _) = create_admin_account_and_login(&client).await; 320 let (_, target_did) = create_account_and_login(&client).await; 321 let res = client 322 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base)) 323 .query(&[("did", target_did.as_str())]) 324 .send() 325 .await 326 .unwrap(); 327 assert_eq!(res.status(), StatusCode::OK); 328 client 329 .post(format!("{}/xrpc/com.atproto.admin.updateSubjectStatus", base)) 330 .bearer_auth(&admin_jwt) 331 .json(&serde_json::json!({ 332 "subject": { 333 "$type": "com.atproto.admin.defs#repoRef", 334 "did": target_did 335 }, 336 "takedown": { 337 "applied": true, 338 "ref": "test-takedown" 339 } 340 })) 341 .send() 342 .await 343 .unwrap(); 344 let takedown_res = client 345 .get(format!("{}/xrpc/com.atproto.sync.getCheckout", base)) 346 .query(&[("did", target_did.as_str())]) 347 .send() 348 .await 349 .unwrap(); 350 assert_eq!(takedown_res.status(), StatusCode::BAD_REQUEST); 351 let body: Value = takedown_res.json().await.unwrap(); 352 assert_eq!(body["error"], "RepoTakendown"); 353}