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": "password" 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"].as_str().expect("No handle in response").to_string(); 30 let params = [("handle", full_handle.as_str())]; 31 let res = client 32 .get(format!( 33 "{}/xrpc/com.atproto.identity.resolveHandle", 34 base_url().await 35 )) 36 .query(&params) 37 .send() 38 .await 39 .expect("Failed to send request"); 40 assert_eq!(res.status(), StatusCode::OK); 41 let body: Value = res.json().await.expect("Response was not valid JSON"); 42 assert_eq!(body["did"], did); 43} 44 45#[tokio::test] 46async fn test_resolve_handle_not_found() { 47 let client = client(); 48 let params = [("handle", "nonexistent_handle_12345")]; 49 let res = client 50 .get(format!( 51 "{}/xrpc/com.atproto.identity.resolveHandle", 52 base_url().await 53 )) 54 .query(&params) 55 .send() 56 .await 57 .expect("Failed to send request"); 58 assert_eq!(res.status(), StatusCode::NOT_FOUND); 59 let body: Value = res.json().await.expect("Response was not valid JSON"); 60 assert_eq!(body["error"], "HandleNotFound"); 61} 62 63#[tokio::test] 64async fn test_resolve_handle_missing_param() { 65 let client = client(); 66 let res = client 67 .get(format!( 68 "{}/xrpc/com.atproto.identity.resolveHandle", 69 base_url().await 70 )) 71 .send() 72 .await 73 .expect("Failed to send request"); 74 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 75} 76 77#[tokio::test] 78async fn test_well_known_did() { 79 let client = client(); 80 let res = client 81 .get(format!("{}/.well-known/did.json", base_url().await)) 82 .send() 83 .await 84 .expect("Failed to send request"); 85 assert_eq!(res.status(), StatusCode::OK); 86 let body: Value = res.json().await.expect("Response was not valid JSON"); 87 assert!(body["id"].as_str().unwrap().starts_with("did:web:")); 88 assert_eq!(body["service"][0]["type"], "AtprotoPersonalDataServer"); 89} 90 91#[tokio::test] 92async fn test_create_did_web_account_and_resolve() { 93 let client = client(); 94 let mock_server = MockServer::start().await; 95 let mock_uri = mock_server.uri(); 96 let mock_addr = mock_uri.trim_start_matches("http://"); 97 let did = format!("did:web:{}", mock_addr.replace(":", "%3A")); 98 let handle = format!("webuser_{}", uuid::Uuid::new_v4()); 99 let pds_endpoint = base_url().await.replace("http://", "https://"); 100 let did_doc = json!({ 101 "@context": ["https://www.w3.org/ns/did/v1"], 102 "id": did, 103 "service": [{ 104 "id": "#atproto_pds", 105 "type": "AtprotoPersonalDataServer", 106 "serviceEndpoint": pds_endpoint 107 }] 108 }); 109 Mock::given(method("GET")) 110 .and(path("/.well-known/did.json")) 111 .respond_with(ResponseTemplate::new(200).set_body_json(did_doc)) 112 .mount(&mock_server) 113 .await; 114 let payload = json!({ 115 "handle": handle, 116 "email": format!("{}@example.com", handle), 117 "password": "password", 118 "did": did 119 }); 120 let res = client 121 .post(format!( 122 "{}/xrpc/com.atproto.server.createAccount", 123 base_url().await 124 )) 125 .json(&payload) 126 .send() 127 .await 128 .expect("Failed to send request"); 129 if res.status() != StatusCode::OK { 130 let status = res.status(); 131 let body: Value = res 132 .json() 133 .await 134 .unwrap_or(json!({"error": "could not parse body"})); 135 panic!("createAccount failed with status {}: {:?}", status, body); 136 } 137 let body: Value = res 138 .json() 139 .await 140 .expect("createAccount response was not JSON"); 141 assert_eq!(body["did"], did); 142 let res = client 143 .get(format!("{}/u/{}/did.json", base_url().await, handle)) 144 .send() 145 .await 146 .expect("Failed to fetch DID doc"); 147 assert_eq!( 148 res.status(), 149 StatusCode::NOT_FOUND, 150 "External did:web should not have DID doc served by PDS (user hosts their own)" 151 ); 152} 153 154#[tokio::test] 155async fn test_create_account_duplicate_handle() { 156 let client = client(); 157 let handle = format!("dupe_{}", uuid::Uuid::new_v4()); 158 let email = format!("{}@example.com", handle); 159 let payload = json!({ 160 "handle": handle, 161 "email": email, 162 "password": "password" 163 }); 164 let res = client 165 .post(format!( 166 "{}/xrpc/com.atproto.server.createAccount", 167 base_url().await 168 )) 169 .json(&payload) 170 .send() 171 .await 172 .expect("Failed to send request"); 173 assert_eq!(res.status(), StatusCode::OK); 174 let res = client 175 .post(format!( 176 "{}/xrpc/com.atproto.server.createAccount", 177 base_url().await 178 )) 179 .json(&payload) 180 .send() 181 .await 182 .expect("Failed to send request"); 183 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 184 let body: Value = res.json().await.expect("Response was not JSON"); 185 assert_eq!(body["error"], "HandleTaken"); 186} 187 188#[tokio::test] 189async fn test_did_web_lifecycle() { 190 let client = client(); 191 let mock_server = MockServer::start().await; 192 let mock_uri = mock_server.uri(); 193 let mock_addr = mock_uri.trim_start_matches("http://"); 194 let handle = format!("lifecycle_{}", uuid::Uuid::new_v4()); 195 let did = format!("did:web:{}:u:{}", mock_addr.replace(":", "%3A"), handle); 196 let email = format!("{}@test.com", handle); 197 let pds_endpoint = base_url().await.replace("http://", "https://"); 198 let did_doc = json!({ 199 "@context": ["https://www.w3.org/ns/did/v1"], 200 "id": did, 201 "service": [{ 202 "id": "#atproto_pds", 203 "type": "AtprotoPersonalDataServer", 204 "serviceEndpoint": pds_endpoint 205 }] 206 }); 207 Mock::given(method("GET")) 208 .and(path(format!("/u/{}/did.json", handle))) 209 .respond_with(ResponseTemplate::new(200).set_body_json(did_doc)) 210 .mount(&mock_server) 211 .await; 212 let create_payload = json!({ 213 "handle": handle, 214 "email": email, 215 "password": "password", 216 "did": did 217 }); 218 let res = client 219 .post(format!( 220 "{}/xrpc/com.atproto.server.createAccount", 221 base_url().await 222 )) 223 .json(&create_payload) 224 .send() 225 .await 226 .expect("Failed createAccount"); 227 if res.status() != StatusCode::OK { 228 let body: Value = res.json().await.unwrap(); 229 println!("createAccount failed: {:?}", body); 230 panic!("createAccount returned non-200"); 231 } 232 assert_eq!(res.status(), StatusCode::OK); 233 let create_body: Value = res.json().await.expect("Not JSON"); 234 assert_eq!(create_body["did"], did); 235 let _jwt = verify_new_account(&client, &did).await; 236 /* 237 let profile_payload = json!({ 238 "repo": did, 239 "collection": "app.bsky.actor.profile", 240 "rkey": "self", 241 "record": { 242 "$type": "app.bsky.actor.profile", 243 "displayName": "DID Web User", 244 "description": "Testing lifecycle" 245 } 246 }); 247 let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await)) 248 .bearer_auth(_jwt) 249 .json(&profile_payload) 250 .send() 251 .await 252 .expect("Failed putRecord"); 253 if res.status() != StatusCode::OK { 254 let body: Value = res.json().await.unwrap(); 255 println!("putRecord failed: {:?}", body); 256 panic!("putRecord returned non-200"); 257 } 258 assert_eq!(res.status(), StatusCode::OK); 259 let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await)) 260 .query(&[ 261 ("repo", &handle), 262 ("collection", &"app.bsky.actor.profile".to_string()), 263 ("rkey", &"self".to_string()) 264 ]) 265 .send() 266 .await 267 .expect("Failed getRecord"); 268 if res.status() != StatusCode::OK { 269 let body: Value = res.json().await.unwrap(); 270 println!("getRecord failed: {:?}", body); 271 panic!("getRecord returned non-200"); 272 } 273 let record_body: Value = res.json().await.expect("Not JSON"); 274 assert_eq!(record_body["value"]["displayName"], "DID Web User"); 275 */ 276} 277 278#[tokio::test] 279async fn test_get_recommended_did_credentials_success() { 280 let client = client(); 281 let (access_jwt, _) = create_account_and_login(&client).await; 282 let res = client 283 .get(format!( 284 "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials", 285 base_url().await 286 )) 287 .bearer_auth(&access_jwt) 288 .send() 289 .await 290 .expect("Failed to send request"); 291 assert_eq!(res.status(), StatusCode::OK); 292 let body: Value = res.json().await.expect("Response was not valid JSON"); 293 assert!(body["rotationKeys"].is_array()); 294 assert!(body["alsoKnownAs"].is_array()); 295 assert!(body["verificationMethods"].is_object()); 296 assert!(body["services"].is_object()); 297 let rotation_keys = body["rotationKeys"].as_array().unwrap(); 298 assert!(!rotation_keys.is_empty()); 299 assert!(rotation_keys[0].as_str().unwrap().starts_with("did:key:")); 300 let also_known_as = body["alsoKnownAs"].as_array().unwrap(); 301 assert!(!also_known_as.is_empty()); 302 assert!(also_known_as[0].as_str().unwrap().starts_with("at://")); 303 assert!(body["verificationMethods"]["atproto"].is_string()); 304 assert_eq!( 305 body["services"]["atprotoPds"]["type"], 306 "AtprotoPersonalDataServer" 307 ); 308 assert!(body["services"]["atprotoPds"]["endpoint"].is_string()); 309} 310 311#[tokio::test] 312async fn test_get_recommended_did_credentials_no_auth() { 313 let client = client(); 314 let res = client 315 .get(format!( 316 "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials", 317 base_url().await 318 )) 319 .send() 320 .await 321 .expect("Failed to send request"); 322 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 323 let body: Value = res.json().await.expect("Response was not valid JSON"); 324 assert_eq!(body["error"], "AuthenticationRequired"); 325}