PDS software with bells & whistles you didn’t even know you needed. will move this to its own account when ready.
at main 13 kB view raw
1mod common; 2mod helpers; 3 4use common::*; 5use helpers::*; 6use reqwest::StatusCode; 7use serde_json::Value; 8 9#[tokio::test] 10async fn test_get_repo_takendown_returns_error() { 11 let client = client(); 12 let (_, did) = create_account_and_login(&client).await; 13 14 set_account_takedown(&did, Some("test-takedown-ref")).await; 15 16 let res = client 17 .get(format!( 18 "{}/xrpc/com.atproto.sync.getRepo", 19 base_url().await 20 )) 21 .query(&[("did", did.as_str())]) 22 .send() 23 .await 24 .expect("Failed to send request"); 25 26 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 27 let body: Value = res.json().await.expect("Response was not valid JSON"); 28 assert_eq!(body["error"], "RepoTakendown"); 29} 30 31#[tokio::test] 32async fn test_get_repo_deactivated_returns_error() { 33 let client = client(); 34 let (_, did) = create_account_and_login(&client).await; 35 36 set_account_deactivated(&did, true).await; 37 38 let res = client 39 .get(format!( 40 "{}/xrpc/com.atproto.sync.getRepo", 41 base_url().await 42 )) 43 .query(&[("did", did.as_str())]) 44 .send() 45 .await 46 .expect("Failed to send request"); 47 48 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 49 let body: Value = res.json().await.expect("Response was not valid JSON"); 50 assert_eq!(body["error"], "RepoDeactivated"); 51} 52 53#[tokio::test] 54async fn test_get_latest_commit_takendown_returns_error() { 55 let client = client(); 56 let (_, did) = create_account_and_login(&client).await; 57 58 set_account_takedown(&did, Some("test-takedown-ref")).await; 59 60 let res = client 61 .get(format!( 62 "{}/xrpc/com.atproto.sync.getLatestCommit", 63 base_url().await 64 )) 65 .query(&[("did", did.as_str())]) 66 .send() 67 .await 68 .expect("Failed to send request"); 69 70 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 71 let body: Value = res.json().await.expect("Response was not valid JSON"); 72 assert_eq!(body["error"], "RepoTakendown"); 73} 74 75#[tokio::test] 76async fn test_get_blocks_takendown_returns_error() { 77 let client = client(); 78 let (_, did) = create_account_and_login(&client).await; 79 80 let commit_res = client 81 .get(format!( 82 "{}/xrpc/com.atproto.sync.getLatestCommit", 83 base_url().await 84 )) 85 .query(&[("did", did.as_str())]) 86 .send() 87 .await 88 .expect("Failed to get commit"); 89 let commit_body: Value = commit_res.json().await.unwrap(); 90 let cid = commit_body["cid"].as_str().unwrap(); 91 92 set_account_takedown(&did, Some("test-takedown-ref")).await; 93 94 let res = client 95 .get(format!( 96 "{}/xrpc/com.atproto.sync.getBlocks", 97 base_url().await 98 )) 99 .query(&[("did", did.as_str()), ("cids", cid)]) 100 .send() 101 .await 102 .expect("Failed to send request"); 103 104 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 105 let body: Value = res.json().await.expect("Response was not valid JSON"); 106 assert_eq!(body["error"], "RepoTakendown"); 107} 108 109#[tokio::test] 110async fn test_get_repo_status_shows_takendown_status() { 111 let client = client(); 112 let (_, did) = create_account_and_login(&client).await; 113 114 set_account_takedown(&did, Some("test-takedown-ref")).await; 115 116 let res = client 117 .get(format!( 118 "{}/xrpc/com.atproto.sync.getRepoStatus", 119 base_url().await 120 )) 121 .query(&[("did", did.as_str())]) 122 .send() 123 .await 124 .expect("Failed to send request"); 125 126 assert_eq!(res.status(), StatusCode::OK); 127 let body: Value = res.json().await.expect("Response was not valid JSON"); 128 assert_eq!(body["active"], false); 129 assert_eq!(body["status"], "takendown"); 130 assert!(body.get("rev").is_none() || body["rev"].is_null()); 131} 132 133#[tokio::test] 134async fn test_get_repo_status_shows_deactivated_status() { 135 let client = client(); 136 let (_, did) = create_account_and_login(&client).await; 137 138 set_account_deactivated(&did, true).await; 139 140 let res = client 141 .get(format!( 142 "{}/xrpc/com.atproto.sync.getRepoStatus", 143 base_url().await 144 )) 145 .query(&[("did", did.as_str())]) 146 .send() 147 .await 148 .expect("Failed to send request"); 149 150 assert_eq!(res.status(), StatusCode::OK); 151 let body: Value = res.json().await.expect("Response was not valid JSON"); 152 assert_eq!(body["active"], false); 153 assert_eq!(body["status"], "deactivated"); 154} 155 156#[tokio::test] 157async fn test_list_repos_shows_status_field() { 158 let client = client(); 159 let (_, did) = create_account_and_login(&client).await; 160 161 set_account_takedown(&did, Some("test-takedown-ref")).await; 162 163 let res = client 164 .get(format!( 165 "{}/xrpc/com.atproto.sync.listRepos?limit=1000", 166 base_url().await 167 )) 168 .send() 169 .await 170 .expect("Failed to send request"); 171 172 assert_eq!(res.status(), StatusCode::OK); 173 let body: Value = res.json().await.expect("Response was not valid JSON"); 174 let repos = body["repos"].as_array().unwrap(); 175 176 let takendown_repo = repos.iter().find(|r| r["did"] == did); 177 assert!(takendown_repo.is_some(), "Takendown repo should be in list"); 178 let repo = takendown_repo.unwrap(); 179 assert_eq!(repo["active"], false); 180 assert_eq!(repo["status"], "takendown"); 181} 182 183#[tokio::test] 184async fn test_get_blob_takendown_returns_error() { 185 let client = client(); 186 let (jwt, did) = create_account_and_login(&client).await; 187 188 let blob_res = client 189 .post(format!( 190 "{}/xrpc/com.atproto.repo.uploadBlob", 191 base_url().await 192 )) 193 .header("Content-Type", "image/png") 194 .bearer_auth(&jwt) 195 .body(vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) 196 .send() 197 .await 198 .expect("Failed to upload blob"); 199 let blob_body: Value = blob_res.json().await.unwrap(); 200 let cid = blob_body["blob"]["ref"]["$link"].as_str().unwrap(); 201 202 set_account_takedown(&did, Some("test-takedown-ref")).await; 203 204 let res = client 205 .get(format!( 206 "{}/xrpc/com.atproto.sync.getBlob", 207 base_url().await 208 )) 209 .query(&[("did", did.as_str()), ("cid", cid)]) 210 .send() 211 .await 212 .expect("Failed to send request"); 213 214 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 215 let body: Value = res.json().await.expect("Response was not valid JSON"); 216 assert_eq!(body["error"], "RepoTakendown"); 217} 218 219#[tokio::test] 220async fn test_get_blob_has_security_headers() { 221 let client = client(); 222 let (jwt, did) = create_account_and_login(&client).await; 223 224 let blob_res = client 225 .post(format!( 226 "{}/xrpc/com.atproto.repo.uploadBlob", 227 base_url().await 228 )) 229 .header("Content-Type", "image/png") 230 .bearer_auth(&jwt) 231 .body(vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) 232 .send() 233 .await 234 .expect("Failed to upload blob"); 235 let blob_body: Value = blob_res.json().await.unwrap(); 236 let cid = blob_body["blob"]["ref"]["$link"].as_str().unwrap(); 237 238 let res = client 239 .get(format!( 240 "{}/xrpc/com.atproto.sync.getBlob", 241 base_url().await 242 )) 243 .query(&[("did", did.as_str()), ("cid", cid)]) 244 .send() 245 .await 246 .expect("Failed to send request"); 247 248 assert_eq!(res.status(), StatusCode::OK); 249 250 let headers = res.headers(); 251 assert_eq!( 252 headers 253 .get("x-content-type-options") 254 .map(|v| v.to_str().unwrap()), 255 Some("nosniff"), 256 "Missing x-content-type-options: nosniff header" 257 ); 258 assert_eq!( 259 headers 260 .get("content-security-policy") 261 .map(|v| v.to_str().unwrap()), 262 Some("default-src 'none'; sandbox"), 263 "Missing content-security-policy header" 264 ); 265 assert!( 266 headers.get("content-length").is_some(), 267 "Missing content-length header" 268 ); 269} 270 271#[tokio::test] 272async fn test_get_blocks_missing_cids_returns_error() { 273 let client = client(); 274 let (_, did) = create_account_and_login(&client).await; 275 276 let fake_cid = "bafyreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu"; 277 278 let res = client 279 .get(format!( 280 "{}/xrpc/com.atproto.sync.getBlocks", 281 base_url().await 282 )) 283 .query(&[("did", did.as_str()), ("cids", fake_cid)]) 284 .send() 285 .await 286 .expect("Failed to send request"); 287 288 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 289 let body: Value = res.json().await.expect("Response was not valid JSON"); 290 assert_eq!(body["error"], "InvalidRequest"); 291 assert!( 292 body["message"] 293 .as_str() 294 .unwrap() 295 .contains("Could not find blocks"), 296 "Error message should mention missing blocks" 297 ); 298} 299 300#[tokio::test] 301async fn test_get_blocks_accepts_array_format() { 302 let client = client(); 303 let (_, did) = create_account_and_login(&client).await; 304 305 let commit_res = client 306 .get(format!( 307 "{}/xrpc/com.atproto.sync.getLatestCommit", 308 base_url().await 309 )) 310 .query(&[("did", did.as_str())]) 311 .send() 312 .await 313 .expect("Failed to get commit"); 314 let commit_body: Value = commit_res.json().await.unwrap(); 315 let cid = commit_body["cid"].as_str().unwrap(); 316 317 let url = format!( 318 "{}/xrpc/com.atproto.sync.getBlocks?did={}&cids={}&cids={}", 319 base_url().await, 320 did, 321 cid, 322 cid 323 ); 324 let res = client 325 .get(&url) 326 .send() 327 .await 328 .expect("Failed to send request"); 329 330 assert_eq!(res.status(), StatusCode::OK); 331 let content_type = res.headers().get("content-type").unwrap().to_str().unwrap(); 332 assert!( 333 content_type.contains("application/vnd.ipld.car"), 334 "Response should be a CAR file" 335 ); 336} 337 338#[tokio::test] 339async fn test_get_repo_since_returns_partial() { 340 let client = client(); 341 let (jwt, did) = create_account_and_login(&client).await; 342 343 let initial_commit_res = client 344 .get(format!( 345 "{}/xrpc/com.atproto.sync.getLatestCommit", 346 base_url().await 347 )) 348 .query(&[("did", did.as_str())]) 349 .send() 350 .await 351 .expect("Failed to get initial commit"); 352 let initial_body: Value = initial_commit_res.json().await.unwrap(); 353 let initial_rev = initial_body["rev"].as_str().unwrap(); 354 355 let full_repo_res = client 356 .get(format!( 357 "{}/xrpc/com.atproto.sync.getRepo", 358 base_url().await 359 )) 360 .query(&[("did", did.as_str())]) 361 .send() 362 .await 363 .expect("Failed to get full repo"); 364 assert_eq!(full_repo_res.status(), StatusCode::OK); 365 let full_repo_bytes = full_repo_res.bytes().await.unwrap(); 366 let full_repo_size = full_repo_bytes.len(); 367 368 create_post(&client, &did, &jwt, "Test post for since param").await; 369 370 let partial_repo_res = client 371 .get(format!( 372 "{}/xrpc/com.atproto.sync.getRepo", 373 base_url().await 374 )) 375 .query(&[("did", did.as_str()), ("since", initial_rev)]) 376 .send() 377 .await 378 .expect("Failed to get partial repo"); 379 assert_eq!(partial_repo_res.status(), StatusCode::OK); 380 let partial_repo_bytes = partial_repo_res.bytes().await.unwrap(); 381 let partial_repo_size = partial_repo_bytes.len(); 382 383 assert!( 384 partial_repo_size < full_repo_size, 385 "Partial export (since={}) should be smaller than full export: {} vs {}", 386 initial_rev, 387 partial_repo_size, 388 full_repo_size 389 ); 390} 391 392#[tokio::test] 393async fn test_list_blobs_takendown_returns_error() { 394 let client = client(); 395 let (_, did) = create_account_and_login(&client).await; 396 397 set_account_takedown(&did, Some("test-takedown-ref")).await; 398 399 let res = client 400 .get(format!( 401 "{}/xrpc/com.atproto.sync.listBlobs", 402 base_url().await 403 )) 404 .query(&[("did", did.as_str())]) 405 .send() 406 .await 407 .expect("Failed to send request"); 408 409 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 410 let body: Value = res.json().await.expect("Response was not valid JSON"); 411 assert_eq!(body["error"], "RepoTakendown"); 412} 413 414#[tokio::test] 415async fn test_get_record_takendown_returns_error() { 416 let client = client(); 417 let (jwt, did) = create_account_and_login(&client).await; 418 419 let (uri, _cid) = create_post(&client, &did, &jwt, "Test post").await; 420 let parts: Vec<&str> = uri.split('/').collect(); 421 let collection = parts[parts.len() - 2]; 422 let rkey = parts[parts.len() - 1]; 423 424 set_account_takedown(&did, Some("test-takedown-ref")).await; 425 426 let res = client 427 .get(format!( 428 "{}/xrpc/com.atproto.sync.getRecord", 429 base_url().await 430 )) 431 .query(&[ 432 ("did", did.as_str()), 433 ("collection", collection), 434 ("rkey", rkey), 435 ]) 436 .send() 437 .await 438 .expect("Failed to send request"); 439 440 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 441 let body: Value = res.json().await.expect("Response was not valid JSON"); 442 assert_eq!(body["error"], "RepoTakendown"); 443}