this repo has no description
1mod common; 2use common::*; 3 4use reqwest::{Client, StatusCode}; 5use serde_json::{json, Value}; 6use chrono::Utc; 7#[allow(unused_imports)] 8use std::time::Duration; 9 10async fn setup_new_user(handle_prefix: &str) -> (String, String) { 11 let client = client(); 12 let ts = Utc::now().timestamp_millis(); 13 let handle = format!("{}-{}.test", handle_prefix, ts); 14 let email = format!("{}-{}@test.com", handle_prefix, ts); 15 let password = "e2e-password-123"; 16 17 let create_account_payload = json!({ 18 "handle": handle, 19 "email": email, 20 "password": password 21 }); 22 let create_res = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base_url().await)) 23 .json(&create_account_payload) 24 .send() 25 .await 26 .expect("setup_new_user: Failed to send createAccount"); 27 28 if create_res.status() != StatusCode::OK { 29 panic!("setup_new_user: Failed to create account: {:?}", create_res.text().await); 30 } 31 32 let create_body: Value = create_res.json().await.expect("setup_new_user: createAccount response was not JSON"); 33 34 let new_did = create_body["did"].as_str().expect("setup_new_user: Response had no DID").to_string(); 35 let new_jwt = create_body["accessJwt"].as_str().expect("setup_new_user: Response had no accessJwt").to_string(); 36 37 (new_did, new_jwt) 38} 39 40#[tokio::test] 41#[ignore] 42async fn test_post_crud_lifecycle() { 43 let client = client(); 44 let (did, jwt) = setup_new_user("lifecycle-crud").await; 45 let collection = "app.bsky.feed.post"; 46 47 let rkey = format!("e2e_lifecycle_{}", Utc::now().timestamp_millis()); 48 let now = Utc::now().to_rfc3339(); 49 50 let original_text = "Hello from the lifecycle test!"; 51 let create_payload = json!({ 52 "repo": did, 53 "collection": collection, 54 "rkey": rkey, 55 "record": { 56 "$type": collection, 57 "text": original_text, 58 "createdAt": now 59 } 60 }); 61 62 let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 63 .bearer_auth(&jwt) 64 .json(&create_payload) 65 .send() 66 .await 67 .expect("Failed to send create request"); 68 69 assert_eq!(create_res.status(), StatusCode::OK, "Failed to create record"); 70 let create_body: Value = create_res.json().await.expect("create response was not JSON"); 71 let uri = create_body["uri"].as_str().unwrap(); 72 73 74 let params = [ 75 ("repo", did.as_str()), 76 ("collection", collection), 77 ("rkey", &rkey), 78 ]; 79 let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 80 .query(&params) 81 .send() 82 .await 83 .expect("Failed to send get request"); 84 85 assert_eq!(get_res.status(), StatusCode::OK, "Failed to get record after create"); 86 let get_body: Value = get_res.json().await.expect("get response was not JSON"); 87 assert_eq!(get_body["uri"], uri); 88 assert_eq!(get_body["value"]["text"], original_text); 89 90 91 let updated_text = "This post has been updated."; 92 let update_payload = json!({ 93 "repo": did, 94 "collection": collection, 95 "rkey": rkey, 96 "record": { 97 "$type": collection, 98 "text": updated_text, 99 "createdAt": now 100 } 101 }); 102 103 let update_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 104 .bearer_auth(&jwt) 105 .json(&update_payload) 106 .send() 107 .await 108 .expect("Failed to send update request"); 109 110 assert_eq!(update_res.status(), StatusCode::OK, "Failed to update record"); 111 112 113 let get_updated_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 114 .query(&params) 115 .send() 116 .await 117 .expect("Failed to send get-after-update request"); 118 119 assert_eq!(get_updated_res.status(), StatusCode::OK, "Failed to get record after update"); 120 let get_updated_body: Value = get_updated_res.json().await.expect("get-updated response was not JSON"); 121 assert_eq!(get_updated_body["value"]["text"], updated_text, "Text was not updated"); 122 123 124 let delete_payload = json!({ 125 "repo": did, 126 "collection": collection, 127 "rkey": rkey 128 }); 129 130 let delete_res = client.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base_url().await)) 131 .bearer_auth(&jwt) 132 .json(&delete_payload) 133 .send() 134 .await 135 .expect("Failed to send delete request"); 136 137 assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete record"); 138 139 140 let get_deleted_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 141 .query(&params) 142 .send() 143 .await 144 .expect("Failed to send get-after-delete request"); 145 146 assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Record was found, but it should be deleted"); 147} 148 149#[tokio::test] 150#[ignore] 151async fn test_record_update_conflict_lifecycle() { 152 let client = client(); 153 let (user_did, user_jwt) = setup_new_user("user-conflict").await; 154 155 let profile_payload = json!({ 156 "repo": user_did, 157 "collection": "app.bsky.actor.profile", 158 "rkey": "self", 159 "record": { 160 "$type": "app.bsky.actor.profile", 161 "displayName": "Original Name" 162 } 163 }); 164 let create_res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 165 .bearer_auth(&user_jwt) 166 .json(&profile_payload) 167 .send().await.expect("create profile failed"); 168 169 if create_res.status() != StatusCode::OK { 170 return; 171 } 172 173 let get_res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 174 .query(&[ 175 ("repo", &user_did), 176 ("collection", &"app.bsky.actor.profile".to_string()), 177 ("rkey", &"self".to_string()), 178 ]) 179 .send().await.expect("getRecord failed"); 180 let get_body: Value = get_res.json().await.expect("getRecord not json"); 181 let cid_v1 = get_body["cid"].as_str().expect("Profile v1 had no CID").to_string(); 182 183 let update_payload_v2 = json!({ 184 "repo": user_did, 185 "collection": "app.bsky.actor.profile", 186 "rkey": "self", 187 "record": { 188 "$type": "app.bsky.actor.profile", 189 "displayName": "Updated Name (v2)" 190 }, 191 "swapCommit": cid_v1 // <-- Correctly point to v1 192 }); 193 let update_res_v2 = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 194 .bearer_auth(&user_jwt) 195 .json(&update_payload_v2) 196 .send().await.expect("putRecord v2 failed"); 197 assert_eq!(update_res_v2.status(), StatusCode::OK, "v2 update failed"); 198 let update_body_v2: Value = update_res_v2.json().await.expect("v2 body not json"); 199 let cid_v2 = update_body_v2["cid"].as_str().expect("v2 response had no CID").to_string(); 200 201 let update_payload_v3_stale = json!({ 202 "repo": user_did, 203 "collection": "app.bsky.actor.profile", 204 "rkey": "self", 205 "record": { 206 "$type": "app.bsky.actor.profile", 207 "displayName": "Stale Update (v3)" 208 }, 209 "swapCommit": cid_v1 210 }); 211 let update_res_v3_stale = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 212 .bearer_auth(&user_jwt) 213 .json(&update_payload_v3_stale) 214 .send().await.expect("putRecord v3 (stale) failed"); 215 216 assert_eq!( 217 update_res_v3_stale.status(), 218 StatusCode::CONFLICT, 219 "Stale update did not cause a 409 Conflict" 220 ); 221 222 let update_payload_v3_good = json!({ 223 "repo": user_did, 224 "collection": "app.bsky.actor.profile", 225 "rkey": "self", 226 "record": { 227 "$type": "app.bsky.actor.profile", 228 "displayName": "Good Update (v3)" 229 }, 230 "swapCommit": cid_v2 // <-- Correct 231 }); 232 let update_res_v3_good = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 233 .bearer_auth(&user_jwt) 234 .json(&update_payload_v3_good) 235 .send().await.expect("putRecord v3 (good) failed"); 236 237 assert_eq!(update_res_v3_good.status(), StatusCode::OK, "v3 (good) update failed"); 238}