mod common; mod helpers; use common::*; use helpers::*; use reqwest::StatusCode; use serde_json::{Value, json}; #[tokio::test] async fn test_list_sessions_returns_current_session() { let client = client(); let (did, jwt) = setup_new_user("list-sessions").await; let res = client .get(format!("{}/xrpc/_account.listSessions", base_url().await)) .bearer_auth(&jwt) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::OK); let body: Value = res.json().await.unwrap(); let sessions = body["sessions"] .as_array() .expect("sessions should be array"); assert!(!sessions.is_empty(), "Should have at least one session"); let current = sessions .iter() .find(|s| s["isCurrent"].as_bool() == Some(true)); assert!(current.is_some(), "Should have a current session marked"); let session = current.unwrap(); assert!(session["id"].as_str().is_some(), "Session should have id"); assert!( session["createdAt"].as_str().is_some(), "Session should have createdAt" ); assert!( session["expiresAt"].as_str().is_some(), "Session should have expiresAt" ); let _ = did; } #[tokio::test] async fn test_list_sessions_multiple_sessions() { let client = client(); let ts = chrono::Utc::now().timestamp_millis(); let handle = format!("multi-list-{}.test", ts); let email = format!("multi-list-{}@test.com", ts); let password = "Testpass123!"; let create_payload = json!({ "handle": handle, "email": email, "password": password }); let create_res = client .post(format!( "{}/xrpc/com.atproto.server.createAccount", base_url().await )) .json(&create_payload) .send() .await .expect("Failed to create account"); assert_eq!(create_res.status(), StatusCode::OK); let create_body: Value = create_res.json().await.unwrap(); let did = create_body["did"].as_str().unwrap(); let jwt1 = verify_new_account(&client, did).await; let login_payload = json!({ "identifier": handle, "password": password }); let login_res = client .post(format!( "{}/xrpc/com.atproto.server.createSession", base_url().await )) .json(&login_payload) .send() .await .expect("Failed to login"); assert_eq!(login_res.status(), StatusCode::OK); let login_body: Value = login_res.json().await.unwrap(); let jwt2 = login_body["accessJwt"].as_str().unwrap(); let list_res = client .get(format!("{}/xrpc/_account.listSessions", base_url().await)) .bearer_auth(jwt2) .send() .await .expect("Failed to list sessions"); assert_eq!(list_res.status(), StatusCode::OK); let list_body: Value = list_res.json().await.unwrap(); let sessions = list_body["sessions"].as_array().unwrap(); assert!( sessions.len() >= 2, "Should have at least 2 sessions, got {}", sessions.len() ); let _ = jwt1; } #[tokio::test] async fn test_list_sessions_requires_auth() { let client = client(); let res = client .get(format!("{}/xrpc/_account.listSessions", base_url().await)) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] async fn test_revoke_session_success() { let client = client(); let ts = chrono::Utc::now().timestamp_millis(); let handle = format!("revoke-sess-{}.test", ts); let email = format!("revoke-sess-{}@test.com", ts); let password = "Testpass123!"; let create_payload = json!({ "handle": handle, "email": email, "password": password }); let create_res = client .post(format!( "{}/xrpc/com.atproto.server.createAccount", base_url().await )) .json(&create_payload) .send() .await .expect("Failed to create account"); assert_eq!(create_res.status(), StatusCode::OK); let create_body: Value = create_res.json().await.unwrap(); let did = create_body["did"].as_str().unwrap(); let jwt1 = verify_new_account(&client, did).await; let login_payload = json!({ "identifier": handle, "password": password }); let login_res = client .post(format!( "{}/xrpc/com.atproto.server.createSession", base_url().await )) .json(&login_payload) .send() .await .expect("Failed to login"); assert_eq!(login_res.status(), StatusCode::OK); let login_body: Value = login_res.json().await.unwrap(); let jwt2 = login_body["accessJwt"].as_str().unwrap(); let list_res = client .get(format!("{}/xrpc/_account.listSessions", base_url().await)) .bearer_auth(jwt2) .send() .await .expect("Failed to list sessions"); let list_body: Value = list_res.json().await.unwrap(); let sessions = list_body["sessions"].as_array().unwrap(); let other_session = sessions .iter() .find(|s| s["isCurrent"].as_bool() != Some(true)); assert!( other_session.is_some(), "Should have another session to revoke" ); let session_id = other_session.unwrap()["id"].as_str().unwrap(); let revoke_res = client .post(format!("{}/xrpc/_account.revokeSession", base_url().await)) .bearer_auth(jwt2) .json(&json!({"sessionId": session_id})) .send() .await .expect("Failed to revoke session"); assert_eq!(revoke_res.status(), StatusCode::OK); let list_after_res = client .get(format!("{}/xrpc/_account.listSessions", base_url().await)) .bearer_auth(jwt2) .send() .await .expect("Failed to list sessions after revoke"); let list_after_body: Value = list_after_res.json().await.unwrap(); let sessions_after = list_after_body["sessions"].as_array().unwrap(); let revoked_still_exists = sessions_after .iter() .any(|s| s["id"].as_str() == Some(session_id)); assert!( !revoked_still_exists, "Revoked session should not appear in list" ); let _ = jwt1; } #[tokio::test] async fn test_revoke_session_invalid_id() { let client = client(); let (_, jwt) = setup_new_user("revoke-invalid").await; let res = client .post(format!("{}/xrpc/_account.revokeSession", base_url().await)) .bearer_auth(&jwt) .json(&json!({"sessionId": "not-a-number"})) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::BAD_REQUEST); } #[tokio::test] async fn test_revoke_session_not_found() { let client = client(); let (_, jwt) = setup_new_user("revoke-notfound").await; let res = client .post(format!("{}/xrpc/_account.revokeSession", base_url().await)) .bearer_auth(&jwt) .json(&json!({"sessionId": "jwt:999999999"})) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::NOT_FOUND); } #[tokio::test] async fn test_revoke_session_requires_auth() { let client = client(); let res = client .post(format!("{}/xrpc/_account.revokeSession", base_url().await)) .json(&json!({"sessionId": "1"})) .send() .await .expect("Failed to send request"); assert_eq!(res.status(), StatusCode::UNAUTHORIZED); }