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(¶ms)
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(¶ms)
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}