forked from
lewis.moe/bspds-sandbox
PDS software with bells & whistles you didn’t even know you needed. will move this to its own account when ready.
1mod common;
2mod helpers;
3use common::*;
4use helpers::verify_new_account;
5use reqwest::StatusCode;
6use serde_json::{Value, json};
7
8#[tokio::test]
9async fn test_server_basics() {
10 let client = client();
11 let base = base_url().await;
12 let health = client.get(format!("{}/health", base)).send().await.unwrap();
13 assert_eq!(health.status(), StatusCode::OK);
14 assert_eq!(health.text().await.unwrap(), "OK");
15 let describe = client
16 .get(format!("{}/xrpc/com.atproto.server.describeServer", base))
17 .send()
18 .await
19 .unwrap();
20 assert_eq!(describe.status(), StatusCode::OK);
21 let body: Value = describe.json().await.unwrap();
22 assert!(body.get("availableUserDomains").is_some());
23}
24
25#[tokio::test]
26async fn test_account_and_session_lifecycle() {
27 let client = client();
28 let base = base_url().await;
29 let handle = format!("u{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
30 let payload = json!({ "handle": handle, "email": format!("{}@example.com", handle), "password": "Testpass123!" });
31 let create_res = client
32 .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
33 .json(&payload)
34 .send()
35 .await
36 .unwrap();
37 assert_eq!(create_res.status(), StatusCode::OK);
38 let create_body: Value = create_res.json().await.unwrap();
39 let did = create_body["did"].as_str().unwrap();
40 let _ = verify_new_account(&client, did).await;
41 let login = client
42 .post(format!("{}/xrpc/com.atproto.server.createSession", base))
43 .json(&json!({ "identifier": handle, "password": "Testpass123!" }))
44 .send()
45 .await
46 .unwrap();
47 assert_eq!(login.status(), StatusCode::OK);
48 let login_body: Value = login.json().await.unwrap();
49 let access_jwt = login_body["accessJwt"].as_str().unwrap().to_string();
50 let refresh_jwt = login_body["refreshJwt"].as_str().unwrap().to_string();
51 let refresh = client
52 .post(format!("{}/xrpc/com.atproto.server.refreshSession", base))
53 .bearer_auth(&refresh_jwt)
54 .send()
55 .await
56 .unwrap();
57 assert_eq!(refresh.status(), StatusCode::OK);
58 let refresh_body: Value = refresh.json().await.unwrap();
59 assert!(refresh_body["accessJwt"].as_str().is_some());
60 assert_ne!(refresh_body["accessJwt"].as_str().unwrap(), access_jwt);
61 assert_ne!(refresh_body["refreshJwt"].as_str().unwrap(), refresh_jwt);
62 let missing_id = client
63 .post(format!("{}/xrpc/com.atproto.server.createSession", base))
64 .json(&json!({ "password": "Testpass123!" }))
65 .send()
66 .await
67 .unwrap();
68 assert!(
69 missing_id.status() == StatusCode::BAD_REQUEST
70 || missing_id.status() == StatusCode::UNPROCESSABLE_ENTITY
71 );
72 let invalid_handle = client.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
73 .json(&json!({ "handle": "invalid!handle.com", "email": "test@example.com", "password": "Testpass123!" }))
74 .send().await.unwrap();
75 assert_eq!(invalid_handle.status(), StatusCode::BAD_REQUEST);
76 let unauth_session = client
77 .get(format!("{}/xrpc/com.atproto.server.getSession", base))
78 .bearer_auth(AUTH_TOKEN)
79 .send()
80 .await
81 .unwrap();
82 assert_eq!(unauth_session.status(), StatusCode::UNAUTHORIZED);
83 let delete_session = client
84 .post(format!("{}/xrpc/com.atproto.server.deleteSession", base))
85 .bearer_auth(AUTH_TOKEN)
86 .send()
87 .await
88 .unwrap();
89 assert_eq!(delete_session.status(), StatusCode::UNAUTHORIZED);
90}
91
92#[tokio::test]
93async fn test_service_auth() {
94 let client = client();
95 let base = base_url().await;
96 let (access_jwt, did) = create_account_and_login(&client).await;
97 let res = client
98 .get(format!("{}/xrpc/com.atproto.server.getServiceAuth", base))
99 .bearer_auth(&access_jwt)
100 .query(&[("aud", "did:web:example.com")])
101 .send()
102 .await
103 .unwrap();
104 assert_eq!(res.status(), StatusCode::OK);
105 let body: Value = res.json().await.unwrap();
106 let token = body["token"].as_str().unwrap();
107 let parts: Vec<&str> = token.split('.').collect();
108 assert_eq!(parts.len(), 3, "Token should be a valid JWT");
109 use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
110 let payload_bytes = URL_SAFE_NO_PAD.decode(parts[1]).unwrap();
111 let claims: Value = serde_json::from_slice(&payload_bytes).unwrap();
112 assert_eq!(claims["iss"], did);
113 assert_eq!(claims["sub"], did);
114 assert_eq!(claims["aud"], "did:web:example.com");
115 let lxm_res = client
116 .get(format!("{}/xrpc/com.atproto.server.getServiceAuth", base))
117 .bearer_auth(&access_jwt)
118 .query(&[
119 ("aud", "did:web:example.com"),
120 ("lxm", "com.atproto.repo.getRecord"),
121 ])
122 .send()
123 .await
124 .unwrap();
125 assert_eq!(lxm_res.status(), StatusCode::OK);
126 let lxm_body: Value = lxm_res.json().await.unwrap();
127 let lxm_token = lxm_body["token"].as_str().unwrap();
128 let lxm_parts: Vec<&str> = lxm_token.split('.').collect();
129 let lxm_payload = URL_SAFE_NO_PAD.decode(lxm_parts[1]).unwrap();
130 let lxm_claims: Value = serde_json::from_slice(&lxm_payload).unwrap();
131 assert_eq!(lxm_claims["lxm"], "com.atproto.repo.getRecord");
132 let unauth = client
133 .get(format!("{}/xrpc/com.atproto.server.getServiceAuth", base))
134 .query(&[("aud", "did:web:example.com")])
135 .send()
136 .await
137 .unwrap();
138 assert_eq!(unauth.status(), StatusCode::UNAUTHORIZED);
139 let missing_aud = client
140 .get(format!("{}/xrpc/com.atproto.server.getServiceAuth", base))
141 .bearer_auth(&access_jwt)
142 .send()
143 .await
144 .unwrap();
145 assert_eq!(missing_aud.status(), StatusCode::BAD_REQUEST);
146}
147
148#[tokio::test]
149async fn test_account_status_and_activation() {
150 let client = client();
151 let base = base_url().await;
152 let (access_jwt, _) = create_account_and_login(&client).await;
153 let status = client
154 .get(format!(
155 "{}/xrpc/com.atproto.server.checkAccountStatus",
156 base
157 ))
158 .bearer_auth(&access_jwt)
159 .send()
160 .await
161 .unwrap();
162 assert_eq!(status.status(), StatusCode::OK);
163 let body: Value = status.json().await.unwrap();
164 assert_eq!(body["activated"], true);
165 assert_eq!(body["validDid"], true);
166 assert!(body["repoCommit"].is_string());
167 assert!(body["repoRev"].is_string());
168 assert!(body["indexedRecords"].is_number());
169 let unauth_status = client
170 .get(format!(
171 "{}/xrpc/com.atproto.server.checkAccountStatus",
172 base
173 ))
174 .send()
175 .await
176 .unwrap();
177 assert_eq!(unauth_status.status(), StatusCode::UNAUTHORIZED);
178 let activate = client
179 .post(format!("{}/xrpc/com.atproto.server.activateAccount", base))
180 .bearer_auth(&access_jwt)
181 .send()
182 .await
183 .unwrap();
184 assert_eq!(activate.status(), StatusCode::OK);
185 let unauth_activate = client
186 .post(format!("{}/xrpc/com.atproto.server.activateAccount", base))
187 .send()
188 .await
189 .unwrap();
190 assert_eq!(unauth_activate.status(), StatusCode::UNAUTHORIZED);
191 let deactivate = client
192 .post(format!(
193 "{}/xrpc/com.atproto.server.deactivateAccount",
194 base
195 ))
196 .bearer_auth(&access_jwt)
197 .json(&json!({}))
198 .send()
199 .await
200 .unwrap();
201 assert_eq!(deactivate.status(), StatusCode::OK);
202}