use book_manager::app::App; use loco_rs::testing::prelude::*; use serial_test::serial; use super::prepare_data; #[tokio::test] #[serial] async fn can_list_books_with_pagination() { request::(|request, _ctx| async move { // First test if auth endpoint works (to verify routing is working) let auth_response = request.get("/api/auth/current").await; println!("Auth endpoint status: {}", auth_response.status_code()); // Now test books endpoint let response = request.get("/api/books").await; let status = response.status_code(); let body = response.text(); println!("Books endpoint status: {}", status); println!("Books response (first 300 chars): {}", &body[..body.len().min(300)]); // The route should return 200 with JSON, not HTML assert_eq!( status, 200, "List books request should succeed" ); // Check if it's JSON (not HTML) assert!( body.trim_start().starts_with('{'), "Response should be JSON, but got: {}", &body[..body.len().min(100)] ); // Try to parse as JSON let json: serde_json::Value = serde_json::from_str(&body) .expect("Response should be valid JSON"); // Verify response structure assert!(json.get("books").is_some(), "Response should contain 'books' field"); assert!(json.get("total").is_some(), "Response should contain 'total' field"); assert!(json.get("page").is_some(), "Response should contain 'page' field"); assert!(json.get("per_page").is_some(), "Response should contain 'per_page' field"); assert!(json.get("total_pages").is_some(), "Response should contain 'total_pages' field"); // Verify default values assert_eq!(json["page"], 1, "Default page should be 1"); assert_eq!(json["per_page"], 12, "Default per_page should be 12"); }) .await; } #[tokio::test] #[serial] async fn can_list_books_with_custom_pagination() { request::(|request, _ctx| async move { // Test custom pagination parameters let response = request.get("/api/books?page=2&per_page=5").await; assert_eq!( response.status_code(), 200, "List books with custom pagination should succeed" ); let json: serde_json::Value = response.json(); assert_eq!(json["page"], 2, "Page should be 2"); assert_eq!(json["per_page"], 5, "Per page should be 5"); }) .await; } #[tokio::test] #[serial] async fn can_get_single_book() { request::(|request, ctx| async move { // First create a test book let user = prepare_data::init_user_login(&request, &ctx).await; let book_payload = serde_json::json!({ "title": "Test Book", "author": "Test Author", "description": "A test book description", "content": "This is the content of the test book." }); let (auth_key, auth_value) = prepare_data::auth_header(&user.token); let create_response = request .post("/api/books") .add_header(auth_key, auth_value.clone()) .json(&book_payload) .await; assert_eq!( create_response.status_code(), 200, "Create book request should succeed" ); let created_book: serde_json::Value = create_response.json(); let book_id = created_book["id"].as_i64().unwrap(); // Now get the book by ID let get_response = request.get(&format!("/api/books/{}", book_id)).await; assert_eq!( get_response.status_code(), 200, "Get single book request should succeed" ); let retrieved_book: serde_json::Value = get_response.json(); assert_eq!(retrieved_book["title"], "Test Book"); assert_eq!(retrieved_book["author"], "Test Author"); assert_eq!(retrieved_book["description"], "A test book description"); }) .await; } #[tokio::test] #[serial] async fn can_create_book_with_auth() { request::(|request, ctx| async move { let user = prepare_data::init_user_login(&request, &ctx).await; let book_payload = serde_json::json!({ "title": "Rust Programming", "author": "Steve Klabnik", "description": "Learn Rust programming", "content": "Rust is a systems programming language..." }); let (auth_key, auth_value) = prepare_data::auth_header(&user.token); let response = request .post("/api/books") .add_header(auth_key, auth_value) .json(&book_payload) .await; assert_eq!( response.status_code(), 200, "Create book request should succeed with authentication" ); let json: serde_json::Value = response.json(); assert_eq!(json["title"], "Rust Programming"); assert_eq!(json["author"], "Steve Klabnik"); assert!(json.get("id").is_some(), "Created book should have an ID"); }) .await; } #[tokio::test] #[serial] async fn cannot_create_book_without_auth() { request::(|request, _ctx| async move { let book_payload = serde_json::json!({ "title": "Unauthorized Book", "author": "No Auth", "content": "This should fail" }); let response = request .post("/api/books") .json(&book_payload) .await; // Should return 401 Unauthorized or similar error assert_ne!( response.status_code(), 200, "Create book should fail without authentication" ); }) .await; }