this repo has no description
1mod common; 2use common::*; 3use reqwest::StatusCode; 4use serde_json::{Value, json}; 5use wiremock::matchers::{method, path}; 6use wiremock::{Mock, MockServer, ResponseTemplate}; 7 8#[tokio::test] 9async fn test_resolve_handle_success() { 10 let client = client(); 11 let short_handle = format!("resolvetest-{}", uuid::Uuid::new_v4()); 12 let payload = json!({ 13 "handle": short_handle, 14 "email": format!("{}@example.com", short_handle), 15 "password": "Testpass123!" 16 }); 17 let res = client 18 .post(format!( 19 "{}/xrpc/com.atproto.server.createAccount", 20 base_url().await 21 )) 22 .json(&payload) 23 .send() 24 .await 25 .expect("Failed to create account"); 26 assert_eq!(res.status(), StatusCode::OK); 27 let body: Value = res.json().await.expect("Invalid JSON"); 28 let did = body["did"].as_str().expect("No DID").to_string(); 29 let full_handle = body["handle"] 30 .as_str() 31 .expect("No handle in response") 32 .to_string(); 33 let params = [("handle", full_handle.as_str())]; 34 let res = client 35 .get(format!( 36 "{}/xrpc/com.atproto.identity.resolveHandle", 37 base_url().await 38 )) 39 .query(&params) 40 .send() 41 .await 42 .expect("Failed to send request"); 43 assert_eq!(res.status(), StatusCode::OK); 44 let body: Value = res.json().await.expect("Response was not valid JSON"); 45 assert_eq!(body["did"], did); 46} 47 48#[tokio::test] 49async fn test_resolve_handle_not_found() { 50 let client = client(); 51 let params = [("handle", "nonexistent_handle_12345")]; 52 let res = client 53 .get(format!( 54 "{}/xrpc/com.atproto.identity.resolveHandle", 55 base_url().await 56 )) 57 .query(&params) 58 .send() 59 .await 60 .expect("Failed to send request"); 61 assert_eq!(res.status(), StatusCode::NOT_FOUND); 62 let body: Value = res.json().await.expect("Response was not valid JSON"); 63 assert_eq!(body["error"], "HandleNotFound"); 64} 65 66#[tokio::test] 67async fn test_resolve_handle_missing_param() { 68 let client = client(); 69 let res = client 70 .get(format!( 71 "{}/xrpc/com.atproto.identity.resolveHandle", 72 base_url().await 73 )) 74 .send() 75 .await 76 .expect("Failed to send request"); 77 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 78} 79 80#[tokio::test] 81async fn test_well_known_did() { 82 let client = client(); 83 let res = client 84 .get(format!("{}/.well-known/did.json", base_url().await)) 85 .send() 86 .await 87 .expect("Failed to send request"); 88 assert_eq!(res.status(), StatusCode::OK); 89 let body: Value = res.json().await.expect("Response was not valid JSON"); 90 assert!(body["id"].as_str().unwrap().starts_with("did:web:")); 91 assert_eq!(body["service"][0]["type"], "AtprotoPersonalDataServer"); 92} 93 94#[tokio::test] 95async fn test_create_did_web_account_and_resolve() { 96 let client = client(); 97 let mock_server = MockServer::start().await; 98 let mock_uri = mock_server.uri(); 99 let mock_addr = mock_uri.trim_start_matches("http://"); 100 let did = format!("did:web:{}", mock_addr.replace(":", "%3A")); 101 let handle = format!("webuser-{}", uuid::Uuid::new_v4()); 102 let pds_endpoint = base_url().await.replace("http://", "https://"); 103 104 let reserve_res = client 105 .post(format!( 106 "{}/xrpc/com.atproto.server.reserveSigningKey", 107 base_url().await 108 )) 109 .json(&json!({ "did": did })) 110 .send() 111 .await 112 .expect("Failed to reserve signing key"); 113 assert_eq!(reserve_res.status(), StatusCode::OK); 114 let reserve_body: Value = reserve_res.json().await.expect("Response was not JSON"); 115 let signing_key = reserve_body["signingKey"] 116 .as_str() 117 .expect("No signingKey returned"); 118 let public_key_multibase = signing_key 119 .strip_prefix("did:key:") 120 .expect("signingKey should start with did:key:"); 121 122 let did_doc = json!({ 123 "@context": ["https://www.w3.org/ns/did/v1"], 124 "id": did, 125 "verificationMethod": [{ 126 "id": format!("{}#atproto", did), 127 "type": "Multikey", 128 "controller": did, 129 "publicKeyMultibase": public_key_multibase 130 }], 131 "service": [{ 132 "id": "#atproto_pds", 133 "type": "AtprotoPersonalDataServer", 134 "serviceEndpoint": pds_endpoint 135 }] 136 }); 137 Mock::given(method("GET")) 138 .and(path("/.well-known/did.json")) 139 .respond_with(ResponseTemplate::new(200).set_body_json(did_doc)) 140 .mount(&mock_server) 141 .await; 142 let payload = json!({ 143 "handle": handle, 144 "email": format!("{}@example.com", handle), 145 "password": "Testpass123!", 146 "did": did, 147 "signingKey": signing_key 148 }); 149 let res = client 150 .post(format!( 151 "{}/xrpc/com.atproto.server.createAccount", 152 base_url().await 153 )) 154 .json(&payload) 155 .send() 156 .await 157 .expect("Failed to send request"); 158 if res.status() != StatusCode::OK { 159 let status = res.status(); 160 let body: Value = res 161 .json() 162 .await 163 .unwrap_or(json!({"error": "could not parse body"})); 164 panic!("createAccount failed with status {}: {:?}", status, body); 165 } 166 let body: Value = res 167 .json() 168 .await 169 .expect("createAccount response was not JSON"); 170 assert_eq!(body["did"], did); 171 let res = client 172 .get(format!("{}/u/{}/did.json", base_url().await, handle)) 173 .send() 174 .await 175 .expect("Failed to fetch DID doc"); 176 assert_eq!( 177 res.status(), 178 StatusCode::NOT_FOUND, 179 "External did:web should not have DID doc served by PDS (user hosts their own)" 180 ); 181} 182 183#[tokio::test] 184async fn test_create_account_duplicate_handle() { 185 let client = client(); 186 let handle = format!("dupe-{}", uuid::Uuid::new_v4()); 187 let email = format!("{}@example.com", handle); 188 let payload = json!({ 189 "handle": handle, 190 "email": email, 191 "password": "Testpass123!" 192 }); 193 let res = client 194 .post(format!( 195 "{}/xrpc/com.atproto.server.createAccount", 196 base_url().await 197 )) 198 .json(&payload) 199 .send() 200 .await 201 .expect("Failed to send request"); 202 assert_eq!(res.status(), StatusCode::OK); 203 let res = client 204 .post(format!( 205 "{}/xrpc/com.atproto.server.createAccount", 206 base_url().await 207 )) 208 .json(&payload) 209 .send() 210 .await 211 .expect("Failed to send request"); 212 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 213 let body: Value = res.json().await.expect("Response was not JSON"); 214 assert_eq!(body["error"], "HandleTaken"); 215} 216 217#[tokio::test] 218async fn test_did_web_lifecycle() { 219 let client = client(); 220 let mock_server = MockServer::start().await; 221 let mock_uri = mock_server.uri(); 222 let mock_addr = mock_uri.trim_start_matches("http://"); 223 let handle = format!("lifecycle-{}", uuid::Uuid::new_v4()); 224 let did = format!("did:web:{}:u:{}", mock_addr.replace(":", "%3A"), handle); 225 let email = format!("{}@test.com", handle); 226 let pds_endpoint = base_url().await.replace("http://", "https://"); 227 228 let reserve_res = client 229 .post(format!( 230 "{}/xrpc/com.atproto.server.reserveSigningKey", 231 base_url().await 232 )) 233 .json(&json!({ "did": did })) 234 .send() 235 .await 236 .expect("Failed to reserve signing key"); 237 assert_eq!(reserve_res.status(), StatusCode::OK); 238 let reserve_body: Value = reserve_res.json().await.expect("Response was not JSON"); 239 let signing_key = reserve_body["signingKey"] 240 .as_str() 241 .expect("No signingKey returned"); 242 let public_key_multibase = signing_key 243 .strip_prefix("did:key:") 244 .expect("signingKey should start with did:key:"); 245 246 let did_doc = json!({ 247 "@context": ["https://www.w3.org/ns/did/v1"], 248 "id": did, 249 "verificationMethod": [{ 250 "id": format!("{}#atproto", did), 251 "type": "Multikey", 252 "controller": did, 253 "publicKeyMultibase": public_key_multibase 254 }], 255 "service": [{ 256 "id": "#atproto_pds", 257 "type": "AtprotoPersonalDataServer", 258 "serviceEndpoint": pds_endpoint 259 }] 260 }); 261 Mock::given(method("GET")) 262 .and(path(format!("/u/{}/did.json", handle))) 263 .respond_with(ResponseTemplate::new(200).set_body_json(did_doc)) 264 .mount(&mock_server) 265 .await; 266 let create_payload = json!({ 267 "handle": handle, 268 "email": email, 269 "password": "Testpass123!", 270 "did": did, 271 "signingKey": signing_key 272 }); 273 let res = client 274 .post(format!( 275 "{}/xrpc/com.atproto.server.createAccount", 276 base_url().await 277 )) 278 .json(&create_payload) 279 .send() 280 .await 281 .expect("Failed createAccount"); 282 if res.status() != StatusCode::OK { 283 let body: Value = res.json().await.unwrap(); 284 println!("createAccount failed: {:?}", body); 285 panic!("createAccount returned non-200"); 286 } 287 assert_eq!(res.status(), StatusCode::OK); 288 let create_body: Value = res.json().await.expect("Not JSON"); 289 assert_eq!(create_body["did"], did); 290 let _jwt = verify_new_account(&client, &did).await; 291 /* 292 let profile_payload = json!({ 293 "repo": did, 294 "collection": "app.bsky.actor.profile", 295 "rkey": "self", 296 "record": { 297 "$type": "app.bsky.actor.profile", 298 "displayName": "DID Web User", 299 "description": "Testing lifecycle" 300 } 301 }); 302 let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 303 .bearer_auth(_jwt) 304 .json(&profile_payload) 305 .send() 306 .await 307 .expect("Failed putRecord"); 308 if res.status() != StatusCode::OK { 309 let body: Value = res.json().await.unwrap(); 310 println!("putRecord failed: {:?}", body); 311 panic!("putRecord returned non-200"); 312 } 313 assert_eq!(res.status(), StatusCode::OK); 314 let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 315 .query(&[ 316 ("repo", &handle), 317 ("collection", &"app.bsky.actor.profile".to_string()), 318 ("rkey", &"self".to_string()) 319 ]) 320 .send() 321 .await 322 .expect("Failed getRecord"); 323 if res.status() != StatusCode::OK { 324 let body: Value = res.json().await.unwrap(); 325 println!("getRecord failed: {:?}", body); 326 panic!("getRecord returned non-200"); 327 } 328 let record_body: Value = res.json().await.expect("Not JSON"); 329 assert_eq!(record_body["value"]["displayName"], "DID Web User"); 330 */ 331} 332 333#[tokio::test] 334async fn test_get_recommended_did_credentials_success() { 335 let client = client(); 336 let (access_jwt, _) = create_account_and_login(&client).await; 337 let res = client 338 .get(format!( 339 "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials", 340 base_url().await 341 )) 342 .bearer_auth(&access_jwt) 343 .send() 344 .await 345 .expect("Failed to send request"); 346 assert_eq!(res.status(), StatusCode::OK); 347 let body: Value = res.json().await.expect("Response was not valid JSON"); 348 assert!(body["rotationKeys"].is_array()); 349 assert!(body["alsoKnownAs"].is_array()); 350 assert!(body["verificationMethods"].is_object()); 351 assert!(body["services"].is_object()); 352 let rotation_keys = body["rotationKeys"].as_array().unwrap(); 353 assert!(!rotation_keys.is_empty()); 354 assert!(rotation_keys[0].as_str().unwrap().starts_with("did:key:")); 355 let also_known_as = body["alsoKnownAs"].as_array().unwrap(); 356 assert!(!also_known_as.is_empty()); 357 assert!(also_known_as[0].as_str().unwrap().starts_with("at://")); 358 assert!(body["verificationMethods"]["atproto"].is_string()); 359 assert_eq!( 360 body["services"]["atproto_pds"]["type"], 361 "AtprotoPersonalDataServer" 362 ); 363 assert!(body["services"]["atproto_pds"]["endpoint"].is_string()); 364} 365 366#[tokio::test] 367async fn test_get_recommended_did_credentials_no_auth() { 368 let client = client(); 369 let res = client 370 .get(format!( 371 "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials", 372 base_url().await 373 )) 374 .send() 375 .await 376 .expect("Failed to send request"); 377 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 378 let body: Value = res.json().await.expect("Response was not valid JSON"); 379 assert_eq!(body["error"], "AuthenticationRequired"); 380} 381 382#[tokio::test] 383async fn test_update_handle_to_same() { 384 let client = client(); 385 let (access_jwt, _did) = create_account_and_login(&client).await; 386 let session = client 387 .get(format!( 388 "{}/xrpc/com.atproto.server.getSession", 389 base_url().await 390 )) 391 .bearer_auth(&access_jwt) 392 .send() 393 .await 394 .expect("Failed to get session"); 395 let session_body: Value = session.json().await.expect("Invalid JSON"); 396 let current_handle = session_body["handle"].as_str().expect("No handle").to_string(); 397 let short_handle = current_handle.split('.').next().unwrap_or(&current_handle); 398 let res = client 399 .post(format!( 400 "{}/xrpc/com.atproto.identity.updateHandle", 401 base_url().await 402 )) 403 .bearer_auth(&access_jwt) 404 .json(&json!({ "handle": short_handle })) 405 .send() 406 .await 407 .expect("Failed to send request"); 408 assert_eq!(res.status(), StatusCode::OK); 409} 410 411#[tokio::test] 412async fn test_update_handle_no_auth() { 413 let client = client(); 414 let res = client 415 .post(format!( 416 "{}/xrpc/com.atproto.identity.updateHandle", 417 base_url().await 418 )) 419 .json(&json!({ "handle": "newhandle" })) 420 .send() 421 .await 422 .expect("Failed to send request"); 423 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 424 let body: Value = res.json().await.expect("Response was not valid JSON"); 425 assert_eq!(body["error"], "AuthenticationRequired"); 426} 427 428#[tokio::test] 429async fn test_update_handle_invalid_characters() { 430 let client = client(); 431 let (access_jwt, _did) = create_account_and_login(&client).await; 432 let res = client 433 .post(format!( 434 "{}/xrpc/com.atproto.identity.updateHandle", 435 base_url().await 436 )) 437 .bearer_auth(&access_jwt) 438 .json(&json!({ "handle": "invalid@handle!" })) 439 .send() 440 .await 441 .expect("Failed to send request"); 442 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 443 let body: Value = res.json().await.expect("Response was not valid JSON"); 444 assert_eq!(body["error"], "InvalidHandle"); 445} 446 447#[tokio::test] 448async fn test_update_handle_empty() { 449 let client = client(); 450 let (access_jwt, _did) = create_account_and_login(&client).await; 451 let res = client 452 .post(format!( 453 "{}/xrpc/com.atproto.identity.updateHandle", 454 base_url().await 455 )) 456 .bearer_auth(&access_jwt) 457 .json(&json!({ "handle": "" })) 458 .send() 459 .await 460 .expect("Failed to send request"); 461 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 462 let body: Value = res.json().await.expect("Response was not valid JSON"); 463 assert_eq!(body["error"], "InvalidRequest"); 464} 465 466#[tokio::test] 467async fn test_update_handle_taken() { 468 let client = client(); 469 let (access_jwt1, _did1) = create_account_and_login(&client).await; 470 let (access_jwt2, _did2) = create_account_and_login(&client).await; 471 let short_handle = format!("taken{}", &uuid::Uuid::new_v4().to_string()[..8]); 472 let update1 = client 473 .post(format!( 474 "{}/xrpc/com.atproto.identity.updateHandle", 475 base_url().await 476 )) 477 .bearer_auth(&access_jwt1) 478 .json(&json!({ "handle": short_handle })) 479 .send() 480 .await 481 .expect("Failed to update handle"); 482 assert_eq!(update1.status(), StatusCode::OK); 483 let res = client 484 .post(format!( 485 "{}/xrpc/com.atproto.identity.updateHandle", 486 base_url().await 487 )) 488 .bearer_auth(&access_jwt2) 489 .json(&json!({ "handle": short_handle })) 490 .send() 491 .await 492 .expect("Failed to send request"); 493 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 494 let body: Value = res.json().await.expect("Response was not valid JSON"); 495 assert_eq!(body["error"], "HandleTaken"); 496} 497 498#[tokio::test] 499async fn test_update_handle_too_short() { 500 let client = client(); 501 let (access_jwt, _did) = create_account_and_login(&client).await; 502 let res = client 503 .post(format!( 504 "{}/xrpc/com.atproto.identity.updateHandle", 505 base_url().await 506 )) 507 .bearer_auth(&access_jwt) 508 .json(&json!({ "handle": "ab" })) 509 .send() 510 .await 511 .expect("Failed to send request"); 512 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 513 let body: Value = res.json().await.expect("Response was not valid JSON"); 514 assert_eq!(body["error"], "InvalidHandle"); 515 assert!(body["message"].as_str().unwrap().contains("short")); 516} 517 518#[tokio::test] 519async fn test_update_handle_too_long() { 520 let client = client(); 521 let (access_jwt, _did) = create_account_and_login(&client).await; 522 let res = client 523 .post(format!( 524 "{}/xrpc/com.atproto.identity.updateHandle", 525 base_url().await 526 )) 527 .bearer_auth(&access_jwt) 528 .json(&json!({ "handle": "thishandleiswaytoolongforservicedomain" })) 529 .send() 530 .await 531 .expect("Failed to send request"); 532 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 533 let body: Value = res.json().await.expect("Response was not valid JSON"); 534 assert_eq!(body["error"], "InvalidHandle"); 535 assert!(body["message"].as_str().unwrap().contains("long")); 536}