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