this repo has no description
1mod common; 2mod helpers; 3use common::*; 4use reqwest::StatusCode; 5use serde_json::{Value, json}; 6 7#[tokio::test] 8async fn test_check_account_status_returns_correct_block_count() { 9 let client = client(); 10 let base = base_url().await; 11 let (access_jwt, did) = create_account_and_login(&client).await; 12 13 let status1 = client 14 .get(format!( 15 "{}/xrpc/com.atproto.server.checkAccountStatus", 16 base 17 )) 18 .bearer_auth(&access_jwt) 19 .send() 20 .await 21 .unwrap(); 22 assert_eq!(status1.status(), StatusCode::OK); 23 let body1: Value = status1.json().await.unwrap(); 24 let initial_blocks = body1["repoBlocks"].as_i64().unwrap(); 25 assert!( 26 initial_blocks >= 2, 27 "New account should have at least 2 blocks (commit + empty MST)" 28 ); 29 30 let create_res = client 31 .post(format!("{}/xrpc/com.atproto.repo.createRecord", base)) 32 .bearer_auth(&access_jwt) 33 .json(&json!({ 34 "repo": did, 35 "collection": "app.bsky.feed.post", 36 "record": { 37 "$type": "app.bsky.feed.post", 38 "text": "Test post for block counting", 39 "createdAt": chrono::Utc::now().to_rfc3339() 40 } 41 })) 42 .send() 43 .await 44 .unwrap(); 45 assert_eq!(create_res.status(), StatusCode::OK); 46 let create_body: Value = create_res.json().await.unwrap(); 47 let rkey = create_body["uri"] 48 .as_str() 49 .unwrap() 50 .split('/') 51 .next_back() 52 .unwrap() 53 .to_string(); 54 55 let status2 = client 56 .get(format!( 57 "{}/xrpc/com.atproto.server.checkAccountStatus", 58 base 59 )) 60 .bearer_auth(&access_jwt) 61 .send() 62 .await 63 .unwrap(); 64 let body2: Value = status2.json().await.unwrap(); 65 let after_create_blocks = body2["repoBlocks"].as_i64().unwrap(); 66 assert!( 67 after_create_blocks > initial_blocks, 68 "Block count should increase after creating a record" 69 ); 70 71 let delete_res = client 72 .post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base)) 73 .bearer_auth(&access_jwt) 74 .json(&json!({ 75 "repo": did, 76 "collection": "app.bsky.feed.post", 77 "rkey": rkey 78 })) 79 .send() 80 .await 81 .unwrap(); 82 assert_eq!(delete_res.status(), StatusCode::OK); 83 84 let status3 = client 85 .get(format!( 86 "{}/xrpc/com.atproto.server.checkAccountStatus", 87 base 88 )) 89 .bearer_auth(&access_jwt) 90 .send() 91 .await 92 .unwrap(); 93 let body3: Value = status3.json().await.unwrap(); 94 let after_delete_blocks = body3["repoBlocks"].as_i64().unwrap(); 95 assert!( 96 after_delete_blocks <= after_create_blocks, 97 "Block count should decrease or stay same after deleting a record (was {}, now {})", 98 after_create_blocks, 99 after_delete_blocks 100 ); 101 assert!( 102 after_delete_blocks >= initial_blocks, 103 "Block count after delete should be at least initial count (initial {}, now {})", 104 initial_blocks, 105 after_delete_blocks 106 ); 107} 108 109#[tokio::test] 110async fn test_check_account_status_returns_valid_repo_rev() { 111 let client = client(); 112 let base = base_url().await; 113 let (access_jwt, _) = create_account_and_login(&client).await; 114 115 let status = client 116 .get(format!( 117 "{}/xrpc/com.atproto.server.checkAccountStatus", 118 base 119 )) 120 .bearer_auth(&access_jwt) 121 .send() 122 .await 123 .unwrap(); 124 assert_eq!(status.status(), StatusCode::OK); 125 let body: Value = status.json().await.unwrap(); 126 127 let repo_rev = body["repoRev"].as_str().unwrap(); 128 assert!(!repo_rev.is_empty(), "repoRev should not be empty"); 129 assert!( 130 repo_rev.chars().all(|c| c.is_alphanumeric()), 131 "repoRev should be alphanumeric TID" 132 ); 133} 134 135#[tokio::test] 136async fn test_check_account_status_valid_did_is_true_for_active_account() { 137 let client = client(); 138 let base = base_url().await; 139 let (access_jwt, _) = create_account_and_login(&client).await; 140 141 let status = client 142 .get(format!( 143 "{}/xrpc/com.atproto.server.checkAccountStatus", 144 base 145 )) 146 .bearer_auth(&access_jwt) 147 .send() 148 .await 149 .unwrap(); 150 assert_eq!(status.status(), StatusCode::OK); 151 let body: Value = status.json().await.unwrap(); 152 153 assert_eq!( 154 body["validDid"], true, 155 "validDid should be true for active account with correct DID document" 156 ); 157 assert_eq!( 158 body["activated"], true, 159 "activated should be true for active account" 160 ); 161} 162 163#[tokio::test] 164async fn test_deactivate_account_with_delete_after() { 165 let client = client(); 166 let base = base_url().await; 167 let (access_jwt, _) = create_account_and_login(&client).await; 168 169 let future_time = chrono::Utc::now() + chrono::Duration::hours(24); 170 let delete_after = future_time.to_rfc3339(); 171 172 let deactivate = client 173 .post(format!( 174 "{}/xrpc/com.atproto.server.deactivateAccount", 175 base 176 )) 177 .bearer_auth(&access_jwt) 178 .json(&json!({ 179 "deleteAfter": delete_after 180 })) 181 .send() 182 .await 183 .unwrap(); 184 assert_eq!(deactivate.status(), StatusCode::OK); 185 186 let status = client 187 .get(format!( 188 "{}/xrpc/com.atproto.server.checkAccountStatus", 189 base 190 )) 191 .bearer_auth(&access_jwt) 192 .send() 193 .await 194 .unwrap(); 195 assert_eq!(status.status(), StatusCode::OK); 196 let body: Value = status.json().await.unwrap(); 197 assert_eq!(body["activated"], false, "Account should be deactivated"); 198} 199 200#[tokio::test] 201async fn test_create_account_returns_did_doc() { 202 let client = client(); 203 let base = base_url().await; 204 205 let handle = format!("dd{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 206 let payload = json!({ 207 "handle": handle, 208 "email": format!("{}@example.com", handle), 209 "password": "Testpass123!" 210 }); 211 212 let create_res = client 213 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 214 .json(&payload) 215 .send() 216 .await 217 .unwrap(); 218 assert_eq!(create_res.status(), StatusCode::OK); 219 let body: Value = create_res.json().await.unwrap(); 220 221 assert!( 222 body["accessJwt"].is_string(), 223 "accessJwt should always be returned" 224 ); 225 assert!( 226 body["refreshJwt"].is_string(), 227 "refreshJwt should always be returned" 228 ); 229 assert!(body["did"].is_string(), "did should be returned"); 230 231 if body["didDoc"].is_object() { 232 let did_doc = &body["didDoc"]; 233 assert!(did_doc["id"].is_string(), "didDoc should have id field"); 234 } 235} 236 237#[tokio::test] 238async fn test_create_account_always_returns_tokens() { 239 let client = client(); 240 let base = base_url().await; 241 242 let handle = format!("tt{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 243 let payload = json!({ 244 "handle": handle, 245 "email": format!("{}@example.com", handle), 246 "password": "Testpass123!" 247 }); 248 249 let create_res = client 250 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 251 .json(&payload) 252 .send() 253 .await 254 .unwrap(); 255 assert_eq!(create_res.status(), StatusCode::OK); 256 let body: Value = create_res.json().await.unwrap(); 257 258 let access_jwt = body["accessJwt"] 259 .as_str() 260 .expect("accessJwt should be present"); 261 let refresh_jwt = body["refreshJwt"] 262 .as_str() 263 .expect("refreshJwt should be present"); 264 265 assert!(!access_jwt.is_empty(), "accessJwt should not be empty"); 266 assert!(!refresh_jwt.is_empty(), "refreshJwt should not be empty"); 267 268 let parts: Vec<&str> = access_jwt.split('.').collect(); 269 assert_eq!( 270 parts.len(), 271 3, 272 "accessJwt should be a valid JWT with 3 parts" 273 ); 274} 275 276#[tokio::test] 277async fn test_describe_server_has_links_and_contact() { 278 let client = client(); 279 let base = base_url().await; 280 281 let describe = client 282 .get(format!("{}/xrpc/com.atproto.server.describeServer", base)) 283 .send() 284 .await 285 .unwrap(); 286 assert_eq!(describe.status(), StatusCode::OK); 287 let body: Value = describe.json().await.unwrap(); 288 289 assert!( 290 body.get("links").is_some(), 291 "describeServer should include links object" 292 ); 293 assert!( 294 body.get("contact").is_some(), 295 "describeServer should include contact object" 296 ); 297 298 let links = &body["links"]; 299 assert!( 300 links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(), 301 "links should have privacyPolicy field (can be null)" 302 ); 303 assert!( 304 links.get("termsOfService").is_some() || links["termsOfService"].is_null(), 305 "links should have termsOfService field (can be null)" 306 ); 307 308 let contact = &body["contact"]; 309 assert!( 310 contact.get("email").is_some() || contact["email"].is_null(), 311 "contact should have email field (can be null)" 312 ); 313} 314 315#[tokio::test] 316async fn test_delete_account_password_max_length() { 317 let client = client(); 318 let base = base_url().await; 319 320 let handle = format!("pl{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 321 let payload = json!({ 322 "handle": handle, 323 "email": format!("{}@example.com", handle), 324 "password": "Testpass123!" 325 }); 326 327 let create_res = client 328 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 329 .json(&payload) 330 .send() 331 .await 332 .unwrap(); 333 assert_eq!(create_res.status(), StatusCode::OK); 334 let body: Value = create_res.json().await.unwrap(); 335 let did = body["did"].as_str().unwrap(); 336 337 let too_long_password = "a".repeat(600); 338 let delete_res = client 339 .post(format!("{}/xrpc/com.atproto.server.deleteAccount", base)) 340 .json(&json!({ 341 "did": did, 342 "password": too_long_password, 343 "token": "fake-token" 344 })) 345 .send() 346 .await 347 .unwrap(); 348 349 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 350 let error_body: Value = delete_res.json().await.unwrap(); 351 assert!( 352 error_body["message"] 353 .as_str() 354 .unwrap() 355 .contains("password length") 356 || error_body["error"].as_str().unwrap() == "InvalidRequest" 357 ); 358}