//! Tests for [`super`]. use super::*; // ── FreePricing ── #[test] fn free_is_free() { assert!(FreePricing.is_free()); } #[test] fn free_always_accessible() { assert!(FreePricing.can_access(&AccessContext::default())); } #[test] fn free_price_display() { assert_eq!(FreePricing.price_display(SettlementCurrency::Usd), "Free"); } #[test] fn free_price_cents() { assert_eq!(FreePricing.price_cents(), 0); } #[test] fn free_checkout_type() { assert_eq!(FreePricing.checkout_type(), CheckoutType::None); } #[test] fn free_validate_amount() { assert!( FreePricing .validate_amount(0, SettlementCurrency::Usd) .is_ok() ); assert!( FreePricing .validate_amount(100, SettlementCurrency::Usd) .is_ok() ); } #[test] fn free_kind() { assert_eq!(FreePricing.kind(), db::PricingKind::Free); } // ── FixedPricing ── #[test] fn fixed_not_free() { let p = FixedPricing { price_cents: 999 }; assert!(!p.is_free()); } #[test] fn fixed_access_creator() { let p = FixedPricing { price_cents: 999 }; assert!(p.can_access(&AccessContext { is_creator: true, ..Default::default() })); } #[test] fn fixed_access_purchased() { let p = FixedPricing { price_cents: 999 }; assert!(p.can_access(&AccessContext { has_purchased: true, ..Default::default() })); } #[test] fn fixed_access_subscribed() { let p = FixedPricing { price_cents: 999 }; assert!(p.can_access(&AccessContext { subscription: Some(crate::db::subscriptions::SubscriptionGate::test_witness()), ..Default::default() })); } #[test] fn fixed_access_denied() { let p = FixedPricing { price_cents: 999 }; assert!(!p.can_access(&AccessContext::default())); } #[test] fn fixed_price_display_whole() { let p = FixedPricing { price_cents: 1000 }; assert_eq!(p.price_display(SettlementCurrency::Usd), "$10"); } #[test] fn fixed_price_display_cents() { let p = FixedPricing { price_cents: 999 }; assert_eq!(p.price_display(SettlementCurrency::Usd), "$9.99"); } #[test] fn fixed_validate_amount_ok() { let p = FixedPricing { price_cents: 999 }; assert!(p.validate_amount(999, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(1500, SettlementCurrency::Usd).is_ok()); } #[test] fn fixed_validate_amount_too_low() { let p = FixedPricing { price_cents: 999 }; assert!(p.validate_amount(500, SettlementCurrency::Usd).is_err()); } #[test] fn fixed_kind() { let p = FixedPricing { price_cents: 999 }; assert_eq!(p.kind(), db::PricingKind::BuyOnce); } // ── PwywPricing ── #[test] fn pwyw_not_free() { let p = PwywPricing { min_cents: Some(0) }; assert!(!p.is_free()); } #[test] fn pwyw_not_free_even_zero_min() { let p = PwywPricing { min_cents: None }; assert!(!p.is_free()); } #[test] fn pwyw_access_creator() { let p = PwywPricing { min_cents: Some(500), }; assert!(p.can_access(&AccessContext { is_creator: true, ..Default::default() })); } #[test] fn pwyw_access_purchased() { let p = PwywPricing { min_cents: Some(500), }; assert!(p.can_access(&AccessContext { has_purchased: true, ..Default::default() })); } #[test] fn pwyw_access_denied() { let p = PwywPricing { min_cents: Some(500), }; assert!(!p.can_access(&AccessContext::default())); } #[test] fn pwyw_price_display_with_min() { let p = PwywPricing { min_cents: Some(500), }; assert_eq!(p.price_display(SettlementCurrency::Usd), "From $5"); } #[test] fn pwyw_price_display_no_min() { let p = PwywPricing { min_cents: None }; assert_eq!( p.price_display(SettlementCurrency::Usd), "Pay what you want" ); } #[test] fn pwyw_price_display_zero_min() { let p = PwywPricing { min_cents: Some(0) }; assert_eq!( p.price_display(SettlementCurrency::Usd), "Pay what you want" ); } #[test] fn pwyw_validate_amount_ok() { let p = PwywPricing { min_cents: Some(500), }; assert!(p.validate_amount(500, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(1000, SettlementCurrency::Usd).is_ok()); } #[test] fn pwyw_validate_amount_too_low() { let p = PwywPricing { min_cents: Some(500), }; assert!(p.validate_amount(400, SettlementCurrency::Usd).is_err()); } #[test] fn pwyw_validate_amount_zero_min() { let p = PwywPricing { min_cents: Some(0) }; assert!(p.validate_amount(0, SettlementCurrency::Usd).is_ok()); } #[test] fn pwyw_chargeable_minimum_is_the_larger_of_the_two_floors() { // Stripe refuses a charge under the settlement currency's floor, so a // creator minimum below it is not a price anyone can pay. let low = PwywPricing { min_cents: Some(25), }; assert_eq!(low.chargeable_minimum_cents(SettlementCurrency::Usd), 50); assert_eq!(low.chargeable_minimum_cents(SettlementCurrency::Gbp), 30); let high = PwywPricing { min_cents: Some(999), }; assert_eq!(high.chargeable_minimum_cents(SettlementCurrency::Usd), 999); // No minimum at all still has a chargeable floor: $0 is a free claim, // not a charge, and every charge clears Stripe's floor. let none = PwywPricing { min_cents: None }; assert_eq!(none.chargeable_minimum_cents(SettlementCurrency::Usd), 50); } #[test] fn pwyw_price_display_states_the_chargeable_minimum() { // The card used to promise "From $0.25" against a charge path that // refused anything under $0.50. let p = PwywPricing { min_cents: Some(25), }; assert_eq!(p.price_display(SettlementCurrency::Usd), "From $0.50"); assert_eq!(p.price_display(SettlementCurrency::Gbp), "From \u{a3}0.30"); } #[test] fn pwyw_sub_floor_amount_is_refused_by_the_model_not_by_stripe() { // 25c against a 25c minimum: the model itself now names $0.50, so the // buyer is not told the "minimum purchase amount" by a downstream // guard that sounds like the creator mispriced the project. let p = PwywPricing { min_cents: Some(25), }; let Err(msg) = p.validate_amount(25, SettlementCurrency::Usd) else { panic!("25c must not reach a charge"); }; assert_eq!(msg, "Amount must be at least $0.50"); assert!(p.validate_amount(50, SettlementCurrency::Usd).is_ok()); } #[test] fn pwyw_free_claim_survives_the_floor() { // The floor is on a charge. A creator offering the project for nothing // still gets $0 claims, which never reach Stripe. let free = PwywPricing { min_cents: Some(0) }; assert!(free.validate_amount(0, SettlementCurrency::Usd).is_ok()); assert!( PwywPricing { min_cents: None } .validate_amount(0, SettlementCurrency::Usd) .is_ok() ); // But a creator who set a real minimum is not offering it free. let paid = PwywPricing { min_cents: Some(500), }; assert!(paid.validate_amount(0, SettlementCurrency::Usd).is_err()); } #[test] fn pwyw_minimum_cents() { let p = PwywPricing { min_cents: Some(500), }; assert_eq!(p.minimum_cents(), Some(500)); } #[test] fn pwyw_price_cents_with_min() { let p = PwywPricing { min_cents: Some(500), }; assert_eq!(p.price_cents(), 500); } #[test] fn pwyw_price_cents_no_min() { let p = PwywPricing { min_cents: None }; assert_eq!(p.price_cents(), 0); } #[test] fn pwyw_kind() { let p = PwywPricing { min_cents: None }; assert_eq!(p.kind(), db::PricingKind::Pwyw); } // ── SubscriptionPricing ── #[test] fn subscription_not_free() { assert!(!SubscriptionPricing.is_free()); } #[test] fn subscription_access_creator() { assert!(SubscriptionPricing.can_access(&AccessContext { is_creator: true, ..Default::default() })); } #[test] fn subscription_access_subscribed() { assert!(SubscriptionPricing.can_access(&AccessContext { subscription: Some(crate::db::subscriptions::SubscriptionGate::test_witness()), ..Default::default() })); } #[test] fn subscription_access_purchased_not_enough() { assert!(!SubscriptionPricing.can_access(&AccessContext { has_purchased: true, ..Default::default() })); } #[test] fn subscription_access_denied() { assert!(!SubscriptionPricing.can_access(&AccessContext::default())); } #[test] fn subscription_price_cents_is_zero() { assert_eq!(SubscriptionPricing.price_cents(), 0); } #[test] fn subscription_price_display() { assert_eq!( SubscriptionPricing.price_display(SettlementCurrency::Usd), "Subscription" ); } #[test] fn subscription_checkout_type() { assert_eq!( SubscriptionPricing.checkout_type(), CheckoutType::Subscription ); } #[test] fn subscription_validate_amount() { assert!( SubscriptionPricing .validate_amount(100, SettlementCurrency::Usd) .is_err() ); } #[test] fn subscription_kind() { assert_eq!(SubscriptionPricing.kind(), db::PricingKind::Subscription); } // ── Constructors ── #[test] fn for_item_free() { let item = make_test_item(0, false, None); let p = for_item(&item); assert!(p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::None); } #[test] fn for_item_fixed() { let item = make_test_item(999, false, None); let p = for_item(&item); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::OneTime); assert_eq!(p.price_cents(), 999); } #[test] fn for_item_pwyw() { let mut item = make_test_item(500, false, Some(100)); item.pwyw_enabled = true; let p = for_item(&item); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::PayWhatYouWant); assert_eq!(p.minimum_cents(), Some(100)); } #[test] fn for_project_free() { let project = make_test_project(db::PricingKind::Free, 0, None); let p = for_project(&project); assert!(p.is_free()); } #[test] fn for_project_buy_once() { let project = make_test_project(db::PricingKind::BuyOnce, 1999, None); let p = for_project(&project); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::OneTime); assert_eq!(p.price_cents(), 1999); } #[test] fn for_project_pwyw() { let project = make_test_project(db::PricingKind::Pwyw, 0, Some(500)); let p = for_project(&project); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::PayWhatYouWant); } #[test] fn for_project_subscription() { let project = make_test_project(db::PricingKind::Subscription, 0, None); let p = for_project(&project); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::Subscription); } // ── Edge cases (test-fuzz) ── #[test] fn fixed_zero_cents_still_not_free() { // FixedPricing with 0 cents: is_free is hardcoded false let p = FixedPricing { price_cents: 0 }; assert!(!p.is_free()); assert_eq!(p.price_cents(), 0); } #[test] fn fixed_negative_price_validate_amount() { // Negative price_cents is semantically wrong but FixedPricing doesn't validate construction let p = FixedPricing { price_cents: -100 }; // amount >= price_cents (-100), so 0 and -50 pass, but -200 fails assert!(p.validate_amount(0, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(-50, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(-200, SettlementCurrency::Usd).is_err()); // -200 < -100 } #[test] fn pwyw_validate_amount_at_cap() { let p = PwywPricing { min_cents: Some(0) }; assert!( p.validate_amount(1_000_000, SettlementCurrency::Usd) .is_ok() ); // exactly $10,000 assert!( p.validate_amount(1_000_001, SettlementCurrency::Usd) .is_err() ); // $10,000.01 } #[test] fn pwyw_validate_amount_negative() { let p = PwywPricing { min_cents: Some(0) }; // Negative amount is below min (0), should fail assert!(p.validate_amount(-1, SettlementCurrency::Usd).is_err()); } #[test] fn pwyw_negative_min_cents() { // A negative minimum is a corrupt row, and it used to let a negative // amount through on the "still above the minimum" reading. The floor // is now the larger of the creator's minimum and the currency's, so a // corrupt row cannot open a path to a negative charge. let p = PwywPricing { min_cents: Some(-100), }; assert!(p.validate_amount(-50, SettlementCurrency::Usd).is_err()); assert!(p.validate_amount(500, SettlementCurrency::Usd).is_ok()); } #[test] fn fixed_validate_amount_no_upper_cap() { // FixedPricing has no $10k cap like PWYW does let p = FixedPricing { price_cents: 100 }; assert!( p.validate_amount(99_999_999, SettlementCurrency::Usd) .is_ok() ); } #[test] fn for_item_pwyw_zero_price_still_pwyw() { // pwyw_enabled=true with price_cents=0 → PWYW, not Free let mut item = make_test_item(0, false, None); item.pwyw_enabled = true; let p = for_item(&item); assert!(!p.is_free()); assert_eq!(p.checkout_type(), CheckoutType::PayWhatYouWant); } #[test] fn subscription_purchased_user_cannot_access() { // Subscription items don't honor has_purchased (by design) assert!(!SubscriptionPricing.can_access(&AccessContext { has_purchased: true, subscription: None, is_creator: false, })); } #[test] fn free_minimum_cents_is_none() { assert_eq!(FreePricing.minimum_cents(), None); } #[test] fn fixed_minimum_cents_is_none() { let p = FixedPricing { price_cents: 999 }; assert_eq!(p.minimum_cents(), None); } #[test] fn subscription_minimum_cents_is_none() { assert_eq!(SubscriptionPricing.minimum_cents(), None); } // ── Adversarial (test-fuzz) ── #[test] fn adversarial_pwyw_max_i32_amount() { let p = PwywPricing { min_cents: Some(0) }; // i32::MAX = 2,147,483,647 cents = ~$21.4M, should be rejected by $10k cap assert!( p.validate_amount(i32::MAX, SettlementCurrency::Usd) .is_err() ); } #[test] fn adversarial_pwyw_min_i32_amount() { let p = PwywPricing { min_cents: Some(0) }; assert!( p.validate_amount(i32::MIN, SettlementCurrency::Usd) .is_err() ); } #[test] fn adversarial_fixed_price_i32_max() { let p = FixedPricing { price_cents: i32::MAX, }; // validate_amount with exactly i32::MAX should pass assert!(p.validate_amount(i32::MAX, SettlementCurrency::Usd).is_ok()); // Any amount below should fail assert!( p.validate_amount(i32::MAX - 1, SettlementCurrency::Usd) .is_err() ); } #[test] fn adversarial_all_access_flags_false() { let ctx = AccessContext { is_creator: false, has_purchased: false, subscription: None, }; // Only FreePricing should grant access with no flags assert!(FreePricing.can_access(&ctx)); assert!(!FixedPricing { price_cents: 100 }.can_access(&ctx)); assert!(!PwywPricing { min_cents: None }.can_access(&ctx)); assert!(!SubscriptionPricing.can_access(&ctx)); } #[test] fn adversarial_all_access_flags_true() { let ctx = AccessContext { is_creator: true, has_purchased: true, subscription: Some(crate::db::subscriptions::SubscriptionGate::test_witness()), }; // All pricing models should grant access with all flags assert!(FreePricing.can_access(&ctx)); assert!(FixedPricing { price_cents: 100 }.can_access(&ctx)); assert!(PwywPricing { min_cents: None }.can_access(&ctx)); assert!(SubscriptionPricing.can_access(&ctx)); } // ── Test helpers ── fn make_test_item(price_cents: i32, pwyw_enabled: bool, pwyw_min_cents: Option) -> db::DbItem { db::DbItem { id: db::ItemId::nil(), project_id: db::ProjectId::nil(), title: "test".to_string(), description: None, price_cents, item_type: db::ItemType::Digital, thumbnail_url: None, is_public: true, sort_order: 0, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), body: None, word_count: None, reading_time_minutes: None, audio_url: None, duration_seconds: None, cover_image_url: None, episode_number: None, audio_s3_key: None, cover_s3_key: None, enable_license_keys: false, default_max_activations: None, sales_count: 0, play_count: 0, unique_play_count: 0, download_count: 0, pwyw_enabled, pwyw_min_cents, scan_status: db::FileScanStatus::Clean, cover_scan_status: "clean".to_string(), release_announced_at: None, publish_at: None, mt_thread_id: None, web_only: false, audio_file_size_bytes: None, cover_file_size_bytes: None, video_s3_key: None, video_file_size_bytes: None, video_duration_seconds: None, video_width: None, video_height: None, slug: "test".to_string(), listed: true, license_preset: None, custom_license_text: None, ai_tier: db::AiTier::Handmade, ai_disclosure: None, removed_by_admin: false, removal_reason: None, removed_at: None, deleted_at: None, } } fn make_test_project( pricing_model: db::PricingKind, price_cents: i32, pwyw_min_cents: Option, ) -> db::DbProject { db::DbProject { id: db::ProjectId::nil(), user_id: db::UserId::nil(), slug: db::Slug::from_trusted("test".to_string()), title: "Test Project".to_string(), description: None, project_type: db::ProjectType::General, cover_image_url: None, cover_scan_status: "clean".to_string(), theme_id: None, is_public: true, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), cache_generation: 0, mt_community_id: None, features: vec![], pricing_model, price_cents, pwyw_min_cents, license_verification_enabled: false, ai_tier: db::AiTier::Handmade, ai_disclosure: None, custom_html: String::new(), custom_css: String::new(), custom_pages_updated_at: None, } } // ── Edge cases: PWYW min exceeds cap (test-fuzz) ── #[test] fn pwyw_min_above_cap_creates_impossible_range() { // If min_cents > 1_000_000, no valid amount exists: // amount must be >= min (1_000_001) AND <= 1_000_000, empty set. let p = PwywPricing { min_cents: Some(1_000_001), }; // Any amount below min fails the min check assert!( p.validate_amount(1_000_000, SettlementCurrency::Usd) .is_err() ); // Any amount at/above min fails the cap check assert!( p.validate_amount(1_000_001, SettlementCurrency::Usd) .is_err() ); // Even i32::MAX fails assert!( p.validate_amount(i32::MAX, SettlementCurrency::Usd) .is_err() ); } #[test] fn pwyw_min_exactly_at_cap_allows_single_value() { // min_cents == 1_000_000: only amount == 1_000_000 should work let p = PwywPricing { min_cents: Some(1_000_000), }; assert!( p.validate_amount(1_000_000, SettlementCurrency::Usd) .is_ok() ); assert!(p.validate_amount(999_999, SettlementCurrency::Usd).is_err()); assert!( p.validate_amount(1_000_001, SettlementCurrency::Usd) .is_err() ); } #[test] fn pwyw_none_min_allows_zero() { // min_cents = None → unwrap_or(0) → amount >= 0 required let p = PwywPricing { min_cents: None }; assert!(p.validate_amount(0, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(-1, SettlementCurrency::Usd).is_err()); assert!( p.validate_amount(1_000_000, SettlementCurrency::Usd) .is_ok() ); assert!( p.validate_amount(1_000_001, SettlementCurrency::Usd) .is_err() ); } #[test] fn fixed_validate_amount_at_exact_boundary() { // Amount exactly equal to price should pass (not off-by-one) let p = FixedPricing { price_cents: 1 }; assert!(p.validate_amount(1, SettlementCurrency::Usd).is_ok()); assert!(p.validate_amount(0, SettlementCurrency::Usd).is_err()); } #[test] fn pwyw_access_subscribed() { // PwywPricing should grant access to subscribers (like FixedPricing) let p = PwywPricing { min_cents: Some(500), }; assert!(p.can_access(&AccessContext { subscription: Some(crate::db::subscriptions::SubscriptionGate::test_witness()), ..Default::default() })); } // ── Property-based tests (proptest) ── proptest::proptest! { #[test] fn prop_free_always_accessible( is_creator in proptest::bool::ANY, has_purchased in proptest::bool::ANY, has_active_subscription in proptest::bool::ANY, ) { let ctx = AccessContext { is_creator, has_purchased, subscription: has_active_subscription.then(crate::db::subscriptions::SubscriptionGate::test_witness) }; proptest::prop_assert!(FreePricing.can_access(&ctx)); proptest::prop_assert_eq!(FreePricing.price_cents(), 0); } #[test] fn prop_fixed_access_requires_flag( is_creator in proptest::bool::ANY, has_purchased in proptest::bool::ANY, has_active_subscription in proptest::bool::ANY, ) { let ctx = AccessContext { is_creator, has_purchased, subscription: has_active_subscription.then(crate::db::subscriptions::SubscriptionGate::test_witness) }; let p = FixedPricing { price_cents: 999 }; let expected = is_creator || has_purchased || has_active_subscription; proptest::prop_assert_eq!(p.can_access(&ctx), expected); } #[test] fn prop_pwyw_access_requires_flag( is_creator in proptest::bool::ANY, has_purchased in proptest::bool::ANY, has_active_subscription in proptest::bool::ANY, ) { let ctx = AccessContext { is_creator, has_purchased, subscription: has_active_subscription.then(crate::db::subscriptions::SubscriptionGate::test_witness) }; let p = PwywPricing { min_cents: Some(500) }; let expected = is_creator || has_purchased || has_active_subscription; proptest::prop_assert_eq!(p.can_access(&ctx), expected); } #[test] fn prop_subscription_ignores_purchased( is_creator in proptest::bool::ANY, has_purchased in proptest::bool::ANY, has_active_subscription in proptest::bool::ANY, ) { let ctx = AccessContext { is_creator, has_purchased, subscription: has_active_subscription.then(crate::db::subscriptions::SubscriptionGate::test_witness) }; // Subscription only honors is_creator and has_active_subscription let expected = is_creator || has_active_subscription; proptest::prop_assert_eq!(SubscriptionPricing.can_access(&ctx), expected); } #[test] fn prop_fixed_validate_amount_consistent(price in 0..=1_000_000i32, amount in -100_000..=2_000_000i32) { let p = FixedPricing { price_cents: price }; let result = p.validate_amount(amount, SettlementCurrency::Usd); if amount >= price { proptest::prop_assert!(result.is_ok()); } else { proptest::prop_assert!(result.is_err()); } } #[test] fn prop_pwyw_validate_enforces_min_and_cap(min in 0..=1_100_000i32, amount in -1_000..=1_100_000i32) { let p = PwywPricing { min_cents: Some(min) }; let result = p.validate_amount(amount, SettlementCurrency::Usd); if amount >= min && amount <= 1_000_000 { proptest::prop_assert!(result.is_ok()); } else { proptest::prop_assert!(result.is_err()); } } #[test] fn prop_subscription_never_direct_purchase(amount in proptest::num::i32::ANY) { proptest::prop_assert!(SubscriptionPricing.validate_amount(amount, SettlementCurrency::Usd).is_err()); } } // ── parse_dollars_to_cents ── #[test] fn empty_or_missing_is_zero() { assert_eq!(parse_dollars_to_cents("Price", None).unwrap(), 0); assert_eq!(parse_dollars_to_cents("Price", Some("")).unwrap(), 0); assert_eq!(parse_dollars_to_cents("Price", Some(" ")).unwrap(), 0); } #[test] fn rounds_to_nearest_cent() { assert_eq!(parse_dollars_to_cents("Price", Some("9.99")).unwrap(), 999); assert_eq!(parse_dollars_to_cents("Price", Some("1.234")).unwrap(), 123); assert_eq!(parse_dollars_to_cents("Price", Some("1.236")).unwrap(), 124); } #[test] fn rejects_nan() { assert!(parse_dollars_to_cents("Price", Some("NaN")).is_err()); assert!(parse_dollars_to_cents("Price", Some("nan")).is_err()); } #[test] fn rejects_infinity() { assert!(parse_dollars_to_cents("Price", Some("inf")).is_err()); assert!(parse_dollars_to_cents("Price", Some("Infinity")).is_err()); } #[test] fn rejects_negative() { assert!(parse_dollars_to_cents("Price", Some("-1")).is_err()); assert!(parse_dollars_to_cents("Price", Some("-0.01")).is_err()); } #[test] fn rejects_overflow() { assert!(parse_dollars_to_cents("Price", Some("100000000000")).is_err()); assert!(parse_dollars_to_cents("Price", Some("1e20")).is_err()); } #[test] fn rejects_garbage() { assert!(parse_dollars_to_cents("Price", Some("abc")).is_err()); assert!(parse_dollars_to_cents("Price", Some("free")).is_err()); } #[test] fn strips_clipboard_decoration() { // Clipboard pastes from invoices / price lists shouldn't 422. assert_eq!(parse_dollars_to_cents("Price", Some("$5")).unwrap(), 500); assert_eq!( parse_dollars_to_cents("Price", Some("1,000")).unwrap(), 100_000 ); assert_eq!( parse_dollars_to_cents("Price", Some("$ 1,250.00")).unwrap(), 125_000 ); assert_eq!( parse_dollars_to_cents("Price", Some(" $9.99 ")).unwrap(), 999 ); // Decoration-only input still parses as garbage (no digits → fails) assert!(parse_dollars_to_cents("Price", Some("$$")).is_err()); } // `validate_dollars_f64` is the JSON-handler entry point and had no test of // its own: every case above reached it only through the string parser, so // mutation testing found all twelve of its mutants alive (Phase 0 run, // 2026-08-16). The boundary cases are the point — an off-by-one on the // overflow guard is the difference between a $21,474,836.47 price and a // wrapped negative one. #[test] fn validate_f64_rejects_non_finite() { assert!(validate_dollars_f64("Price", f64::NAN).is_err()); assert!(validate_dollars_f64("Price", f64::INFINITY).is_err()); assert!(validate_dollars_f64("Price", f64::NEG_INFINITY).is_err()); } #[test] fn validate_f64_rejects_negative_but_accepts_zero() { assert!(validate_dollars_f64("Price", -0.01).is_err()); assert!(validate_dollars_f64("Price", -1.0).is_err()); assert_eq!(validate_dollars_f64("Price", 0.0).unwrap(), 0); } #[test] fn validate_f64_multiplies_by_a_hundred_and_rounds() { assert_eq!(validate_dollars_f64("Price", 9.99).unwrap(), 999); assert_eq!(validate_dollars_f64("Price", 1.234).unwrap(), 123); assert_eq!(validate_dollars_f64("Price", 1.236).unwrap(), 124); assert_eq!(validate_dollars_f64("Price", 2.0).unwrap(), 200); } #[test] fn validate_f64_overflow_guard_is_inclusive_at_i32_max_cents() { // Exactly `i32::MAX` cents is the largest representable price and must // be accepted; one cent more must not be. let at_max = f64::from(i32::MAX) / 100.0; assert_eq!(validate_dollars_f64("Price", at_max).unwrap(), i32::MAX); assert!(validate_dollars_f64("Price", at_max + 0.01).is_err()); assert!(validate_dollars_f64("Price", 1e20).is_err()); }