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; 10use chrono::Utc; 11 12#[tokio::test] 13async fn test_social_flow_lifecycle() { 14 let client = client(); 15 16 let (alice_did, alice_jwt) = setup_new_user("alice-social").await; 17 let (bob_did, bob_jwt) = setup_new_user("bob-social").await; 18 19 let (post1_uri, _) = create_post(&client, &alice_did, &alice_jwt, "Alice's first post!").await; 20 21 create_follow(&client, &bob_did, &bob_jwt, &alice_did).await; 22 23 tokio::time::sleep(Duration::from_secs(1)).await; 24 25 let timeline_res_1 = client 26 .get(format!( 27 "{}/xrpc/app.bsky.feed.getTimeline", 28 base_url().await 29 )) 30 .bearer_auth(&bob_jwt) 31 .send() 32 .await 33 .expect("Failed to get timeline (1)"); 34 35 assert_eq!( 36 timeline_res_1.status(), 37 reqwest::StatusCode::OK, 38 "Failed to get timeline (1)" 39 ); 40 let timeline_body_1: Value = timeline_res_1.json().await.expect("Timeline (1) not JSON"); 41 let feed_1 = timeline_body_1["feed"].as_array().unwrap(); 42 assert_eq!(feed_1.len(), 1, "Timeline should have 1 post"); 43 assert_eq!( 44 feed_1[0]["post"]["uri"], post1_uri, 45 "Post URI mismatch in timeline (1)" 46 ); 47 48 let (post2_uri, _) = create_post( 49 &client, 50 &alice_did, 51 &alice_jwt, 52 "Alice's second post, so exciting!", 53 ) 54 .await; 55 56 tokio::time::sleep(Duration::from_secs(1)).await; 57 58 let timeline_res_2 = client 59 .get(format!( 60 "{}/xrpc/app.bsky.feed.getTimeline", 61 base_url().await 62 )) 63 .bearer_auth(&bob_jwt) 64 .send() 65 .await 66 .expect("Failed to get timeline (2)"); 67 68 assert_eq!( 69 timeline_res_2.status(), 70 reqwest::StatusCode::OK, 71 "Failed to get timeline (2)" 72 ); 73 let timeline_body_2: Value = timeline_res_2.json().await.expect("Timeline (2) not JSON"); 74 let feed_2 = timeline_body_2["feed"].as_array().unwrap(); 75 assert_eq!(feed_2.len(), 2, "Timeline should have 2 posts"); 76 assert_eq!( 77 feed_2[0]["post"]["uri"], post2_uri, 78 "Post 2 should be first" 79 ); 80 assert_eq!( 81 feed_2[1]["post"]["uri"], post1_uri, 82 "Post 1 should be second" 83 ); 84 85 let delete_payload = json!({ 86 "repo": alice_did, 87 "collection": "app.bsky.feed.post", 88 "rkey": post1_uri.split('/').last().unwrap() 89 }); 90 let delete_res = client 91 .post(format!( 92 "{}/xrpc/com.atproto.repo.deleteRecord", 93 base_url().await 94 )) 95 .bearer_auth(&alice_jwt) 96 .json(&delete_payload) 97 .send() 98 .await 99 .expect("Failed to send delete request"); 100 assert_eq!( 101 delete_res.status(), 102 reqwest::StatusCode::OK, 103 "Failed to delete record" 104 ); 105 106 tokio::time::sleep(Duration::from_secs(1)).await; 107 108 let timeline_res_3 = client 109 .get(format!( 110 "{}/xrpc/app.bsky.feed.getTimeline", 111 base_url().await 112 )) 113 .bearer_auth(&bob_jwt) 114 .send() 115 .await 116 .expect("Failed to get timeline (3)"); 117 118 assert_eq!( 119 timeline_res_3.status(), 120 reqwest::StatusCode::OK, 121 "Failed to get timeline (3)" 122 ); 123 let timeline_body_3: Value = timeline_res_3.json().await.expect("Timeline (3) not JSON"); 124 let feed_3 = timeline_body_3["feed"].as_array().unwrap(); 125 assert_eq!(feed_3.len(), 1, "Timeline should have 1 post after delete"); 126 assert_eq!( 127 feed_3[0]["post"]["uri"], post2_uri, 128 "Only post 2 should remain" 129 ); 130} 131 132#[tokio::test] 133async fn test_like_lifecycle() { 134 let client = client(); 135 136 let (alice_did, alice_jwt) = setup_new_user("alice-like").await; 137 let (bob_did, bob_jwt) = setup_new_user("bob-like").await; 138 139 let (post_uri, post_cid) = create_post(&client, &alice_did, &alice_jwt, "Like this post!").await; 140 141 let (like_uri, _) = create_like(&client, &bob_did, &bob_jwt, &post_uri, &post_cid).await; 142 143 let like_rkey = like_uri.split('/').last().unwrap(); 144 let get_like_res = client 145 .get(format!( 146 "{}/xrpc/com.atproto.repo.getRecord", 147 base_url().await 148 )) 149 .query(&[ 150 ("repo", bob_did.as_str()), 151 ("collection", "app.bsky.feed.like"), 152 ("rkey", like_rkey), 153 ]) 154 .send() 155 .await 156 .expect("Failed to get like"); 157 158 assert_eq!(get_like_res.status(), StatusCode::OK); 159 let like_body: Value = get_like_res.json().await.unwrap(); 160 assert_eq!(like_body["value"]["subject"]["uri"], post_uri); 161 162 let delete_payload = json!({ 163 "repo": bob_did, 164 "collection": "app.bsky.feed.like", 165 "rkey": like_rkey 166 }); 167 168 let delete_res = client 169 .post(format!( 170 "{}/xrpc/com.atproto.repo.deleteRecord", 171 base_url().await 172 )) 173 .bearer_auth(&bob_jwt) 174 .json(&delete_payload) 175 .send() 176 .await 177 .expect("Failed to delete like"); 178 179 assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete like"); 180 181 let get_deleted_res = client 182 .get(format!( 183 "{}/xrpc/com.atproto.repo.getRecord", 184 base_url().await 185 )) 186 .query(&[ 187 ("repo", bob_did.as_str()), 188 ("collection", "app.bsky.feed.like"), 189 ("rkey", like_rkey), 190 ]) 191 .send() 192 .await 193 .expect("Failed to check deleted like"); 194 195 assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Like should be deleted"); 196} 197 198#[tokio::test] 199async fn test_repost_lifecycle() { 200 let client = client(); 201 202 let (alice_did, alice_jwt) = setup_new_user("alice-repost").await; 203 let (bob_did, bob_jwt) = setup_new_user("bob-repost").await; 204 205 let (post_uri, post_cid) = create_post(&client, &alice_did, &alice_jwt, "Repost this!").await; 206 207 let (repost_uri, _) = create_repost(&client, &bob_did, &bob_jwt, &post_uri, &post_cid).await; 208 209 let repost_rkey = repost_uri.split('/').last().unwrap(); 210 let get_repost_res = client 211 .get(format!( 212 "{}/xrpc/com.atproto.repo.getRecord", 213 base_url().await 214 )) 215 .query(&[ 216 ("repo", bob_did.as_str()), 217 ("collection", "app.bsky.feed.repost"), 218 ("rkey", repost_rkey), 219 ]) 220 .send() 221 .await 222 .expect("Failed to get repost"); 223 224 assert_eq!(get_repost_res.status(), StatusCode::OK); 225 let repost_body: Value = get_repost_res.json().await.unwrap(); 226 assert_eq!(repost_body["value"]["subject"]["uri"], post_uri); 227 228 let delete_payload = json!({ 229 "repo": bob_did, 230 "collection": "app.bsky.feed.repost", 231 "rkey": repost_rkey 232 }); 233 234 let delete_res = client 235 .post(format!( 236 "{}/xrpc/com.atproto.repo.deleteRecord", 237 base_url().await 238 )) 239 .bearer_auth(&bob_jwt) 240 .json(&delete_payload) 241 .send() 242 .await 243 .expect("Failed to delete repost"); 244 245 assert_eq!(delete_res.status(), StatusCode::OK, "Failed to delete repost"); 246} 247 248#[tokio::test] 249async fn test_unfollow_lifecycle() { 250 let client = client(); 251 252 let (alice_did, _alice_jwt) = setup_new_user("alice-unfollow").await; 253 let (bob_did, bob_jwt) = setup_new_user("bob-unfollow").await; 254 255 let (follow_uri, _) = create_follow(&client, &bob_did, &bob_jwt, &alice_did).await; 256 257 let follow_rkey = follow_uri.split('/').last().unwrap(); 258 let get_follow_res = client 259 .get(format!( 260 "{}/xrpc/com.atproto.repo.getRecord", 261 base_url().await 262 )) 263 .query(&[ 264 ("repo", bob_did.as_str()), 265 ("collection", "app.bsky.graph.follow"), 266 ("rkey", follow_rkey), 267 ]) 268 .send() 269 .await 270 .expect("Failed to get follow"); 271 272 assert_eq!(get_follow_res.status(), StatusCode::OK); 273 274 let unfollow_payload = json!({ 275 "repo": bob_did, 276 "collection": "app.bsky.graph.follow", 277 "rkey": follow_rkey 278 }); 279 280 let unfollow_res = client 281 .post(format!( 282 "{}/xrpc/com.atproto.repo.deleteRecord", 283 base_url().await 284 )) 285 .bearer_auth(&bob_jwt) 286 .json(&unfollow_payload) 287 .send() 288 .await 289 .expect("Failed to unfollow"); 290 291 assert_eq!(unfollow_res.status(), StatusCode::OK, "Failed to unfollow"); 292 293 let get_deleted_res = client 294 .get(format!( 295 "{}/xrpc/com.atproto.repo.getRecord", 296 base_url().await 297 )) 298 .query(&[ 299 ("repo", bob_did.as_str()), 300 ("collection", "app.bsky.graph.follow"), 301 ("rkey", follow_rkey), 302 ]) 303 .send() 304 .await 305 .expect("Failed to check deleted follow"); 306 307 assert_eq!(get_deleted_res.status(), StatusCode::NOT_FOUND, "Follow should be deleted"); 308} 309 310#[tokio::test] 311async fn test_timeline_after_unfollow() { 312 let client = client(); 313 314 let (alice_did, alice_jwt) = setup_new_user("alice-tl-unfollow").await; 315 let (bob_did, bob_jwt) = setup_new_user("bob-tl-unfollow").await; 316 317 let (follow_uri, _) = create_follow(&client, &bob_did, &bob_jwt, &alice_did).await; 318 319 create_post(&client, &alice_did, &alice_jwt, "Post while following").await; 320 321 tokio::time::sleep(Duration::from_secs(1)).await; 322 323 let timeline_res = client 324 .get(format!( 325 "{}/xrpc/app.bsky.feed.getTimeline", 326 base_url().await 327 )) 328 .bearer_auth(&bob_jwt) 329 .send() 330 .await 331 .expect("Failed to get timeline"); 332 333 assert_eq!(timeline_res.status(), StatusCode::OK); 334 let timeline_body: Value = timeline_res.json().await.unwrap(); 335 let feed = timeline_body["feed"].as_array().unwrap(); 336 assert_eq!(feed.len(), 1, "Should see 1 post from Alice"); 337 338 let follow_rkey = follow_uri.split('/').last().unwrap(); 339 let unfollow_payload = json!({ 340 "repo": bob_did, 341 "collection": "app.bsky.graph.follow", 342 "rkey": follow_rkey 343 }); 344 client 345 .post(format!( 346 "{}/xrpc/com.atproto.repo.deleteRecord", 347 base_url().await 348 )) 349 .bearer_auth(&bob_jwt) 350 .json(&unfollow_payload) 351 .send() 352 .await 353 .expect("Failed to unfollow"); 354 355 tokio::time::sleep(Duration::from_secs(1)).await; 356 357 let timeline_after_res = client 358 .get(format!( 359 "{}/xrpc/app.bsky.feed.getTimeline", 360 base_url().await 361 )) 362 .bearer_auth(&bob_jwt) 363 .send() 364 .await 365 .expect("Failed to get timeline after unfollow"); 366 367 assert_eq!(timeline_after_res.status(), StatusCode::OK); 368 let timeline_after: Value = timeline_after_res.json().await.unwrap(); 369 let feed_after = timeline_after["feed"].as_array().unwrap(); 370 assert_eq!(feed_after.len(), 0, "Should see 0 posts after unfollowing"); 371} 372 373#[tokio::test] 374async fn test_mutual_follow_lifecycle() { 375 let client = client(); 376 377 let (alice_did, alice_jwt) = setup_new_user("alice-mutual").await; 378 let (bob_did, bob_jwt) = setup_new_user("bob-mutual").await; 379 380 create_follow(&client, &alice_did, &alice_jwt, &bob_did).await; 381 create_follow(&client, &bob_did, &bob_jwt, &alice_did).await; 382 383 create_post(&client, &alice_did, &alice_jwt, "Alice's post for mutual").await; 384 create_post(&client, &bob_did, &bob_jwt, "Bob's post for mutual").await; 385 386 tokio::time::sleep(Duration::from_secs(1)).await; 387 388 let alice_timeline_res = client 389 .get(format!( 390 "{}/xrpc/app.bsky.feed.getTimeline", 391 base_url().await 392 )) 393 .bearer_auth(&alice_jwt) 394 .send() 395 .await 396 .expect("Failed to get Alice's timeline"); 397 398 assert_eq!(alice_timeline_res.status(), StatusCode::OK); 399 let alice_tl: Value = alice_timeline_res.json().await.unwrap(); 400 let alice_feed = alice_tl["feed"].as_array().unwrap(); 401 assert_eq!(alice_feed.len(), 1, "Alice should see Bob's 1 post"); 402 403 let bob_timeline_res = client 404 .get(format!( 405 "{}/xrpc/app.bsky.feed.getTimeline", 406 base_url().await 407 )) 408 .bearer_auth(&bob_jwt) 409 .send() 410 .await 411 .expect("Failed to get Bob's timeline"); 412 413 assert_eq!(bob_timeline_res.status(), StatusCode::OK); 414 let bob_tl: Value = bob_timeline_res.json().await.unwrap(); 415 let bob_feed = bob_tl["feed"].as_array().unwrap(); 416 assert_eq!(bob_feed.len(), 1, "Bob should see Alice's 1 post"); 417} 418 419#[tokio::test] 420async fn test_account_to_post_full_lifecycle() { 421 let client = client(); 422 let ts = Utc::now().timestamp_millis(); 423 let handle = format!("fullcycle-{}.test", ts); 424 let email = format!("fullcycle-{}@test.com", ts); 425 let password = "fullcycle-password"; 426 427 let create_account_res = client 428 .post(format!( 429 "{}/xrpc/com.atproto.server.createAccount", 430 base_url().await 431 )) 432 .json(&json!({ 433 "handle": handle, 434 "email": email, 435 "password": password 436 })) 437 .send() 438 .await 439 .expect("Failed to create account"); 440 441 assert_eq!(create_account_res.status(), StatusCode::OK); 442 let account_body: Value = create_account_res.json().await.unwrap(); 443 let did = account_body["did"].as_str().unwrap().to_string(); 444 let access_jwt = account_body["accessJwt"].as_str().unwrap().to_string(); 445 446 let get_session_res = client 447 .get(format!( 448 "{}/xrpc/com.atproto.server.getSession", 449 base_url().await 450 )) 451 .bearer_auth(&access_jwt) 452 .send() 453 .await 454 .expect("Failed to get session"); 455 456 assert_eq!(get_session_res.status(), StatusCode::OK); 457 let session_body: Value = get_session_res.json().await.unwrap(); 458 assert_eq!(session_body["did"], did); 459 assert_eq!(session_body["handle"], handle); 460 461 let profile_res = client 462 .post(format!( 463 "{}/xrpc/com.atproto.repo.putRecord", 464 base_url().await 465 )) 466 .bearer_auth(&access_jwt) 467 .json(&json!({ 468 "repo": did, 469 "collection": "app.bsky.actor.profile", 470 "rkey": "self", 471 "record": { 472 "$type": "app.bsky.actor.profile", 473 "displayName": "Full Cycle User" 474 } 475 })) 476 .send() 477 .await 478 .expect("Failed to create profile"); 479 480 assert_eq!(profile_res.status(), StatusCode::OK); 481 482 let (post_uri, post_cid) = create_post(&client, &did, &access_jwt, "My first post!").await; 483 484 let get_post_res = client 485 .get(format!( 486 "{}/xrpc/com.atproto.repo.getRecord", 487 base_url().await 488 )) 489 .query(&[ 490 ("repo", did.as_str()), 491 ("collection", "app.bsky.feed.post"), 492 ("rkey", post_uri.split('/').last().unwrap()), 493 ]) 494 .send() 495 .await 496 .expect("Failed to get post"); 497 498 assert_eq!(get_post_res.status(), StatusCode::OK); 499 500 create_like(&client, &did, &access_jwt, &post_uri, &post_cid).await; 501 502 let describe_res = client 503 .get(format!( 504 "{}/xrpc/com.atproto.repo.describeRepo", 505 base_url().await 506 )) 507 .query(&[("repo", did.as_str())]) 508 .send() 509 .await 510 .expect("Failed to describe repo"); 511 512 assert_eq!(describe_res.status(), StatusCode::OK); 513 let describe_body: Value = describe_res.json().await.unwrap(); 514 assert_eq!(describe_body["did"], did); 515 assert_eq!(describe_body["handle"], handle); 516}