this repo has no description
1mod common;
2mod helpers;
3use common::*;
4use reqwest::StatusCode;
5use serde_json::{Value, json};
6
7#[tokio::test]
8async fn test_check_account_status_returns_correct_block_count() {
9 let client = client();
10 let base = base_url().await;
11 let (access_jwt, did) = create_account_and_login(&client).await;
12
13 let status1 = client
14 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
15 .bearer_auth(&access_jwt)
16 .send()
17 .await
18 .unwrap();
19 assert_eq!(status1.status(), StatusCode::OK);
20 let body1: Value = status1.json().await.unwrap();
21 let initial_blocks = body1["repoBlocks"].as_i64().unwrap();
22 assert!(initial_blocks >= 2, "New account should have at least 2 blocks (commit + empty MST)");
23
24 let create_res = client
25 .post(format!("{}/xrpc/com.atproto.repo.createRecord", base))
26 .bearer_auth(&access_jwt)
27 .json(&json!({
28 "repo": did,
29 "collection": "app.bsky.feed.post",
30 "record": {
31 "$type": "app.bsky.feed.post",
32 "text": "Test post for block counting",
33 "createdAt": chrono::Utc::now().to_rfc3339()
34 }
35 }))
36 .send()
37 .await
38 .unwrap();
39 assert_eq!(create_res.status(), StatusCode::OK);
40 let create_body: Value = create_res.json().await.unwrap();
41 let rkey = create_body["uri"].as_str().unwrap().split('/').last().unwrap().to_string();
42
43 let status2 = client
44 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
45 .bearer_auth(&access_jwt)
46 .send()
47 .await
48 .unwrap();
49 let body2: Value = status2.json().await.unwrap();
50 let after_create_blocks = body2["repoBlocks"].as_i64().unwrap();
51 assert!(after_create_blocks > initial_blocks, "Block count should increase after creating a record");
52
53 let delete_res = client
54 .post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base))
55 .bearer_auth(&access_jwt)
56 .json(&json!({
57 "repo": did,
58 "collection": "app.bsky.feed.post",
59 "rkey": rkey
60 }))
61 .send()
62 .await
63 .unwrap();
64 assert_eq!(delete_res.status(), StatusCode::OK);
65
66 let status3 = client
67 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
68 .bearer_auth(&access_jwt)
69 .send()
70 .await
71 .unwrap();
72 let body3: Value = status3.json().await.unwrap();
73 let after_delete_blocks = body3["repoBlocks"].as_i64().unwrap();
74 assert!(
75 after_delete_blocks >= after_create_blocks,
76 "Block count should not decrease after deleting a record (was {}, now {})",
77 after_create_blocks,
78 after_delete_blocks
79 );
80}
81
82#[tokio::test]
83async fn test_check_account_status_returns_valid_repo_rev() {
84 let client = client();
85 let base = base_url().await;
86 let (access_jwt, _) = create_account_and_login(&client).await;
87
88 let status = client
89 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
90 .bearer_auth(&access_jwt)
91 .send()
92 .await
93 .unwrap();
94 assert_eq!(status.status(), StatusCode::OK);
95 let body: Value = status.json().await.unwrap();
96
97 let repo_rev = body["repoRev"].as_str().unwrap();
98 assert!(!repo_rev.is_empty(), "repoRev should not be empty");
99 assert!(repo_rev.chars().all(|c| c.is_alphanumeric()), "repoRev should be alphanumeric TID");
100}
101
102#[tokio::test]
103async fn test_check_account_status_valid_did_is_true_for_active_account() {
104 let client = client();
105 let base = base_url().await;
106 let (access_jwt, _) = create_account_and_login(&client).await;
107
108 let status = client
109 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
110 .bearer_auth(&access_jwt)
111 .send()
112 .await
113 .unwrap();
114 assert_eq!(status.status(), StatusCode::OK);
115 let body: Value = status.json().await.unwrap();
116
117 assert_eq!(body["validDid"], true, "validDid should be true for active account with correct DID document");
118 assert_eq!(body["activated"], true, "activated should be true for active account");
119}
120
121#[tokio::test]
122async fn test_deactivate_account_with_delete_after() {
123 let client = client();
124 let base = base_url().await;
125 let (access_jwt, _) = create_account_and_login(&client).await;
126
127 let future_time = chrono::Utc::now() + chrono::Duration::hours(24);
128 let delete_after = future_time.to_rfc3339();
129
130 let deactivate = client
131 .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
132 .bearer_auth(&access_jwt)
133 .json(&json!({
134 "deleteAfter": delete_after
135 }))
136 .send()
137 .await
138 .unwrap();
139 assert_eq!(deactivate.status(), StatusCode::OK);
140
141 let status = client
142 .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
143 .bearer_auth(&access_jwt)
144 .send()
145 .await
146 .unwrap();
147 assert_eq!(status.status(), StatusCode::OK);
148 let body: Value = status.json().await.unwrap();
149 assert_eq!(body["activated"], false, "Account should be deactivated");
150}
151
152#[tokio::test]
153async fn test_create_account_returns_did_doc() {
154 let client = client();
155 let base = base_url().await;
156
157 let handle = format!("dd{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
158 let payload = json!({
159 "handle": handle,
160 "email": format!("{}@example.com", handle),
161 "password": "Testpass123!"
162 });
163
164 let create_res = client
165 .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
166 .json(&payload)
167 .send()
168 .await
169 .unwrap();
170 assert_eq!(create_res.status(), StatusCode::OK);
171 let body: Value = create_res.json().await.unwrap();
172
173 assert!(body["accessJwt"].is_string(), "accessJwt should always be returned");
174 assert!(body["refreshJwt"].is_string(), "refreshJwt should always be returned");
175 assert!(body["did"].is_string(), "did should be returned");
176
177 if body["didDoc"].is_object() {
178 let did_doc = &body["didDoc"];
179 assert!(did_doc["id"].is_string(), "didDoc should have id field");
180 }
181}
182
183#[tokio::test]
184async fn test_create_account_always_returns_tokens() {
185 let client = client();
186 let base = base_url().await;
187
188 let handle = format!("tt{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
189 let payload = json!({
190 "handle": handle,
191 "email": format!("{}@example.com", handle),
192 "password": "Testpass123!"
193 });
194
195 let create_res = client
196 .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
197 .json(&payload)
198 .send()
199 .await
200 .unwrap();
201 assert_eq!(create_res.status(), StatusCode::OK);
202 let body: Value = create_res.json().await.unwrap();
203
204 let access_jwt = body["accessJwt"].as_str().expect("accessJwt should be present");
205 let refresh_jwt = body["refreshJwt"].as_str().expect("refreshJwt should be present");
206
207 assert!(!access_jwt.is_empty(), "accessJwt should not be empty");
208 assert!(!refresh_jwt.is_empty(), "refreshJwt should not be empty");
209
210 let parts: Vec<&str> = access_jwt.split('.').collect();
211 assert_eq!(parts.len(), 3, "accessJwt should be a valid JWT with 3 parts");
212}
213
214#[tokio::test]
215async fn test_describe_server_has_links_and_contact() {
216 let client = client();
217 let base = base_url().await;
218
219 let describe = client
220 .get(format!("{}/xrpc/com.atproto.server.describeServer", base))
221 .send()
222 .await
223 .unwrap();
224 assert_eq!(describe.status(), StatusCode::OK);
225 let body: Value = describe.json().await.unwrap();
226
227 assert!(body.get("links").is_some(), "describeServer should include links object");
228 assert!(body.get("contact").is_some(), "describeServer should include contact object");
229
230 let links = &body["links"];
231 assert!(links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(),
232 "links should have privacyPolicy field (can be null)");
233 assert!(links.get("termsOfService").is_some() || links["termsOfService"].is_null(),
234 "links should have termsOfService field (can be null)");
235
236 let contact = &body["contact"];
237 assert!(contact.get("email").is_some() || contact["email"].is_null(),
238 "contact should have email field (can be null)");
239}
240
241#[tokio::test]
242async fn test_delete_account_password_max_length() {
243 let client = client();
244 let base = base_url().await;
245
246 let handle = format!("pl{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
247 let payload = json!({
248 "handle": handle,
249 "email": format!("{}@example.com", handle),
250 "password": "Testpass123!"
251 });
252
253 let create_res = client
254 .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
255 .json(&payload)
256 .send()
257 .await
258 .unwrap();
259 assert_eq!(create_res.status(), StatusCode::OK);
260 let body: Value = create_res.json().await.unwrap();
261 let did = body["did"].as_str().unwrap();
262
263 let too_long_password = "a".repeat(600);
264 let delete_res = client
265 .post(format!("{}/xrpc/com.atproto.server.deleteAccount", base))
266 .json(&json!({
267 "did": did,
268 "password": too_long_password,
269 "token": "fake-token"
270 }))
271 .send()
272 .await
273 .unwrap();
274
275 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST);
276 let error_body: Value = delete_res.json().await.unwrap();
277 assert!(error_body["message"].as_str().unwrap().contains("password length")
278 || error_body["error"].as_str().unwrap() == "InvalidRequest");
279}