"""Tests for BillingSystem behavior.""" from __future__ import annotations import pytest from env.billing import BillingRecord, BillingSystem, Dispute, FailedPayment, Invoice, Refund def _billing_system() -> BillingSystem: record = BillingRecord( customer_id="CUST-A", current_balance=250.0, payment_status="failed", pending_invoices=[Invoice(invoice_id="INV-1", amount=250.0, due_step=2)], failed_payments=[FailedPayment(payment_id="PAY-1", amount=250.0, reason="gateway_timeout")], total_lifetime_value=10000.0, ) return BillingSystem({"CUST-A": record}) # ===================================================================== # Billing checks and refunds # ===================================================================== def test_check_billing_reveals_failed_payments() -> None: system = _billing_system() response = system.check_billing("CUST-A") assert response.payment_status == "failed" assert len(response.failed_payments) == 1 def test_process_refund_respects_cap() -> None: system = _billing_system() system.set_refund_cap(100.0) result = system.process_refund("CUST-A", 120.0) assert result.approved is False assert "policy cap" in result.message.lower() or "cap" in result.message.lower() def test_process_refund_approved_updates_balance() -> None: system = _billing_system() result = system.process_refund("CUST-A", 50.0) assert result.approved is True response = system.check_billing("CUST-A") assert response.current_balance == pytest.approx(200.0) # ===================================================================== # Edge cases # ===================================================================== def test_refund_negative_amount_rejected() -> None: system = _billing_system() result = system.process_refund("CUST-A", -10.0) assert result.approved is False def test_refund_at_cap_boundary_approved() -> None: system = _billing_system() system.set_refund_cap(100.0) result = system.process_refund("CUST-A", 100.0) assert result.approved is True def test_refund_just_above_cap_rejected() -> None: system = _billing_system() system.set_refund_cap(100.0) result = system.process_refund("CUST-A", 100.01) assert result.approved is False def test_refund_clears_balance_to_current() -> None: system = _billing_system() system.set_refund_cap(300.0) system.process_refund("CUST-A", 250.0) response = system.check_billing("CUST-A") assert response.payment_status == "current" assert response.current_balance == pytest.approx(0.0) def test_multiple_refunds_accumulate() -> None: system = _billing_system() system.process_refund("CUST-A", 50.0) system.process_refund("CUST-A", 50.0) response = system.check_billing("CUST-A") assert response.current_balance == pytest.approx(150.0) assert len(response.refund_history) == 2 def test_billing_response_contains_disputes() -> None: record = BillingRecord( customer_id="CUST-D", current_balance=100.0, payment_status="disputed", active_disputes=[Dispute(dispute_id="DSP-1", amount=100.0, reason="unauthorized")], ) system = BillingSystem({"CUST-D": record}) response = system.check_billing("CUST-D") assert len(response.active_disputes) == 1 assert response.active_disputes[0].dispute_id == "DSP-1" def test_default_refund_cap_is_150() -> None: system = _billing_system() result = system.process_refund("CUST-A", 149.0) assert result.approved is True result_over = system.process_refund("CUST-A", 151.0) assert result_over.approved is False def test_check_billing_unknown_customer_raises() -> None: system = _billing_system() with pytest.raises(KeyError): system.check_billing("NONEXISTENT")