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!("rt{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 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!("wu{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 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!("dp{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 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!("lc{}", &uuid::Uuid::new_v4().simple().to_string()[..12]); 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"] 397 .as_str() 398 .expect("No handle") 399 .to_string(); 400 let short_handle = current_handle.split('.').next().unwrap_or(&current_handle); 401 let res = client 402 .post(format!( 403 "{}/xrpc/com.atproto.identity.updateHandle", 404 base_url().await 405 )) 406 .bearer_auth(&access_jwt) 407 .json(&json!({ "handle": short_handle })) 408 .send() 409 .await 410 .expect("Failed to send request"); 411 assert_eq!(res.status(), StatusCode::OK); 412} 413 414#[tokio::test] 415async fn test_update_handle_no_auth() { 416 let client = client(); 417 let res = client 418 .post(format!( 419 "{}/xrpc/com.atproto.identity.updateHandle", 420 base_url().await 421 )) 422 .json(&json!({ "handle": "newhandle" })) 423 .send() 424 .await 425 .expect("Failed to send request"); 426 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 427 let body: Value = res.json().await.expect("Response was not valid JSON"); 428 assert_eq!(body["error"], "AuthenticationRequired"); 429} 430 431#[tokio::test] 432async fn test_update_handle_invalid_characters() { 433 let client = client(); 434 let (access_jwt, _did) = create_account_and_login(&client).await; 435 let res = client 436 .post(format!( 437 "{}/xrpc/com.atproto.identity.updateHandle", 438 base_url().await 439 )) 440 .bearer_auth(&access_jwt) 441 .json(&json!({ "handle": "invalid@handle!" })) 442 .send() 443 .await 444 .expect("Failed to send request"); 445 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 446 let body: Value = res.json().await.expect("Response was not valid JSON"); 447 assert_eq!(body["error"], "InvalidHandle"); 448} 449 450#[tokio::test] 451async fn test_update_handle_empty() { 452 let client = client(); 453 let (access_jwt, _did) = create_account_and_login(&client).await; 454 let res = client 455 .post(format!( 456 "{}/xrpc/com.atproto.identity.updateHandle", 457 base_url().await 458 )) 459 .bearer_auth(&access_jwt) 460 .json(&json!({ "handle": "" })) 461 .send() 462 .await 463 .expect("Failed to send request"); 464 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 465 let body: Value = res.json().await.expect("Response was not valid JSON"); 466 assert_eq!(body["error"], "InvalidRequest"); 467} 468 469#[tokio::test] 470async fn test_update_handle_taken() { 471 let client = client(); 472 let (access_jwt1, _did1) = create_account_and_login(&client).await; 473 let (access_jwt2, _did2) = create_account_and_login(&client).await; 474 let short_handle = format!("taken{}", &uuid::Uuid::new_v4().to_string()[..8]); 475 let update1 = client 476 .post(format!( 477 "{}/xrpc/com.atproto.identity.updateHandle", 478 base_url().await 479 )) 480 .bearer_auth(&access_jwt1) 481 .json(&json!({ "handle": short_handle })) 482 .send() 483 .await 484 .expect("Failed to update handle"); 485 assert_eq!(update1.status(), StatusCode::OK); 486 let res = client 487 .post(format!( 488 "{}/xrpc/com.atproto.identity.updateHandle", 489 base_url().await 490 )) 491 .bearer_auth(&access_jwt2) 492 .json(&json!({ "handle": short_handle })) 493 .send() 494 .await 495 .expect("Failed to send request"); 496 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 497 let body: Value = res.json().await.expect("Response was not valid JSON"); 498 assert_eq!(body["error"], "HandleTaken"); 499} 500 501#[tokio::test] 502async fn test_update_handle_too_short() { 503 let client = client(); 504 let (access_jwt, _did) = create_account_and_login(&client).await; 505 let res = client 506 .post(format!( 507 "{}/xrpc/com.atproto.identity.updateHandle", 508 base_url().await 509 )) 510 .bearer_auth(&access_jwt) 511 .json(&json!({ "handle": "ab" })) 512 .send() 513 .await 514 .expect("Failed to send request"); 515 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 516 let body: Value = res.json().await.expect("Response was not valid JSON"); 517 assert_eq!(body["error"], "InvalidHandle"); 518 assert!(body["message"].as_str().unwrap().contains("short")); 519} 520 521#[tokio::test] 522async fn test_update_handle_too_long() { 523 let client = client(); 524 let (access_jwt, _did) = create_account_and_login(&client).await; 525 let res = client 526 .post(format!( 527 "{}/xrpc/com.atproto.identity.updateHandle", 528 base_url().await 529 )) 530 .bearer_auth(&access_jwt) 531 .json(&json!({ "handle": "thishandleiswaytoolongforservicedomain" })) 532 .send() 533 .await 534 .expect("Failed to send request"); 535 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 536 let body: Value = res.json().await.expect("Response was not valid JSON"); 537 assert_eq!(body["error"], "InvalidHandle"); 538 assert!(body["message"].as_str().unwrap().contains("long")); 539}