this repo has no description
1mod common;
2use common::*;
3
4use chrono::Utc;
5use reqwest::StatusCode;
6use serde_json::{Value, json};
7
8#[tokio::test]
9async fn test_apply_writes_create() {
10 let client = client();
11 let (token, did) = create_account_and_login(&client).await;
12 let now = Utc::now().to_rfc3339();
13
14 let payload = json!({
15 "repo": did,
16 "writes": [
17 {
18 "$type": "com.atproto.repo.applyWrites#create",
19 "collection": "app.bsky.feed.post",
20 "value": {
21 "$type": "app.bsky.feed.post",
22 "text": "Batch created post 1",
23 "createdAt": now
24 }
25 },
26 {
27 "$type": "com.atproto.repo.applyWrites#create",
28 "collection": "app.bsky.feed.post",
29 "value": {
30 "$type": "app.bsky.feed.post",
31 "text": "Batch created post 2",
32 "createdAt": now
33 }
34 }
35 ]
36 });
37
38 let res = client
39 .post(format!(
40 "{}/xrpc/com.atproto.repo.applyWrites",
41 base_url().await
42 ))
43 .bearer_auth(&token)
44 .json(&payload)
45 .send()
46 .await
47 .expect("Failed to send request");
48
49 assert_eq!(res.status(), StatusCode::OK);
50 let body: Value = res.json().await.expect("Response was not valid JSON");
51 assert!(body["commit"]["cid"].is_string());
52 assert!(body["results"].is_array());
53 let results = body["results"].as_array().unwrap();
54 assert_eq!(results.len(), 2);
55 assert!(results[0]["uri"].is_string());
56 assert!(results[0]["cid"].is_string());
57}
58
59#[tokio::test]
60async fn test_apply_writes_update() {
61 let client = client();
62 let (token, did) = create_account_and_login(&client).await;
63 let now = Utc::now().to_rfc3339();
64 let rkey = format!("batch_update_{}", Utc::now().timestamp_millis());
65
66 let create_payload = json!({
67 "repo": did,
68 "collection": "app.bsky.feed.post",
69 "rkey": rkey,
70 "record": {
71 "$type": "app.bsky.feed.post",
72 "text": "Original post",
73 "createdAt": now
74 }
75 });
76 let res = client
77 .post(format!(
78 "{}/xrpc/com.atproto.repo.putRecord",
79 base_url().await
80 ))
81 .bearer_auth(&token)
82 .json(&create_payload)
83 .send()
84 .await
85 .expect("Failed to create");
86 assert_eq!(res.status(), StatusCode::OK);
87
88 let update_payload = json!({
89 "repo": did,
90 "writes": [
91 {
92 "$type": "com.atproto.repo.applyWrites#update",
93 "collection": "app.bsky.feed.post",
94 "rkey": rkey,
95 "value": {
96 "$type": "app.bsky.feed.post",
97 "text": "Updated post via applyWrites",
98 "createdAt": now
99 }
100 }
101 ]
102 });
103
104 let res = client
105 .post(format!(
106 "{}/xrpc/com.atproto.repo.applyWrites",
107 base_url().await
108 ))
109 .bearer_auth(&token)
110 .json(&update_payload)
111 .send()
112 .await
113 .expect("Failed to send request");
114
115 assert_eq!(res.status(), StatusCode::OK);
116 let body: Value = res.json().await.expect("Response was not valid JSON");
117 let results = body["results"].as_array().unwrap();
118 assert_eq!(results.len(), 1);
119 assert!(results[0]["uri"].is_string());
120}
121
122#[tokio::test]
123async fn test_apply_writes_delete() {
124 let client = client();
125 let (token, did) = create_account_and_login(&client).await;
126 let now = Utc::now().to_rfc3339();
127 let rkey = format!("batch_delete_{}", Utc::now().timestamp_millis());
128
129 let create_payload = json!({
130 "repo": did,
131 "collection": "app.bsky.feed.post",
132 "rkey": rkey,
133 "record": {
134 "$type": "app.bsky.feed.post",
135 "text": "Post to delete",
136 "createdAt": now
137 }
138 });
139 let res = client
140 .post(format!(
141 "{}/xrpc/com.atproto.repo.putRecord",
142 base_url().await
143 ))
144 .bearer_auth(&token)
145 .json(&create_payload)
146 .send()
147 .await
148 .expect("Failed to create");
149 assert_eq!(res.status(), StatusCode::OK);
150
151 let delete_payload = json!({
152 "repo": did,
153 "writes": [
154 {
155 "$type": "com.atproto.repo.applyWrites#delete",
156 "collection": "app.bsky.feed.post",
157 "rkey": rkey
158 }
159 ]
160 });
161
162 let res = client
163 .post(format!(
164 "{}/xrpc/com.atproto.repo.applyWrites",
165 base_url().await
166 ))
167 .bearer_auth(&token)
168 .json(&delete_payload)
169 .send()
170 .await
171 .expect("Failed to send request");
172
173 assert_eq!(res.status(), StatusCode::OK);
174
175 let get_res = client
176 .get(format!(
177 "{}/xrpc/com.atproto.repo.getRecord",
178 base_url().await
179 ))
180 .query(&[
181 ("repo", did.as_str()),
182 ("collection", "app.bsky.feed.post"),
183 ("rkey", rkey.as_str()),
184 ])
185 .send()
186 .await
187 .expect("Failed to verify");
188 assert_eq!(get_res.status(), StatusCode::NOT_FOUND);
189}
190
191#[tokio::test]
192async fn test_apply_writes_mixed_operations() {
193 let client = client();
194 let (token, did) = create_account_and_login(&client).await;
195 let now = Utc::now().to_rfc3339();
196 let rkey_to_delete = format!("mixed_del_{}", Utc::now().timestamp_millis());
197 let rkey_to_update = format!("mixed_upd_{}", Utc::now().timestamp_millis());
198
199 let setup_payload = json!({
200 "repo": did,
201 "writes": [
202 {
203 "$type": "com.atproto.repo.applyWrites#create",
204 "collection": "app.bsky.feed.post",
205 "rkey": rkey_to_delete,
206 "value": {
207 "$type": "app.bsky.feed.post",
208 "text": "To be deleted",
209 "createdAt": now
210 }
211 },
212 {
213 "$type": "com.atproto.repo.applyWrites#create",
214 "collection": "app.bsky.feed.post",
215 "rkey": rkey_to_update,
216 "value": {
217 "$type": "app.bsky.feed.post",
218 "text": "To be updated",
219 "createdAt": now
220 }
221 }
222 ]
223 });
224 let res = client
225 .post(format!(
226 "{}/xrpc/com.atproto.repo.applyWrites",
227 base_url().await
228 ))
229 .bearer_auth(&token)
230 .json(&setup_payload)
231 .send()
232 .await
233 .expect("Failed to setup");
234 assert_eq!(res.status(), StatusCode::OK);
235
236 let mixed_payload = json!({
237 "repo": did,
238 "writes": [
239 {
240 "$type": "com.atproto.repo.applyWrites#create",
241 "collection": "app.bsky.feed.post",
242 "value": {
243 "$type": "app.bsky.feed.post",
244 "text": "New post",
245 "createdAt": now
246 }
247 },
248 {
249 "$type": "com.atproto.repo.applyWrites#update",
250 "collection": "app.bsky.feed.post",
251 "rkey": rkey_to_update,
252 "value": {
253 "$type": "app.bsky.feed.post",
254 "text": "Updated text",
255 "createdAt": now
256 }
257 },
258 {
259 "$type": "com.atproto.repo.applyWrites#delete",
260 "collection": "app.bsky.feed.post",
261 "rkey": rkey_to_delete
262 }
263 ]
264 });
265
266 let res = client
267 .post(format!(
268 "{}/xrpc/com.atproto.repo.applyWrites",
269 base_url().await
270 ))
271 .bearer_auth(&token)
272 .json(&mixed_payload)
273 .send()
274 .await
275 .expect("Failed to send request");
276
277 assert_eq!(res.status(), StatusCode::OK);
278 let body: Value = res.json().await.expect("Response was not valid JSON");
279 let results = body["results"].as_array().unwrap();
280 assert_eq!(results.len(), 3);
281}
282
283#[tokio::test]
284async fn test_apply_writes_no_auth() {
285 let client = client();
286
287 let payload = json!({
288 "repo": "did:plc:test",
289 "writes": [
290 {
291 "$type": "com.atproto.repo.applyWrites#create",
292 "collection": "app.bsky.feed.post",
293 "value": {
294 "$type": "app.bsky.feed.post",
295 "text": "Test",
296 "createdAt": "2025-01-01T00:00:00Z"
297 }
298 }
299 ]
300 });
301
302 let res = client
303 .post(format!(
304 "{}/xrpc/com.atproto.repo.applyWrites",
305 base_url().await
306 ))
307 .json(&payload)
308 .send()
309 .await
310 .expect("Failed to send request");
311
312 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
313}
314
315#[tokio::test]
316async fn test_apply_writes_empty_writes() {
317 let client = client();
318 let (token, did) = create_account_and_login(&client).await;
319
320 let payload = json!({
321 "repo": did,
322 "writes": []
323 });
324
325 let res = client
326 .post(format!(
327 "{}/xrpc/com.atproto.repo.applyWrites",
328 base_url().await
329 ))
330 .bearer_auth(&token)
331 .json(&payload)
332 .send()
333 .await
334 .expect("Failed to send request");
335
336 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
337}