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