this repo has no description
1mod common; 2use common::*; 3 4use chrono::Utc; 5use reqwest::{StatusCode, header}; 6use serde_json::{Value, json}; 7 8#[tokio::test] 9async fn test_get_record_not_found() { 10 let client = client(); 11 let (_, did) = create_account_and_login(&client).await; 12 13 let params = [ 14 ("repo", did.as_str()), 15 ("collection", "app.bsky.feed.post"), 16 ("rkey", "nonexistent"), 17 ]; 18 19 let res = client 20 .get(format!( 21 "{}/xrpc/com.atproto.repo.getRecord", 22 base_url().await 23 )) 24 .query(&params) 25 .send() 26 .await 27 .expect("Failed to send request"); 28 29 assert_eq!(res.status(), StatusCode::NOT_FOUND); 30} 31 32#[tokio::test] 33async fn test_upload_blob_no_auth() { 34 let client = client(); 35 let res = client 36 .post(format!( 37 "{}/xrpc/com.atproto.repo.uploadBlob", 38 base_url().await 39 )) 40 .header(header::CONTENT_TYPE, "text/plain") 41 .body("no auth") 42 .send() 43 .await 44 .expect("Failed to send request"); 45 46 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 47 let body: Value = res.json().await.expect("Response was not valid JSON"); 48 assert_eq!(body["error"], "AuthenticationRequired"); 49} 50 51#[tokio::test] 52async fn test_upload_blob_success() { 53 let client = client(); 54 let (token, _) = create_account_and_login(&client).await; 55 let res = client 56 .post(format!( 57 "{}/xrpc/com.atproto.repo.uploadBlob", 58 base_url().await 59 )) 60 .header(header::CONTENT_TYPE, "text/plain") 61 .bearer_auth(token) 62 .body("This is our blob data") 63 .send() 64 .await 65 .expect("Failed to send request"); 66 67 assert_eq!(res.status(), StatusCode::OK); 68 let body: Value = res.json().await.expect("Response was not valid JSON"); 69 assert!(body["blob"]["ref"]["$link"].as_str().is_some()); 70} 71 72#[tokio::test] 73async fn test_put_record_no_auth() { 74 let client = client(); 75 let payload = json!({ 76 "repo": "did:plc:123", 77 "collection": "app.bsky.feed.post", 78 "rkey": "fake", 79 "record": {} 80 }); 81 82 let res = client 83 .post(format!( 84 "{}/xrpc/com.atproto.repo.putRecord", 85 base_url().await 86 )) 87 .json(&payload) 88 .send() 89 .await 90 .expect("Failed to send request"); 91 92 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 93 let body: Value = res.json().await.expect("Response was not valid JSON"); 94 assert_eq!(body["error"], "AuthenticationRequired"); 95} 96 97#[tokio::test] 98async fn test_put_record_success() { 99 let client = client(); 100 let (token, did) = create_account_and_login(&client).await; 101 let now = Utc::now().to_rfc3339(); 102 let payload = json!({ 103 "repo": did, 104 "collection": "app.bsky.feed.post", 105 "rkey": "e2e_test_post", 106 "record": { 107 "$type": "app.bsky.feed.post", 108 "text": "Hello from the e2e test script!", 109 "createdAt": now 110 } 111 }); 112 113 let res = client 114 .post(format!( 115 "{}/xrpc/com.atproto.repo.putRecord", 116 base_url().await 117 )) 118 .bearer_auth(token) 119 .json(&payload) 120 .send() 121 .await 122 .expect("Failed to send request"); 123 124 assert_eq!(res.status(), StatusCode::OK); 125 let body: Value = res.json().await.expect("Response was not valid JSON"); 126 assert!(body.get("uri").is_some()); 127 assert!(body.get("cid").is_some()); 128} 129 130#[tokio::test] 131async fn test_get_record_missing_params() { 132 let client = client(); 133 let params = [("repo", "did:plc:12345")]; 134 135 let res = client 136 .get(format!( 137 "{}/xrpc/com.atproto.repo.getRecord", 138 base_url().await 139 )) 140 .query(&params) 141 .send() 142 .await 143 .expect("Failed to send request"); 144 145 assert_eq!( 146 res.status(), 147 StatusCode::BAD_REQUEST, 148 "Expected 400 for missing params" 149 ); 150} 151 152#[tokio::test] 153async fn test_upload_blob_bad_token() { 154 let client = client(); 155 let res = client 156 .post(format!( 157 "{}/xrpc/com.atproto.repo.uploadBlob", 158 base_url().await 159 )) 160 .header(header::CONTENT_TYPE, "text/plain") 161 .bearer_auth(BAD_AUTH_TOKEN) 162 .body("This is our blob data") 163 .send() 164 .await 165 .expect("Failed to send request"); 166 167 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 168 let body: Value = res.json().await.expect("Response was not valid JSON"); 169 assert_eq!(body["error"], "AuthenticationFailed"); 170} 171 172#[tokio::test] 173async fn test_put_record_mismatched_repo() { 174 let client = client(); 175 let (token, _) = create_account_and_login(&client).await; 176 let now = Utc::now().to_rfc3339(); 177 let payload = json!({ 178 "repo": "did:plc:OTHER-USER", 179 "collection": "app.bsky.feed.post", 180 "rkey": "e2e_test_post", 181 "record": { 182 "$type": "app.bsky.feed.post", 183 "text": "Hello from the e2e test script!", 184 "createdAt": now 185 } 186 }); 187 188 let res = client 189 .post(format!( 190 "{}/xrpc/com.atproto.repo.putRecord", 191 base_url().await 192 )) 193 .bearer_auth(token) 194 .json(&payload) 195 .send() 196 .await 197 .expect("Failed to send request"); 198 199 assert!( 200 res.status() == StatusCode::FORBIDDEN || res.status() == StatusCode::UNAUTHORIZED, 201 "Expected 403 or 401 for mismatched repo and auth, got {}", 202 res.status() 203 ); 204} 205 206#[tokio::test] 207async fn test_put_record_invalid_schema() { 208 let client = client(); 209 let (token, did) = create_account_and_login(&client).await; 210 let now = Utc::now().to_rfc3339(); 211 let payload = json!({ 212 "repo": did, 213 "collection": "app.bsky.feed.post", 214 "rkey": "e2e_test_invalid", 215 "record": { 216 "$type": "app.bsky.feed.post", 217 "createdAt": now 218 } 219 }); 220 221 let res = client 222 .post(format!( 223 "{}/xrpc/com.atproto.repo.putRecord", 224 base_url().await 225 )) 226 .bearer_auth(token) 227 .json(&payload) 228 .send() 229 .await 230 .expect("Failed to send request"); 231 232 assert_eq!( 233 res.status(), 234 StatusCode::BAD_REQUEST, 235 "Expected 400 for invalid record schema" 236 ); 237} 238 239#[tokio::test] 240async fn test_upload_blob_unsupported_mime_type() { 241 let client = client(); 242 let (token, _) = create_account_and_login(&client).await; 243 let res = client 244 .post(format!( 245 "{}/xrpc/com.atproto.repo.uploadBlob", 246 base_url().await 247 )) 248 .header(header::CONTENT_TYPE, "application/xml") 249 .bearer_auth(token) 250 .body("<xml>not an image</xml>") 251 .send() 252 .await 253 .expect("Failed to send request"); 254 255 // Changed expectation to OK for now, bc we don't validate mime type strictly yet. 256 assert_eq!(res.status(), StatusCode::OK); 257} 258 259#[tokio::test] 260async fn test_list_records() { 261 let client = client(); 262 let (_, did) = create_account_and_login(&client).await; 263 let params = [ 264 ("repo", did.as_str()), 265 ("collection", "app.bsky.feed.post"), 266 ("limit", "10"), 267 ]; 268 let res = client 269 .get(format!( 270 "{}/xrpc/com.atproto.repo.listRecords", 271 base_url().await 272 )) 273 .query(&params) 274 .send() 275 .await 276 .expect("Failed to send request"); 277 278 assert_eq!(res.status(), StatusCode::OK); 279} 280 281#[tokio::test] 282async fn test_describe_repo() { 283 let client = client(); 284 let (_, did) = create_account_and_login(&client).await; 285 let params = [("repo", did.as_str())]; 286 let res = client 287 .get(format!( 288 "{}/xrpc/com.atproto.repo.describeRepo", 289 base_url().await 290 )) 291 .query(&params) 292 .send() 293 .await 294 .expect("Failed to send request"); 295 296 assert_eq!(res.status(), StatusCode::OK); 297} 298 299#[tokio::test] 300async fn test_create_record_success_with_generated_rkey() { 301 let client = client(); 302 let (token, did) = create_account_and_login(&client).await; 303 let payload = json!({ 304 "repo": did, 305 "collection": "app.bsky.feed.post", 306 "record": { 307 "$type": "app.bsky.feed.post", 308 "text": "Hello, world!", 309 "createdAt": "2025-12-02T12:00:00Z" 310 } 311 }); 312 313 let res = client 314 .post(format!( 315 "{}/xrpc/com.atproto.repo.createRecord", 316 base_url().await 317 )) 318 .json(&payload) 319 .bearer_auth(token) 320 .send() 321 .await 322 .expect("Failed to send request"); 323 324 assert_eq!(res.status(), StatusCode::OK); 325 let body: Value = res.json().await.expect("Response was not valid JSON"); 326 let uri = body["uri"].as_str().unwrap(); 327 assert!(uri.starts_with(&format!("at://{}/app.bsky.feed.post/", did))); 328 assert!(body.get("cid").is_some()); 329} 330 331#[tokio::test] 332async fn test_create_record_success_with_provided_rkey() { 333 let client = client(); 334 let (token, did) = create_account_and_login(&client).await; 335 let rkey = format!("custom-rkey-{}", Utc::now().timestamp_millis()); 336 let payload = json!({ 337 "repo": did, 338 "collection": "app.bsky.feed.post", 339 "rkey": rkey, 340 "record": { 341 "$type": "app.bsky.feed.post", 342 "text": "Hello, world!", 343 "createdAt": "2025-12-02T12:00:00Z" 344 } 345 }); 346 347 let res = client 348 .post(format!( 349 "{}/xrpc/com.atproto.repo.createRecord", 350 base_url().await 351 )) 352 .json(&payload) 353 .bearer_auth(token) 354 .send() 355 .await 356 .expect("Failed to send request"); 357 358 assert_eq!(res.status(), StatusCode::OK); 359 let body: Value = res.json().await.expect("Response was not valid JSON"); 360 assert_eq!( 361 body["uri"], 362 format!("at://{}/app.bsky.feed.post/{}", did, rkey) 363 ); 364 assert!(body.get("cid").is_some()); 365} 366 367#[tokio::test] 368async fn test_delete_record() { 369 let client = client(); 370 let (token, did) = create_account_and_login(&client).await; 371 let rkey = format!("post_to_delete_{}", Utc::now().timestamp_millis()); 372 373 let create_payload = json!({ 374 "repo": did, 375 "collection": "app.bsky.feed.post", 376 "rkey": rkey, 377 "record": { 378 "$type": "app.bsky.feed.post", 379 "text": "This post will be deleted", 380 "createdAt": Utc::now().to_rfc3339() 381 } 382 }); 383 let create_res = client 384 .post(format!( 385 "{}/xrpc/com.atproto.repo.putRecord", 386 base_url().await 387 )) 388 .bearer_auth(&token) 389 .json(&create_payload) 390 .send() 391 .await 392 .expect("Failed to create record"); 393 assert_eq!(create_res.status(), StatusCode::OK); 394 395 let delete_payload = json!({ 396 "repo": did, 397 "collection": "app.bsky.feed.post", 398 "rkey": rkey 399 }); 400 let delete_res = client 401 .post(format!( 402 "{}/xrpc/com.atproto.repo.deleteRecord", 403 base_url().await 404 )) 405 .bearer_auth(&token) 406 .json(&delete_payload) 407 .send() 408 .await 409 .expect("Failed to send request"); 410 411 assert_eq!(delete_res.status(), StatusCode::OK); 412 413 let get_res = client 414 .get(format!( 415 "{}/xrpc/com.atproto.repo.getRecord", 416 base_url().await 417 )) 418 .query(&[ 419 ("repo", did.as_str()), 420 ("collection", "app.bsky.feed.post"), 421 ("rkey", rkey.as_str()), 422 ]) 423 .send() 424 .await 425 .expect("Failed to verify deletion"); 426 assert_eq!(get_res.status(), StatusCode::NOT_FOUND); 427} 428 429#[tokio::test] 430async fn test_apply_writes_create() { 431 let client = client(); 432 let (token, did) = create_account_and_login(&client).await; 433 let now = Utc::now().to_rfc3339(); 434 435 let payload = json!({ 436 "repo": did, 437 "writes": [ 438 { 439 "$type": "com.atproto.repo.applyWrites#create", 440 "collection": "app.bsky.feed.post", 441 "value": { 442 "$type": "app.bsky.feed.post", 443 "text": "Batch created post 1", 444 "createdAt": now 445 } 446 }, 447 { 448 "$type": "com.atproto.repo.applyWrites#create", 449 "collection": "app.bsky.feed.post", 450 "value": { 451 "$type": "app.bsky.feed.post", 452 "text": "Batch created post 2", 453 "createdAt": now 454 } 455 } 456 ] 457 }); 458 459 let res = client 460 .post(format!( 461 "{}/xrpc/com.atproto.repo.applyWrites", 462 base_url().await 463 )) 464 .bearer_auth(&token) 465 .json(&payload) 466 .send() 467 .await 468 .expect("Failed to send request"); 469 470 assert_eq!(res.status(), StatusCode::OK); 471 let body: Value = res.json().await.expect("Response was not valid JSON"); 472 assert!(body["commit"]["cid"].is_string()); 473 assert!(body["results"].is_array()); 474 let results = body["results"].as_array().unwrap(); 475 assert_eq!(results.len(), 2); 476 assert!(results[0]["uri"].is_string()); 477 assert!(results[0]["cid"].is_string()); 478} 479 480#[tokio::test] 481async fn test_apply_writes_update() { 482 let client = client(); 483 let (token, did) = create_account_and_login(&client).await; 484 let now = Utc::now().to_rfc3339(); 485 let rkey = format!("batch_update_{}", Utc::now().timestamp_millis()); 486 487 let create_payload = json!({ 488 "repo": did, 489 "collection": "app.bsky.feed.post", 490 "rkey": rkey, 491 "record": { 492 "$type": "app.bsky.feed.post", 493 "text": "Original post", 494 "createdAt": now 495 } 496 }); 497 let res = client 498 .post(format!( 499 "{}/xrpc/com.atproto.repo.putRecord", 500 base_url().await 501 )) 502 .bearer_auth(&token) 503 .json(&create_payload) 504 .send() 505 .await 506 .expect("Failed to create"); 507 assert_eq!(res.status(), StatusCode::OK); 508 509 let update_payload = json!({ 510 "repo": did, 511 "writes": [ 512 { 513 "$type": "com.atproto.repo.applyWrites#update", 514 "collection": "app.bsky.feed.post", 515 "rkey": rkey, 516 "value": { 517 "$type": "app.bsky.feed.post", 518 "text": "Updated post via applyWrites", 519 "createdAt": now 520 } 521 } 522 ] 523 }); 524 525 let res = client 526 .post(format!( 527 "{}/xrpc/com.atproto.repo.applyWrites", 528 base_url().await 529 )) 530 .bearer_auth(&token) 531 .json(&update_payload) 532 .send() 533 .await 534 .expect("Failed to send request"); 535 536 assert_eq!(res.status(), StatusCode::OK); 537 let body: Value = res.json().await.expect("Response was not valid JSON"); 538 let results = body["results"].as_array().unwrap(); 539 assert_eq!(results.len(), 1); 540 assert!(results[0]["uri"].is_string()); 541} 542 543#[tokio::test] 544async fn test_apply_writes_delete() { 545 let client = client(); 546 let (token, did) = create_account_and_login(&client).await; 547 let now = Utc::now().to_rfc3339(); 548 let rkey = format!("batch_delete_{}", Utc::now().timestamp_millis()); 549 550 let create_payload = json!({ 551 "repo": did, 552 "collection": "app.bsky.feed.post", 553 "rkey": rkey, 554 "record": { 555 "$type": "app.bsky.feed.post", 556 "text": "Post to delete", 557 "createdAt": now 558 } 559 }); 560 let res = client 561 .post(format!( 562 "{}/xrpc/com.atproto.repo.putRecord", 563 base_url().await 564 )) 565 .bearer_auth(&token) 566 .json(&create_payload) 567 .send() 568 .await 569 .expect("Failed to create"); 570 assert_eq!(res.status(), StatusCode::OK); 571 572 let delete_payload = json!({ 573 "repo": did, 574 "writes": [ 575 { 576 "$type": "com.atproto.repo.applyWrites#delete", 577 "collection": "app.bsky.feed.post", 578 "rkey": rkey 579 } 580 ] 581 }); 582 583 let res = client 584 .post(format!( 585 "{}/xrpc/com.atproto.repo.applyWrites", 586 base_url().await 587 )) 588 .bearer_auth(&token) 589 .json(&delete_payload) 590 .send() 591 .await 592 .expect("Failed to send request"); 593 594 assert_eq!(res.status(), StatusCode::OK); 595 596 let get_res = client 597 .get(format!( 598 "{}/xrpc/com.atproto.repo.getRecord", 599 base_url().await 600 )) 601 .query(&[ 602 ("repo", did.as_str()), 603 ("collection", "app.bsky.feed.post"), 604 ("rkey", rkey.as_str()), 605 ]) 606 .send() 607 .await 608 .expect("Failed to verify"); 609 assert_eq!(get_res.status(), StatusCode::NOT_FOUND); 610} 611 612#[tokio::test] 613async fn test_apply_writes_mixed_operations() { 614 let client = client(); 615 let (token, did) = create_account_and_login(&client).await; 616 let now = Utc::now().to_rfc3339(); 617 let rkey_to_delete = format!("mixed_del_{}", Utc::now().timestamp_millis()); 618 let rkey_to_update = format!("mixed_upd_{}", Utc::now().timestamp_millis()); 619 620 let setup_payload = json!({ 621 "repo": did, 622 "writes": [ 623 { 624 "$type": "com.atproto.repo.applyWrites#create", 625 "collection": "app.bsky.feed.post", 626 "rkey": rkey_to_delete, 627 "value": { 628 "$type": "app.bsky.feed.post", 629 "text": "To be deleted", 630 "createdAt": now 631 } 632 }, 633 { 634 "$type": "com.atproto.repo.applyWrites#create", 635 "collection": "app.bsky.feed.post", 636 "rkey": rkey_to_update, 637 "value": { 638 "$type": "app.bsky.feed.post", 639 "text": "To be updated", 640 "createdAt": now 641 } 642 } 643 ] 644 }); 645 let res = client 646 .post(format!( 647 "{}/xrpc/com.atproto.repo.applyWrites", 648 base_url().await 649 )) 650 .bearer_auth(&token) 651 .json(&setup_payload) 652 .send() 653 .await 654 .expect("Failed to setup"); 655 assert_eq!(res.status(), StatusCode::OK); 656 657 let mixed_payload = json!({ 658 "repo": did, 659 "writes": [ 660 { 661 "$type": "com.atproto.repo.applyWrites#create", 662 "collection": "app.bsky.feed.post", 663 "value": { 664 "$type": "app.bsky.feed.post", 665 "text": "New post", 666 "createdAt": now 667 } 668 }, 669 { 670 "$type": "com.atproto.repo.applyWrites#update", 671 "collection": "app.bsky.feed.post", 672 "rkey": rkey_to_update, 673 "value": { 674 "$type": "app.bsky.feed.post", 675 "text": "Updated text", 676 "createdAt": now 677 } 678 }, 679 { 680 "$type": "com.atproto.repo.applyWrites#delete", 681 "collection": "app.bsky.feed.post", 682 "rkey": rkey_to_delete 683 } 684 ] 685 }); 686 687 let res = client 688 .post(format!( 689 "{}/xrpc/com.atproto.repo.applyWrites", 690 base_url().await 691 )) 692 .bearer_auth(&token) 693 .json(&mixed_payload) 694 .send() 695 .await 696 .expect("Failed to send request"); 697 698 assert_eq!(res.status(), StatusCode::OK); 699 let body: Value = res.json().await.expect("Response was not valid JSON"); 700 let results = body["results"].as_array().unwrap(); 701 assert_eq!(results.len(), 3); 702} 703 704#[tokio::test] 705async fn test_apply_writes_no_auth() { 706 let client = client(); 707 708 let payload = json!({ 709 "repo": "did:plc:test", 710 "writes": [ 711 { 712 "$type": "com.atproto.repo.applyWrites#create", 713 "collection": "app.bsky.feed.post", 714 "value": { 715 "$type": "app.bsky.feed.post", 716 "text": "Test", 717 "createdAt": "2025-01-01T00:00:00Z" 718 } 719 } 720 ] 721 }); 722 723 let res = client 724 .post(format!( 725 "{}/xrpc/com.atproto.repo.applyWrites", 726 base_url().await 727 )) 728 .json(&payload) 729 .send() 730 .await 731 .expect("Failed to send request"); 732 733 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 734} 735 736#[tokio::test] 737async fn test_apply_writes_empty_writes() { 738 let client = client(); 739 let (token, did) = create_account_and_login(&client).await; 740 741 let payload = json!({ 742 "repo": did, 743 "writes": [] 744 }); 745 746 let res = client 747 .post(format!( 748 "{}/xrpc/com.atproto.repo.applyWrites", 749 base_url().await 750 )) 751 .bearer_auth(&token) 752 .json(&payload) 753 .send() 754 .await 755 .expect("Failed to send request"); 756 757 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 758} 759 760#[tokio::test] 761async fn test_list_missing_blobs() { 762 let client = client(); 763 let (access_jwt, _) = create_account_and_login(&client).await; 764 765 let res = client 766 .get(format!( 767 "{}/xrpc/com.atproto.repo.listMissingBlobs", 768 base_url().await 769 )) 770 .bearer_auth(&access_jwt) 771 .send() 772 .await 773 .expect("Failed to send request"); 774 775 assert_eq!(res.status(), StatusCode::OK); 776 let body: Value = res.json().await.expect("Response was not valid JSON"); 777 assert!(body["blobs"].is_array()); 778} 779 780#[tokio::test] 781async fn test_list_missing_blobs_no_auth() { 782 let client = client(); 783 let res = client 784 .get(format!( 785 "{}/xrpc/com.atproto.repo.listMissingBlobs", 786 base_url().await 787 )) 788 .send() 789 .await 790 .expect("Failed to send request"); 791 792 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 793}