mod common; use common::*; use reqwest::{header, StatusCode}; use serde_json::{json, Value}; use chrono::Utc; #[tokio::test] #[ignore] async fn test_get_record() { let client = client(); let params = [ ("repo", "did:plc:12345"), ("collection", "app.bsky.actor.profile"), ("rkey", "self"), ]; let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) .query(¶ms) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["value"]["$type"], "app.bsky.actor.profile"); } #[tokio::test] #[ignore] async fn test_get_record_not_found() { let client = client(); let params = [ ("repo", "did:plc:12345"), ("collection", "app.bsky.feed.post"), ("rkey", "nonexistent"), ]; let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) .query(¶ms) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::NOT_FOUND); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["error"], "NotFound"); } #[tokio::test] #[ignore] async fn test_upload_blob_no_auth() { let client = client(); let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) .header(header::CONTENT_TYPE, "text/plain") .body("no auth") .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::UNAUTHORIZED); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["error"], "AuthenticationFailed"); } #[tokio::test] #[ignore] async fn test_upload_blob_success() { let client = client(); let (token, _) = create_account_and_login(&client).await; let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) .header(header::CONTENT_TYPE, "text/plain") .bearer_auth(token) .body("This is our blob data") .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.expect("Response was not valid JSON"); assert!(body["blob"]["ref"]["$link"].as_str().is_some()); } #[tokio::test] #[ignore] async fn test_put_record_no_auth() { let client = client(); let payload = json!({ "repo": "did:plc:123", "collection": "app.bsky.feed.post", "rkey": "fake", "record": {} }); let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) .json(&payload) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::UNAUTHORIZED); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["error"], "AuthenticationFailed"); } #[tokio::test] #[ignore] async fn test_put_record_success() { let client = client(); let (token, did) = create_account_and_login(&client).await; let now = Utc::now().to_rfc3339(); let payload = json!({ "repo": did, "collection": "app.bsky.feed.post", "rkey": "e2e_test_post", "record": { "$type": "app.bsky.feed.post", "text": "Hello from the e2e test script!", "createdAt": now } }); let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) .bearer_auth(token) .json(&payload) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.expect("Response was not valid JSON"); assert!(body.get("uri").is_some()); assert!(body.get("cid").is_some()); } #[tokio::test] #[ignore] async fn test_get_record_missing_params() { let client = client(); // Missing `collection` and `rkey` let params = [ ("repo", "did:plc:12345"), ]; let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", BASE_URL)) .query(¶ms) .send() .await .expect("Failed to send request"); // This will fail (get 404) until the handler validates query params assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for missing params"); } #[tokio::test] #[ignore] async fn test_upload_blob_bad_token() { let client = client(); let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) .header(header::CONTENT_TYPE, "text/plain") .bearer_auth(BAD_AUTH_TOKEN) .body("This is our blob data") .send() .await .expect("Failed to send request"); // This *should* pass if the auth stub is working correctly assert_eq!(res.status(), StatusCode::UNAUTHORIZED); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["error"], "AuthenticationFailed"); } #[tokio::test] #[ignore] async fn test_put_record_mismatched_repo() { let client = client(); let (token, _) = create_account_and_login(&client).await; let now = Utc::now().to_rfc3339(); let payload = json!({ "repo": "did:plc:OTHER-USER", // This does NOT match AUTH_DID "collection": "app.bsky.feed.post", "rkey": "e2e_test_post", "record": { "$type": "app.bsky.feed.post", "text": "Hello from the e2e test script!", "createdAt": now } }); let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) .bearer_auth(token) .json(&payload) .send() .await .expect("Failed to send request"); // This will fail (get 200) until handler validates repo matches auth assert_eq!(res.status(), StatusCode::FORBIDDEN, "Expected 403 for mismatched repo and auth"); } #[tokio::test] #[ignore] async fn test_put_record_invalid_schema() { let client = client(); let (token, did) = create_account_and_login(&client).await; let now = Utc::now().to_rfc3339(); let payload = json!({ "repo": did, "collection": "app.bsky.feed.post", "rkey": "e2e_test_invalid", "record": { "$type": "app.bsky.feed.post", // "text" field is missing, this is invalid "createdAt": now } }); let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", BASE_URL)) .bearer_auth(token) .json(&payload) .send() .await .expect("Failed to send request"); // This will fail (get 200) until handler validates record schema assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for invalid record schema"); } #[tokio::test] #[ignore] async fn test_upload_blob_unsupported_mime_type() { let client = client(); let (token, _) = create_account_and_login(&client).await; let res = client.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", BASE_URL)) .header(header::CONTENT_TYPE, "application/xml") .bearer_auth(token) .body("not an image") .send() .await .expect("Failed to send request"); // This will fail (get 200) until handler validates mime type assert_eq!(res.status(), StatusCode::BAD_REQUEST, "Expected 400 for unsupported mime type"); } #[tokio::test] async fn test_list_records() { let client = client(); let (_, did) = create_account_and_login(&client).await; let params = [ ("repo", did.as_str()), ("collection", "app.bsky.feed.post"), ("limit", "10"), ]; let res = client.get(format!("{}/xrpc/com.atproto.repo.listRecords", BASE_URL)) .query(¶ms) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); } #[tokio::test] async fn test_delete_record() { let client = client(); let (token, did) = create_account_and_login(&client).await; let payload = json!({ "repo": did, "collection": "app.bsky.feed.post", "rkey": "some_post_to_delete" }); let res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", BASE_URL)) .bearer_auth(token) .json(&payload) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); } #[tokio::test] async fn test_describe_repo() { let client = client(); let (_, did) = create_account_and_login(&client).await; let params = [ ("repo", did.as_str()), ]; let res = client.get(format!("{}/xrpc/com.atproto.repo.describeRepo", BASE_URL)) .query(¶ms) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); } #[tokio::test] async fn test_create_record_success_with_generated_rkey() { let client = client(); let (token, did) = create_account_and_login(&client).await; let payload = json!({ "repo": did, "collection": "app.bsky.feed.post", "record": { "$type": "app.bsky.feed.post", "text": "Hello, world!", "createdAt": "2025-12-02T12:00:00Z" } }); let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) .json(&payload) .bearer_auth(token) // Assuming auth is required .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.expect("Response was not valid JSON"); let uri = body["uri"].as_str().unwrap(); assert!(uri.starts_with(&format!("at://{}/app.bsky.feed.post/", did))); // assert_eq!(body["cid"], "bafyreihy"); // CID is now real } #[tokio::test] async fn test_create_record_success_with_provided_rkey() { let client = client(); let (token, did) = create_account_and_login(&client).await; let rkey = "custom-rkey"; let payload = json!({ "repo": did, "collection": "app.bsky.feed.post", "rkey": rkey, "record": { "$type": "app.bsky.feed.post", "text": "Hello, world!", "createdAt": "2025-12-02T12:00:00Z" } }); let res = client.post(format!("{}/xrpc/com.atproto.repo.createRecord", BASE_URL)) .json(&payload) .bearer_auth(token) // Assuming auth is required .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.expect("Response was not valid JSON"); assert_eq!(body["uri"], format!("at://{}/app.bsky.feed.post/{}", did, rkey)); // assert_eq!(body["cid"], "bafyreihy"); // CID is now real }