//! Cart workflow tests — toggle, remove, count, PWYW amount update. use crate::harness::TestHarness; use serde_json::Value; /// Helper: create a seller with a published paid item. Returns (seller_user_id, project_id, item_id). /// Logs out the seller afterward. async fn setup_seller_with_item(h: &mut TestHarness, price_cents: i64) -> (String, String, String) { let setup = h.create_creator_with_item("cartseller", "digital", price_cents).await; h.publish_project_and_item(&setup.project_id, &setup.item_id).await; let seller_id = setup.user_id.to_string(); h.client.post_form("/logout", "").await; (seller_id, setup.project_id, setup.item_id) } // --------------------------------------------------------------------------- // Toggle // --------------------------------------------------------------------------- #[tokio::test] async fn toggle_cart_adds_and_removes() { let mut h = TestHarness::new().await; let (_, _, item_id) = setup_seller_with_item(&mut h, 500).await; h.signup("cartbuyer", "cartbuyer@test.com", "password123").await; h.login("cartbuyer", "password123").await; // Add to cart let resp = h.client.post_form(&format!("/api/cart/{}", item_id), "").await; assert!(resp.status.is_success(), "Toggle add failed: {} {}", resp.status, resp.text); let data: Value = resp.json(); assert_eq!(data["in_cart"], true, "First toggle should add to cart"); // Toggle again to remove let resp = h.client.post_form(&format!("/api/cart/{}", item_id), "").await; assert!(resp.status.is_success(), "Toggle remove failed: {} {}", resp.status, resp.text); let data: Value = resp.json(); assert_eq!(data["in_cart"], false, "Second toggle should remove from cart"); } #[tokio::test] async fn toggle_cart_own_item_rejected() { let mut h = TestHarness::new().await; let setup = h.create_creator_with_item("selfcart", "digital", 500).await; h.publish_project_and_item(&setup.project_id, &setup.item_id).await; // Creator tries to add own item to cart let resp = h.client.post_form(&format!("/api/cart/{}", setup.item_id), "").await; assert!( resp.status.is_client_error(), "Own item should not be added to cart: {} {}", resp.status, resp.text ); } #[tokio::test] async fn toggle_cart_unpublished_item_rejected() { let mut h = TestHarness::new().await; let setup = h.create_creator_with_item("draftcart", "digital", 500).await; // Items default to is_public=true; explicitly unpublish h.client .put_form(&format!("/api/items/{}", setup.item_id), "is_public=false") .await; h.client.post_form("/logout", "").await; h.signup("draftbuyer", "draftbuyer@test.com", "password123").await; h.login("draftbuyer", "password123").await; let resp = h.client.post_form(&format!("/api/cart/{}", setup.item_id), "").await; assert!( resp.status.is_client_error() || resp.status == 404, "Unpublished item should not be added to cart: {} {}", resp.status, resp.text ); } // --------------------------------------------------------------------------- // Count // --------------------------------------------------------------------------- #[tokio::test] async fn cart_count_empty() { let mut h = TestHarness::new().await; h.signup("emptycount", "emptycount@test.com", "password123").await; h.login("emptycount", "password123").await; let resp = h.client.get("/api/cart/count").await; assert!(resp.status.is_success(), "Cart count failed: {} {}", resp.status, resp.text); let data: Value = resp.json(); assert_eq!(data["count"], 0); } #[tokio::test] async fn cart_count_after_add() { let mut h = TestHarness::new().await; let (_, _, item_id) = setup_seller_with_item(&mut h, 500).await; h.signup("countbuyer", "countbuyer@test.com", "password123").await; h.login("countbuyer", "password123").await; // Add item h.client.post_form(&format!("/api/cart/{}", item_id), "").await; let resp = h.client.get("/api/cart/count").await; assert!(resp.status.is_success()); let data: Value = resp.json(); assert_eq!(data["count"], 1); } // --------------------------------------------------------------------------- // Remove // --------------------------------------------------------------------------- #[tokio::test] async fn remove_from_cart() { let mut h = TestHarness::new().await; let (_, _, item_id) = setup_seller_with_item(&mut h, 500).await; h.signup("rmbuyer", "rmbuyer@test.com", "password123").await; h.login("rmbuyer", "password123").await; // Add then explicitly remove h.client.post_form(&format!("/api/cart/{}", item_id), "").await; let resp = h.client.delete(&format!("/api/cart/{}", item_id)).await; assert!( resp.status.is_success() || resp.status == 204, "Remove from cart failed: {} {}", resp.status, resp.text ); // Verify count is 0 let resp = h.client.get("/api/cart/count").await; let data: Value = resp.json(); assert_eq!(data["count"], 0); } // --------------------------------------------------------------------------- // PWYW amount // --------------------------------------------------------------------------- #[tokio::test] async fn update_cart_pwyw_amount() { let mut h = TestHarness::new().await; // Create a PWYW item let setup = h.create_creator_with_item("pwywseller", "digital", 0).await; h.client .put_form( &format!("/api/items/{}", setup.item_id), "pwyw_enabled=on&pwyw_min_cents=500", ) .await; h.publish_project_and_item(&setup.project_id, &setup.item_id).await; h.client.post_form("/logout", "").await; h.signup("pwywbuyer", "pwywbuyer@test.com", "password123").await; h.login("pwywbuyer", "password123").await; // Add to cart h.client.post_form(&format!("/api/cart/{}", setup.item_id), "").await; // Update PWYW amount let resp = h .client .put_json( &format!("/api/cart/{}", setup.item_id), r#"{"amount_cents": 1000}"#, ) .await; assert!( resp.status.is_success(), "PWYW amount update failed: {} {}", resp.status, resp.text ); } #[tokio::test] async fn update_cart_pwyw_below_minimum_rejected() { let mut h = TestHarness::new().await; let setup = h.create_creator_with_item("pwywmin", "digital", 0).await; h.client .put_form( &format!("/api/items/{}", setup.item_id), "pwyw_enabled=on&pwyw_min_cents=500", ) .await; h.publish_project_and_item(&setup.project_id, &setup.item_id).await; h.client.post_form("/logout", "").await; h.signup("lowbuyer", "lowbuyer@test.com", "password123").await; h.login("lowbuyer", "password123").await; h.client.post_form(&format!("/api/cart/{}", setup.item_id), "").await; // Try amount below minimum let resp = h .client .put_json( &format!("/api/cart/{}", setup.item_id), r#"{"amount_cents": 100}"#, ) .await; assert!( resp.status.is_client_error(), "Below-minimum PWYW should be rejected: {} {}", resp.status, resp.text ); } #[tokio::test] async fn update_cart_pwyw_above_cap_rejected() { let mut h = TestHarness::new().await; let setup = h.create_creator_with_item("pwywcap", "digital", 0).await; h.client .put_form( &format!("/api/items/{}", setup.item_id), "pwyw_enabled=on&pwyw_min_cents=0", ) .await; h.publish_project_and_item(&setup.project_id, &setup.item_id).await; h.client.post_form("/logout", "").await; h.signup("capbuyer", "capbuyer@test.com", "password123").await; h.login("capbuyer", "password123").await; h.client.post_form(&format!("/api/cart/{}", setup.item_id), "").await; // Try amount above $10,000 cap let resp = h .client .put_json( &format!("/api/cart/{}", setup.item_id), r#"{"amount_cents": 1000001}"#, ) .await; assert!( resp.status.is_client_error(), "Above-cap PWYW should be rejected: {} {}", resp.status, resp.text ); }