this repo has no description
1mod common;
2mod helpers;
3
4use common::*;
5use helpers::*;
6
7use reqwest::StatusCode;
8use serde_json::{Value, json};
9use std::time::Duration;
10
11#[tokio::test]
12async fn test_social_flow_lifecycle() {
13 let client = client();
14
15 let (alice_did, alice_jwt) = setup_new_user("alice-social").await;
16 let (bob_did, bob_jwt) = setup_new_user("bob-social").await;
17
18 let (post1_uri, _) = create_post(&client, &alice_did, &alice_jwt, "Alice's first post!").await;
19
20 create_follow(&client, &bob_did, &bob_jwt, &alice_did).await;
21
22 tokio::time::sleep(Duration::from_secs(1)).await;
23
24 let timeline_res_1 = client
25 .get(format!(
26 "{}/xrpc/app.bsky.feed.getTimeline",
27 base_url().await
28 ))
29 .bearer_auth(&bob_jwt)
30 .send()
31 .await
32 .expect("Failed to get timeline (1)");
33
34 assert_eq!(
35 timeline_res_1.status(),
36 reqwest::StatusCode::OK,
37 "Failed to get timeline (1)"
38 );
39 let timeline_body_1: Value = timeline_res_1.json().await.expect("Timeline (1) not JSON");
40 let feed_1 = timeline_body_1["feed"].as_array().unwrap();
41 assert_eq!(feed_1.len(), 1, "Timeline should have 1 post");
42 assert_eq!(
43 feed_1[0]["post"]["uri"], post1_uri,
44 "Post URI mismatch in timeline (1)"
45 );
46
47 let (post2_uri, _) = create_post(
48 &client,
49 &alice_did,
50 &alice_jwt,
51 "Alice's second post, so exciting!",
52 )
53 .await;
54
55 tokio::time::sleep(Duration::from_secs(1)).await;
56
57 let timeline_res_2 = client
58 .get(format!(
59 "{}/xrpc/app.bsky.feed.getTimeline",
60 base_url().await
61 ))
62 .bearer_auth(&bob_jwt)
63 .send()
64 .await
65 .expect("Failed to get timeline (2)");
66
67 assert_eq!(
68 timeline_res_2.status(),
69 reqwest::StatusCode::OK,
70 "Failed to get timeline (2)"
71 );
72 let timeline_body_2: Value = timeline_res_2.json().await.expect("Timeline (2) not JSON");
73 let feed_2 = timeline_body_2["feed"].as_array().unwrap();
74 assert_eq!(feed_2.len(), 2, "Timeline should have 2 posts");
75 assert_eq!(
76 feed_2[0]["post"]["uri"], post2_uri,
77 "Post 2 should be first"
78 );
79 assert_eq!(
80 feed_2[1]["post"]["uri"], post1_uri,
81 "Post 1 should be second"
82 );
83
84 let delete_payload = json!({
85 "repo": alice_did,
86 "collection": "app.bsky.feed.post",
87 "rkey": post1_uri.split('/').last().unwrap()
88 });
89 let delete_res = client
90 .post(format!(
91 "{}/xrpc/com.atproto.repo.deleteRecord",
92 base_url().await
93 ))
94 .bearer_auth(&alice_jwt)
95 .json(&delete_payload)
96 .send()
97 .await
98 .expect("Failed to send delete request");
99 assert_eq!(
100 delete_res.status(),
101 reqwest::StatusCode::OK,
102 "Failed to delete record"
103 );
104
105 tokio::time::sleep(Duration::from_secs(1)).await;
106
107 let timeline_res_3 = client
108 .get(format!(
109 "{}/xrpc/app.bsky.feed.getTimeline",
110 base_url().await
111 ))
112 .bearer_auth(&bob_jwt)
113 .send()
114 .await
115 .expect("Failed to get timeline (3)");
116
117 assert_eq!(
118 timeline_res_3.status(),
119 reqwest::StatusCode::OK,
120 "Failed to get timeline (3)"
121 );
122 let timeline_body_3: Value = timeline_res_3.json().await.expect("Timeline (3) not JSON");
123 let feed_3 = timeline_body_3["feed"].as_array().unwrap();
124 assert_eq!(feed_3.len(), 1, "Timeline should have 1 post after delete");
125 assert_eq!(
126 feed_3[0]["post"]["uri"], post2_uri,
127 "Only post 2 should remain"
128 );
129}
130
131#[tokio::test]
132async fn test_like_lifecycle() {
133 let client = client();
134
135 let (alice_did, alice_jwt) = setup_new_user("alice-like").await;
136 let (bob_did, bob_jwt) = setup_new_user("bob-like").await;
137
138 let (post_uri, post_cid) = create_post(&client, &alice_did, &alice_jwt, "Like this post!").await;
139
140 let (like_uri, _) = create_like(&client, &bob_did, &bob_jwt, &post_uri, &post_cid).await;
141
142 let like_rkey = like_uri.split('/').last().unwrap();
143 let get_like_res = client
144 .get(format!(
145 "{}/xrpc/com.atproto.repo.getRecord",
146 base_url().await
147 ))
148 .query(&[
149 ("repo", bob_did.as_str()),
150 ("collection", "app.bsky.feed.like"),
151 ("rkey", like_rkey),
152 ])
153 .send()
154 .await
155 .expect("Failed to get like");
156
157 assert_eq!(get_like_res.status(), StatusCode::OK);
158 let like_body: Value = get_like_res.json().await.unwrap();
159 assert_eq!(like_body["value"]["subject"]["uri"], post_uri);
160
161 let delete_payload = json!({
162 "repo": bob_did,
163 "collection": "app.bsky.feed.like",
164 "rkey": like_rkey
165 });
166
167 let delete_res = client
168 .post(format!(
169 "{}/xrpc/com.atproto.repo.deleteRecord",
170 base_url().await
171 ))
172 .bearer_auth(&bob_jwt)
173 .json(&delete_payload)
174 .send()
175 .await
176 .expect("Failed to delete like");
177
178 assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete like");
179
180 let get_deleted_res = client
181 .get(format!(
182 "{}/xrpc/com.atproto.repo.getRecord",
183 base_url().await
184 ))
185 .query(&[
186 ("repo", bob_did.as_str()),
187 ("collection", "app.bsky.feed.like"),
188 ("rkey", like_rkey),
189 ])
190 .send()
191 .await
192 .expect("Failed to check deleted like");
193
194 assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Like should be deleted");
195}
196
197#[tokio::test]
198async fn test_repost_lifecycle() {
199 let client = client();
200
201 let (alice_did, alice_jwt) = setup_new_user("alice-repost").await;
202 let (bob_did, bob_jwt) = setup_new_user("bob-repost").await;
203
204 let (post_uri, post_cid) = create_post(&client, &alice_did, &alice_jwt, "Repost this!").await;
205
206 let (repost_uri, _) = create_repost(&client, &bob_did, &bob_jwt, &post_uri, &post_cid).await;
207
208 let repost_rkey = repost_uri.split('/').last().unwrap();
209 let get_repost_res = client
210 .get(format!(
211 "{}/xrpc/com.atproto.repo.getRecord",
212 base_url().await
213 ))
214 .query(&[
215 ("repo", bob_did.as_str()),
216 ("collection", "app.bsky.feed.repost"),
217 ("rkey", repost_rkey),
218 ])
219 .send()
220 .await
221 .expect("Failed to get repost");
222
223 assert_eq!(get_repost_res.status(), StatusCode::OK);
224 let repost_body: Value = get_repost_res.json().await.unwrap();
225 assert_eq!(repost_body["value"]["subject"]["uri"], post_uri);
226
227 let delete_payload = json!({
228 "repo": bob_did,
229 "collection": "app.bsky.feed.repost",
230 "rkey": repost_rkey
231 });
232
233 let delete_res = client
234 .post(format!(
235 "{}/xrpc/com.atproto.repo.deleteRecord",
236 base_url().await
237 ))
238 .bearer_auth(&bob_jwt)
239 .json(&delete_payload)
240 .send()
241 .await
242 .expect("Failed to delete repost");
243
244 assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete repost");
245}
246
247#[tokio::test]
248async fn test_unfollow_lifecycle() {
249 let client = client();
250
251 let (alice_did, _alice_jwt) = setup_new_user("alice-unfollow").await;
252 let (bob_did, bob_jwt) = setup_new_user("bob-unfollow").await;
253
254 let (follow_uri, _) = create_follow(&client, &bob_did, &bob_jwt, &alice_did).await;
255
256 let follow_rkey = follow_uri.split('/').last().unwrap();
257 let get_follow_res = client
258 .get(format!(
259 "{}/xrpc/com.atproto.repo.getRecord",
260 base_url().await
261 ))
262 .query(&[
263 ("repo", bob_did.as_str()),
264 ("collection", "app.bsky.graph.follow"),
265 ("rkey", follow_rkey),
266 ])
267 .send()
268 .await
269 .expect("Failed to get follow");
270
271 assert_eq!(get_follow_res.status(), StatusCode::OK);
272
273 let unfollow_payload = json!({
274 "repo": bob_did,
275 "collection": "app.bsky.graph.follow",
276 "rkey": follow_rkey
277 });
278
279 let unfollow_res = client
280 .post(format!(
281 "{}/xrpc/com.atproto.repo.deleteRecord",
282 base_url().await
283 ))
284 .bearer_auth(&bob_jwt)
285 .json(&unfollow_payload)
286 .send()
287 .await
288 .expect("Failed to unfollow");
289
290 assert_eq!(unfollow_res.status(), StatusCode::OK, "Failed to unfollow");
291
292 let get_deleted_res = client
293 .get(format!(
294 "{}/xrpc/com.atproto.repo.getRecord",
295 base_url().await
296 ))
297 .query(&[
298 ("repo", bob_did.as_str()),
299 ("collection", "app.bsky.graph.follow"),
300 ("rkey", follow_rkey),
301 ])
302 .send()
303 .await
304 .expect("Failed to check deleted follow");
305
306 assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Follow should be deleted");
307}
308
309#[tokio::test]
310async fn test_timeline_after_unfollow() {
311 let client = client();
312
313 let (alice_did, alice_jwt) = setup_new_user("alice-tl-unfollow").await;
314 let (bob_did, bob_jwt) = setup_new_user("bob-tl-unfollow").await;
315
316 let (follow_uri, _) = create_follow(&client, &bob_did, &bob_jwt, &alice_did).await;
317
318 create_post(&client, &alice_did, &alice_jwt, "Post while following").await;
319
320 tokio::time::sleep(Duration::from_secs(1)).await;
321
322 let timeline_res = client
323 .get(format!(
324 "{}/xrpc/app.bsky.feed.getTimeline",
325 base_url().await
326 ))
327 .bearer_auth(&bob_jwt)
328 .send()
329 .await
330 .expect("Failed to get timeline");
331
332 assert_eq!(timeline_res.status(), StatusCode::OK);
333 let timeline_body: Value = timeline_res.json().await.unwrap();
334 let feed = timeline_body["feed"].as_array().unwrap();
335 assert_eq!(feed.len(), 1, "Should see 1 post from Alice");
336
337 let follow_rkey = follow_uri.split('/').last().unwrap();
338 let unfollow_payload = json!({
339 "repo": bob_did,
340 "collection": "app.bsky.graph.follow",
341 "rkey": follow_rkey
342 });
343 client
344 .post(format!(
345 "{}/xrpc/com.atproto.repo.deleteRecord",
346 base_url().await
347 ))
348 .bearer_auth(&bob_jwt)
349 .json(&unfollow_payload)
350 .send()
351 .await
352 .expect("Failed to unfollow");
353
354 tokio::time::sleep(Duration::from_secs(1)).await;
355
356 let timeline_after_res = client
357 .get(format!(
358 "{}/xrpc/app.bsky.feed.getTimeline",
359 base_url().await
360 ))
361 .bearer_auth(&bob_jwt)
362 .send()
363 .await
364 .expect("Failed to get timeline after unfollow");
365
366 assert_eq!(timeline_after_res.status(), StatusCode::OK);
367 let timeline_after: Value = timeline_after_res.json().await.unwrap();
368 let feed_after = timeline_after["feed"].as_array().unwrap();
369 assert_eq!(feed_after.len(), 0, "Should see 0 posts after unfollowing");
370}
371
372#[tokio::test]
373async fn test_mutual_follow_lifecycle() {
374 let client = client();
375
376 let (alice_did, alice_jwt) = setup_new_user("alice-mutual").await;
377 let (bob_did, bob_jwt) = setup_new_user("bob-mutual").await;
378
379 create_follow(&client, &alice_did, &alice_jwt, &bob_did).await;
380 create_follow(&client, &bob_did, &bob_jwt, &alice_did).await;
381
382 create_post(&client, &alice_did, &alice_jwt, "Alice's post for mutual").await;
383 create_post(&client, &bob_did, &bob_jwt, "Bob's post for mutual").await;
384
385 tokio::time::sleep(Duration::from_secs(1)).await;
386
387 let alice_timeline_res = client
388 .get(format!(
389 "{}/xrpc/app.bsky.feed.getTimeline",
390 base_url().await
391 ))
392 .bearer_auth(&alice_jwt)
393 .send()
394 .await
395 .expect("Failed to get Alice's timeline");
396
397 assert_eq!(alice_timeline_res.status(), StatusCode::OK);
398 let alice_tl: Value = alice_timeline_res.json().await.unwrap();
399 let alice_feed = alice_tl["feed"].as_array().unwrap();
400 assert_eq!(alice_feed.len(), 1, "Alice should see Bob's 1 post");
401
402 let bob_timeline_res = client
403 .get(format!(
404 "{}/xrpc/app.bsky.feed.getTimeline",
405 base_url().await
406 ))
407 .bearer_auth(&bob_jwt)
408 .send()
409 .await
410 .expect("Failed to get Bob's timeline");
411
412 assert_eq!(bob_timeline_res.status(), StatusCode::OK);
413 let bob_tl: Value = bob_timeline_res.json().await.unwrap();
414 let bob_feed = bob_tl["feed"].as_array().unwrap();
415 assert_eq!(bob_feed.len(), 1, "Bob should see Alice's 1 post");
416}