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] 207#[ignore] 208async fn test_put_record_invalid_schema() { 209 let client = client(); 210 let (token, did) = create_account_and_login(&client).await; 211 let now = Utc::now().to_rfc3339(); 212 let payload = json!({ 213 "repo": did, 214 "collection": "app.bsky.feed.post", 215 "rkey": "e2e_test_invalid", 216 "record": { 217 "$type": "app.bsky.feed.post", 218 "createdAt": now 219 } 220 }); 221 222 let res = client 223 .post(format!( 224 "{}/xrpc/com.atproto.repo.putRecord", 225 base_url().await 226 )) 227 .bearer_auth(token) 228 .json(&payload) 229 .send() 230 .await 231 .expect("Failed to send request"); 232 233 assert_eq!( 234 res.status(), 235 StatusCode::BAD_REQUEST, 236 "Expected 400 for invalid record schema" 237 ); 238} 239 240#[tokio::test] 241async fn test_upload_blob_unsupported_mime_type() { 242 let client = client(); 243 let (token, _) = create_account_and_login(&client).await; 244 let res = client 245 .post(format!( 246 "{}/xrpc/com.atproto.repo.uploadBlob", 247 base_url().await 248 )) 249 .header(header::CONTENT_TYPE, "application/xml") 250 .bearer_auth(token) 251 .body("<xml>not an image</xml>") 252 .send() 253 .await 254 .expect("Failed to send request"); 255 256 // Changed expectation to OK for now, bc we don't validate mime type strictly yet. 257 assert_eq!(res.status(), StatusCode::OK); 258} 259 260#[tokio::test] 261async fn test_list_records() { 262 let client = client(); 263 let (_, did) = create_account_and_login(&client).await; 264 let params = [ 265 ("repo", did.as_str()), 266 ("collection", "app.bsky.feed.post"), 267 ("limit", "10"), 268 ]; 269 let res = client 270 .get(format!( 271 "{}/xrpc/com.atproto.repo.listRecords", 272 base_url().await 273 )) 274 .query(&params) 275 .send() 276 .await 277 .expect("Failed to send request"); 278 279 assert_eq!(res.status(), StatusCode::OK); 280} 281 282#[tokio::test] 283async fn test_describe_repo() { 284 let client = client(); 285 let (_, did) = create_account_and_login(&client).await; 286 let params = [("repo", did.as_str())]; 287 let res = client 288 .get(format!( 289 "{}/xrpc/com.atproto.repo.describeRepo", 290 base_url().await 291 )) 292 .query(&params) 293 .send() 294 .await 295 .expect("Failed to send request"); 296 297 assert_eq!(res.status(), StatusCode::OK); 298} 299 300#[tokio::test] 301async fn test_create_record_success_with_generated_rkey() { 302 let client = client(); 303 let (token, did) = create_account_and_login(&client).await; 304 let payload = json!({ 305 "repo": did, 306 "collection": "app.bsky.feed.post", 307 "record": { 308 "$type": "app.bsky.feed.post", 309 "text": "Hello, world!", 310 "createdAt": "2025-12-02T12:00:00Z" 311 } 312 }); 313 314 let res = client 315 .post(format!( 316 "{}/xrpc/com.atproto.repo.createRecord", 317 base_url().await 318 )) 319 .json(&payload) 320 .bearer_auth(token) 321 .send() 322 .await 323 .expect("Failed to send request"); 324 325 assert_eq!(res.status(), StatusCode::OK); 326 let body: Value = res.json().await.expect("Response was not valid JSON"); 327 let uri = body["uri"].as_str().unwrap(); 328 assert!(uri.starts_with(&format!("at://{}/app.bsky.feed.post/", did))); 329 assert!(body.get("cid").is_some()); 330} 331 332#[tokio::test] 333async fn test_create_record_success_with_provided_rkey() { 334 let client = client(); 335 let (token, did) = create_account_and_login(&client).await; 336 let rkey = format!("custom-rkey-{}", Utc::now().timestamp_millis()); 337 let payload = json!({ 338 "repo": did, 339 "collection": "app.bsky.feed.post", 340 "rkey": rkey, 341 "record": { 342 "$type": "app.bsky.feed.post", 343 "text": "Hello, world!", 344 "createdAt": "2025-12-02T12:00:00Z" 345 } 346 }); 347 348 let res = client 349 .post(format!( 350 "{}/xrpc/com.atproto.repo.createRecord", 351 base_url().await 352 )) 353 .json(&payload) 354 .bearer_auth(token) 355 .send() 356 .await 357 .expect("Failed to send request"); 358 359 assert_eq!(res.status(), StatusCode::OK); 360 let body: Value = res.json().await.expect("Response was not valid JSON"); 361 assert_eq!( 362 body["uri"], 363 format!("at://{}/app.bsky.feed.post/{}", did, rkey) 364 ); 365 assert!(body.get("cid").is_some()); 366} 367 368#[tokio::test] 369async fn test_delete_record() { 370 let client = client(); 371 let (token, did) = create_account_and_login(&client).await; 372 let rkey = format!("post_to_delete_{}", Utc::now().timestamp_millis()); 373 374 let create_payload = json!({ 375 "repo": did, 376 "collection": "app.bsky.feed.post", 377 "rkey": rkey, 378 "record": { 379 "$type": "app.bsky.feed.post", 380 "text": "This post will be deleted", 381 "createdAt": Utc::now().to_rfc3339() 382 } 383 }); 384 let create_res = client 385 .post(format!( 386 "{}/xrpc/com.atproto.repo.putRecord", 387 base_url().await 388 )) 389 .bearer_auth(&token) 390 .json(&create_payload) 391 .send() 392 .await 393 .expect("Failed to create record"); 394 assert_eq!(create_res.status(), StatusCode::OK); 395 396 let delete_payload = json!({ 397 "repo": did, 398 "collection": "app.bsky.feed.post", 399 "rkey": rkey 400 }); 401 let delete_res = client 402 .post(format!( 403 "{}/xrpc/com.atproto.repo.deleteRecord", 404 base_url().await 405 )) 406 .bearer_auth(&token) 407 .json(&delete_payload) 408 .send() 409 .await 410 .expect("Failed to send request"); 411 412 assert_eq!(delete_res.status(), StatusCode::OK); 413 414 let get_res = client 415 .get(format!( 416 "{}/xrpc/com.atproto.repo.getRecord", 417 base_url().await 418 )) 419 .query(&[ 420 ("repo", did.as_str()), 421 ("collection", "app.bsky.feed.post"), 422 ("rkey", rkey.as_str()), 423 ]) 424 .send() 425 .await 426 .expect("Failed to verify deletion"); 427 assert_eq!(get_res.status(), StatusCode::NOT_FOUND); 428} 429 430#[tokio::test] 431async fn test_apply_writes_create() { 432 let client = client(); 433 let (token, did) = create_account_and_login(&client).await; 434 let now = Utc::now().to_rfc3339(); 435 436 let payload = json!({ 437 "repo": did, 438 "writes": [ 439 { 440 "$type": "com.atproto.repo.applyWrites#create", 441 "collection": "app.bsky.feed.post", 442 "value": { 443 "$type": "app.bsky.feed.post", 444 "text": "Batch created post 1", 445 "createdAt": now 446 } 447 }, 448 { 449 "$type": "com.atproto.repo.applyWrites#create", 450 "collection": "app.bsky.feed.post", 451 "value": { 452 "$type": "app.bsky.feed.post", 453 "text": "Batch created post 2", 454 "createdAt": now 455 } 456 } 457 ] 458 }); 459 460 let res = client 461 .post(format!( 462 "{}/xrpc/com.atproto.repo.applyWrites", 463 base_url().await 464 )) 465 .bearer_auth(&token) 466 .json(&payload) 467 .send() 468 .await 469 .expect("Failed to send request"); 470 471 assert_eq!(res.status(), StatusCode::OK); 472 let body: Value = res.json().await.expect("Response was not valid JSON"); 473 assert!(body["commit"]["cid"].is_string()); 474 assert!(body["results"].is_array()); 475 let results = body["results"].as_array().unwrap(); 476 assert_eq!(results.len(), 2); 477 assert!(results[0]["uri"].is_string()); 478 assert!(results[0]["cid"].is_string()); 479} 480 481#[tokio::test] 482async fn test_apply_writes_update() { 483 let client = client(); 484 let (token, did) = create_account_and_login(&client).await; 485 let now = Utc::now().to_rfc3339(); 486 let rkey = format!("batch_update_{}", Utc::now().timestamp_millis()); 487 488 let create_payload = json!({ 489 "repo": did, 490 "collection": "app.bsky.feed.post", 491 "rkey": rkey, 492 "record": { 493 "$type": "app.bsky.feed.post", 494 "text": "Original post", 495 "createdAt": now 496 } 497 }); 498 let res = client 499 .post(format!( 500 "{}/xrpc/com.atproto.repo.putRecord", 501 base_url().await 502 )) 503 .bearer_auth(&token) 504 .json(&create_payload) 505 .send() 506 .await 507 .expect("Failed to create"); 508 assert_eq!(res.status(), StatusCode::OK); 509 510 let update_payload = json!({ 511 "repo": did, 512 "writes": [ 513 { 514 "$type": "com.atproto.repo.applyWrites#update", 515 "collection": "app.bsky.feed.post", 516 "rkey": rkey, 517 "value": { 518 "$type": "app.bsky.feed.post", 519 "text": "Updated post via applyWrites", 520 "createdAt": now 521 } 522 } 523 ] 524 }); 525 526 let res = client 527 .post(format!( 528 "{}/xrpc/com.atproto.repo.applyWrites", 529 base_url().await 530 )) 531 .bearer_auth(&token) 532 .json(&update_payload) 533 .send() 534 .await 535 .expect("Failed to send request"); 536 537 assert_eq!(res.status(), StatusCode::OK); 538 let body: Value = res.json().await.expect("Response was not valid JSON"); 539 let results = body["results"].as_array().unwrap(); 540 assert_eq!(results.len(), 1); 541 assert!(results[0]["uri"].is_string()); 542} 543 544#[tokio::test] 545async fn test_apply_writes_delete() { 546 let client = client(); 547 let (token, did) = create_account_and_login(&client).await; 548 let now = Utc::now().to_rfc3339(); 549 let rkey = format!("batch_delete_{}", Utc::now().timestamp_millis()); 550 551 let create_payload = json!({ 552 "repo": did, 553 "collection": "app.bsky.feed.post", 554 "rkey": rkey, 555 "record": { 556 "$type": "app.bsky.feed.post", 557 "text": "Post to delete", 558 "createdAt": now 559 } 560 }); 561 let res = client 562 .post(format!( 563 "{}/xrpc/com.atproto.repo.putRecord", 564 base_url().await 565 )) 566 .bearer_auth(&token) 567 .json(&create_payload) 568 .send() 569 .await 570 .expect("Failed to create"); 571 assert_eq!(res.status(), StatusCode::OK); 572 573 let delete_payload = json!({ 574 "repo": did, 575 "writes": [ 576 { 577 "$type": "com.atproto.repo.applyWrites#delete", 578 "collection": "app.bsky.feed.post", 579 "rkey": rkey 580 } 581 ] 582 }); 583 584 let res = client 585 .post(format!( 586 "{}/xrpc/com.atproto.repo.applyWrites", 587 base_url().await 588 )) 589 .bearer_auth(&token) 590 .json(&delete_payload) 591 .send() 592 .await 593 .expect("Failed to send request"); 594 595 assert_eq!(res.status(), StatusCode::OK); 596 597 let get_res = client 598 .get(format!( 599 "{}/xrpc/com.atproto.repo.getRecord", 600 base_url().await 601 )) 602 .query(&[ 603 ("repo", did.as_str()), 604 ("collection", "app.bsky.feed.post"), 605 ("rkey", rkey.as_str()), 606 ]) 607 .send() 608 .await 609 .expect("Failed to verify"); 610 assert_eq!(get_res.status(), StatusCode::NOT_FOUND); 611} 612 613#[tokio::test] 614async fn test_apply_writes_mixed_operations() { 615 let client = client(); 616 let (token, did) = create_account_and_login(&client).await; 617 let now = Utc::now().to_rfc3339(); 618 let rkey_to_delete = format!("mixed_del_{}", Utc::now().timestamp_millis()); 619 let rkey_to_update = format!("mixed_upd_{}", Utc::now().timestamp_millis()); 620 621 let setup_payload = json!({ 622 "repo": did, 623 "writes": [ 624 { 625 "$type": "com.atproto.repo.applyWrites#create", 626 "collection": "app.bsky.feed.post", 627 "rkey": rkey_to_delete, 628 "value": { 629 "$type": "app.bsky.feed.post", 630 "text": "To be deleted", 631 "createdAt": now 632 } 633 }, 634 { 635 "$type": "com.atproto.repo.applyWrites#create", 636 "collection": "app.bsky.feed.post", 637 "rkey": rkey_to_update, 638 "value": { 639 "$type": "app.bsky.feed.post", 640 "text": "To be updated", 641 "createdAt": now 642 } 643 } 644 ] 645 }); 646 let res = client 647 .post(format!( 648 "{}/xrpc/com.atproto.repo.applyWrites", 649 base_url().await 650 )) 651 .bearer_auth(&token) 652 .json(&setup_payload) 653 .send() 654 .await 655 .expect("Failed to setup"); 656 assert_eq!(res.status(), StatusCode::OK); 657 658 let mixed_payload = json!({ 659 "repo": did, 660 "writes": [ 661 { 662 "$type": "com.atproto.repo.applyWrites#create", 663 "collection": "app.bsky.feed.post", 664 "value": { 665 "$type": "app.bsky.feed.post", 666 "text": "New post", 667 "createdAt": now 668 } 669 }, 670 { 671 "$type": "com.atproto.repo.applyWrites#update", 672 "collection": "app.bsky.feed.post", 673 "rkey": rkey_to_update, 674 "value": { 675 "$type": "app.bsky.feed.post", 676 "text": "Updated text", 677 "createdAt": now 678 } 679 }, 680 { 681 "$type": "com.atproto.repo.applyWrites#delete", 682 "collection": "app.bsky.feed.post", 683 "rkey": rkey_to_delete 684 } 685 ] 686 }); 687 688 let res = client 689 .post(format!( 690 "{}/xrpc/com.atproto.repo.applyWrites", 691 base_url().await 692 )) 693 .bearer_auth(&token) 694 .json(&mixed_payload) 695 .send() 696 .await 697 .expect("Failed to send request"); 698 699 assert_eq!(res.status(), StatusCode::OK); 700 let body: Value = res.json().await.expect("Response was not valid JSON"); 701 let results = body["results"].as_array().unwrap(); 702 assert_eq!(results.len(), 3); 703} 704 705#[tokio::test] 706async fn test_apply_writes_no_auth() { 707 let client = client(); 708 709 let payload = json!({ 710 "repo": "did:plc:test", 711 "writes": [ 712 { 713 "$type": "com.atproto.repo.applyWrites#create", 714 "collection": "app.bsky.feed.post", 715 "value": { 716 "$type": "app.bsky.feed.post", 717 "text": "Test", 718 "createdAt": "2025-01-01T00:00:00Z" 719 } 720 } 721 ] 722 }); 723 724 let res = client 725 .post(format!( 726 "{}/xrpc/com.atproto.repo.applyWrites", 727 base_url().await 728 )) 729 .json(&payload) 730 .send() 731 .await 732 .expect("Failed to send request"); 733 734 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 735} 736 737#[tokio::test] 738async fn test_apply_writes_empty_writes() { 739 let client = client(); 740 let (token, did) = create_account_and_login(&client).await; 741 742 let payload = json!({ 743 "repo": did, 744 "writes": [] 745 }); 746 747 let res = client 748 .post(format!( 749 "{}/xrpc/com.atproto.repo.applyWrites", 750 base_url().await 751 )) 752 .bearer_auth(&token) 753 .json(&payload) 754 .send() 755 .await 756 .expect("Failed to send request"); 757 758 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 759}