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