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 .last() 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 not decrease after deleting a record (was {}, now {})", 98 after_create_blocks, 99 after_delete_blocks 100 ); 101} 102 103#[tokio::test] 104async fn test_check_account_status_returns_valid_repo_rev() { 105 let client = client(); 106 let base = base_url().await; 107 let (access_jwt, _) = create_account_and_login(&client).await; 108 109 let status = client 110 .get(format!( 111 "{}/xrpc/com.atproto.server.checkAccountStatus", 112 base 113 )) 114 .bearer_auth(&access_jwt) 115 .send() 116 .await 117 .unwrap(); 118 assert_eq!(status.status(), StatusCode::OK); 119 let body: Value = status.json().await.unwrap(); 120 121 let repo_rev = body["repoRev"].as_str().unwrap(); 122 assert!(!repo_rev.is_empty(), "repoRev should not be empty"); 123 assert!( 124 repo_rev.chars().all(|c| c.is_alphanumeric()), 125 "repoRev should be alphanumeric TID" 126 ); 127} 128 129#[tokio::test] 130async fn test_check_account_status_valid_did_is_true_for_active_account() { 131 let client = client(); 132 let base = base_url().await; 133 let (access_jwt, _) = create_account_and_login(&client).await; 134 135 let status = client 136 .get(format!( 137 "{}/xrpc/com.atproto.server.checkAccountStatus", 138 base 139 )) 140 .bearer_auth(&access_jwt) 141 .send() 142 .await 143 .unwrap(); 144 assert_eq!(status.status(), StatusCode::OK); 145 let body: Value = status.json().await.unwrap(); 146 147 assert_eq!( 148 body["validDid"], true, 149 "validDid should be true for active account with correct DID document" 150 ); 151 assert_eq!( 152 body["activated"], true, 153 "activated should be true for active account" 154 ); 155} 156 157#[tokio::test] 158async fn test_deactivate_account_with_delete_after() { 159 let client = client(); 160 let base = base_url().await; 161 let (access_jwt, _) = create_account_and_login(&client).await; 162 163 let future_time = chrono::Utc::now() + chrono::Duration::hours(24); 164 let delete_after = future_time.to_rfc3339(); 165 166 let deactivate = client 167 .post(format!( 168 "{}/xrpc/com.atproto.server.deactivateAccount", 169 base 170 )) 171 .bearer_auth(&access_jwt) 172 .json(&json!({ 173 "deleteAfter": delete_after 174 })) 175 .send() 176 .await 177 .unwrap(); 178 assert_eq!(deactivate.status(), StatusCode::OK); 179 180 let status = client 181 .get(format!( 182 "{}/xrpc/com.atproto.server.checkAccountStatus", 183 base 184 )) 185 .bearer_auth(&access_jwt) 186 .send() 187 .await 188 .unwrap(); 189 assert_eq!(status.status(), StatusCode::OK); 190 let body: Value = status.json().await.unwrap(); 191 assert_eq!(body["activated"], false, "Account should be deactivated"); 192} 193 194#[tokio::test] 195async fn test_create_account_returns_did_doc() { 196 let client = client(); 197 let base = base_url().await; 198 199 let handle = format!("dd{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 200 let payload = json!({ 201 "handle": handle, 202 "email": format!("{}@example.com", handle), 203 "password": "Testpass123!" 204 }); 205 206 let create_res = client 207 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 208 .json(&payload) 209 .send() 210 .await 211 .unwrap(); 212 assert_eq!(create_res.status(), StatusCode::OK); 213 let body: Value = create_res.json().await.unwrap(); 214 215 assert!( 216 body["accessJwt"].is_string(), 217 "accessJwt should always be returned" 218 ); 219 assert!( 220 body["refreshJwt"].is_string(), 221 "refreshJwt should always be returned" 222 ); 223 assert!(body["did"].is_string(), "did should be returned"); 224 225 if body["didDoc"].is_object() { 226 let did_doc = &body["didDoc"]; 227 assert!(did_doc["id"].is_string(), "didDoc should have id field"); 228 } 229} 230 231#[tokio::test] 232async fn test_create_account_always_returns_tokens() { 233 let client = client(); 234 let base = base_url().await; 235 236 let handle = format!("tt{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 237 let payload = json!({ 238 "handle": handle, 239 "email": format!("{}@example.com", handle), 240 "password": "Testpass123!" 241 }); 242 243 let create_res = client 244 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 245 .json(&payload) 246 .send() 247 .await 248 .unwrap(); 249 assert_eq!(create_res.status(), StatusCode::OK); 250 let body: Value = create_res.json().await.unwrap(); 251 252 let access_jwt = body["accessJwt"] 253 .as_str() 254 .expect("accessJwt should be present"); 255 let refresh_jwt = body["refreshJwt"] 256 .as_str() 257 .expect("refreshJwt should be present"); 258 259 assert!(!access_jwt.is_empty(), "accessJwt should not be empty"); 260 assert!(!refresh_jwt.is_empty(), "refreshJwt should not be empty"); 261 262 let parts: Vec<&str> = access_jwt.split('.').collect(); 263 assert_eq!( 264 parts.len(), 265 3, 266 "accessJwt should be a valid JWT with 3 parts" 267 ); 268} 269 270#[tokio::test] 271async fn test_describe_server_has_links_and_contact() { 272 let client = client(); 273 let base = base_url().await; 274 275 let describe = client 276 .get(format!("{}/xrpc/com.atproto.server.describeServer", base)) 277 .send() 278 .await 279 .unwrap(); 280 assert_eq!(describe.status(), StatusCode::OK); 281 let body: Value = describe.json().await.unwrap(); 282 283 assert!( 284 body.get("links").is_some(), 285 "describeServer should include links object" 286 ); 287 assert!( 288 body.get("contact").is_some(), 289 "describeServer should include contact object" 290 ); 291 292 let links = &body["links"]; 293 assert!( 294 links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(), 295 "links should have privacyPolicy field (can be null)" 296 ); 297 assert!( 298 links.get("termsOfService").is_some() || links["termsOfService"].is_null(), 299 "links should have termsOfService field (can be null)" 300 ); 301 302 let contact = &body["contact"]; 303 assert!( 304 contact.get("email").is_some() || contact["email"].is_null(), 305 "contact should have email field (can be null)" 306 ); 307} 308 309#[tokio::test] 310async fn test_delete_account_password_max_length() { 311 let client = client(); 312 let base = base_url().await; 313 314 let handle = format!("pl{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 315 let payload = json!({ 316 "handle": handle, 317 "email": format!("{}@example.com", handle), 318 "password": "Testpass123!" 319 }); 320 321 let create_res = client 322 .post(format!("{}/xrpc/com.atproto.server.createAccount", base)) 323 .json(&payload) 324 .send() 325 .await 326 .unwrap(); 327 assert_eq!(create_res.status(), StatusCode::OK); 328 let body: Value = create_res.json().await.unwrap(); 329 let did = body["did"].as_str().unwrap(); 330 331 let too_long_password = "a".repeat(600); 332 let delete_res = client 333 .post(format!("{}/xrpc/com.atproto.server.deleteAccount", base)) 334 .json(&json!({ 335 "did": did, 336 "password": too_long_password, 337 "token": "fake-token" 338 })) 339 .send() 340 .await 341 .unwrap(); 342 343 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 344 let error_body: Value = delete_res.json().await.unwrap(); 345 assert!( 346 error_body["message"] 347 .as_str() 348 .unwrap() 349 .contains("password length") 350 || error_body["error"].as_str().unwrap() == "InvalidRequest" 351 ); 352}