# tests/unit/services/test_admin_customer_service.py """ Unit tests for AdminCustomerService. """ from decimal import Decimal import pytest from app.exceptions.customer import CustomerNotFoundException from app.services.admin_customer_service import AdminCustomerService from app.modules.customers.models.customer import Customer @pytest.fixture def admin_customer_service(): """Create AdminCustomerService instance.""" return AdminCustomerService() @pytest.fixture def customer_with_orders(db, test_vendor, test_customer): """Create a customer with order data.""" test_customer.total_orders = 5 test_customer.total_spent = Decimal("250.00") db.commit() db.refresh(test_customer) return test_customer @pytest.fixture def multiple_customers(db, test_vendor): """Create multiple customers for testing.""" customers = [] for i in range(5): customer = Customer( vendor_id=test_vendor.id, email=f"customer{i}@example.com", hashed_password="hashed_password_placeholder", first_name=f"First{i}", last_name=f"Last{i}", customer_number=f"CUST-00{i}", is_active=(i % 2 == 0), # Alternate active/inactive total_orders=i, total_spent=Decimal(str(i * 100)), ) db.add(customer) customers.append(customer) db.commit() for c in customers: db.refresh(c) return customers @pytest.mark.unit class TestAdminCustomerServiceList: """Tests for list_customers method.""" def test_list_customers_empty(self, db, admin_customer_service, test_vendor): """Test listing customers when none exist.""" customers, total = admin_customer_service.list_customers(db) assert customers == [] assert total == 0 def test_list_customers_basic(self, db, admin_customer_service, test_customer): """Test basic customer listing.""" customers, total = admin_customer_service.list_customers(db) assert total == 1 assert len(customers) == 1 assert customers[0]["id"] == test_customer.id assert customers[0]["email"] == test_customer.email def test_list_customers_with_vendor_info( self, db, admin_customer_service, test_customer, test_vendor ): """Test that vendor info is included.""" customers, total = admin_customer_service.list_customers(db) assert customers[0]["vendor_name"] == test_vendor.name assert customers[0]["vendor_code"] == test_vendor.vendor_code def test_list_customers_filter_by_vendor( self, db, admin_customer_service, multiple_customers, test_vendor ): """Test filtering by vendor ID.""" customers, total = admin_customer_service.list_customers( db, vendor_id=test_vendor.id ) assert total == 5 for customer in customers: assert customer["vendor_id"] == test_vendor.id def test_list_customers_filter_by_active_status( self, db, admin_customer_service, multiple_customers ): """Test filtering by active status.""" # Get active customers (0, 2, 4 = 3 customers) customers, total = admin_customer_service.list_customers(db, is_active=True) assert total == 3 # Get inactive customers (1, 3 = 2 customers) customers, total = admin_customer_service.list_customers(db, is_active=False) assert total == 2 def test_list_customers_search_by_email( self, db, admin_customer_service, multiple_customers ): """Test searching by email.""" customers, total = admin_customer_service.list_customers( db, search="customer2@" ) assert total == 1 assert customers[0]["email"] == "customer2@example.com" def test_list_customers_search_by_name( self, db, admin_customer_service, multiple_customers ): """Test searching by name.""" customers, total = admin_customer_service.list_customers(db, search="First3") assert total == 1 assert customers[0]["first_name"] == "First3" def test_list_customers_search_by_customer_number( self, db, admin_customer_service, multiple_customers ): """Test searching by customer number.""" customers, total = admin_customer_service.list_customers(db, search="CUST-001") assert total == 1 assert customers[0]["customer_number"] == "CUST-001" def test_list_customers_pagination( self, db, admin_customer_service, multiple_customers ): """Test pagination.""" # Get first page customers, total = admin_customer_service.list_customers(db, skip=0, limit=2) assert len(customers) == 2 assert total == 5 # Get second page customers, total = admin_customer_service.list_customers(db, skip=2, limit=2) assert len(customers) == 2 # Get last page customers, total = admin_customer_service.list_customers(db, skip=4, limit=2) assert len(customers) == 1 @pytest.mark.unit class TestAdminCustomerServiceStats: """Tests for get_customer_stats method.""" def test_get_customer_stats_empty(self, db, admin_customer_service, test_vendor): """Test stats when no customers exist.""" stats = admin_customer_service.get_customer_stats(db) assert stats["total"] == 0 assert stats["active"] == 0 assert stats["inactive"] == 0 assert stats["with_orders"] == 0 assert stats["total_spent"] == 0 assert stats["total_orders"] == 0 assert stats["avg_order_value"] == 0 def test_get_customer_stats_with_data( self, db, admin_customer_service, multiple_customers ): """Test stats with customer data.""" stats = admin_customer_service.get_customer_stats(db) assert stats["total"] == 5 assert stats["active"] == 3 # 0, 2, 4 assert stats["inactive"] == 2 # 1, 3 # with_orders = customers with total_orders > 0 (1, 2, 3, 4 = 4 customers) assert stats["with_orders"] == 4 # total_spent = 0 + 100 + 200 + 300 + 400 = 1000 assert stats["total_spent"] == 1000.0 # total_orders = 0 + 1 + 2 + 3 + 4 = 10 assert stats["total_orders"] == 10 def test_get_customer_stats_by_vendor( self, db, admin_customer_service, test_customer, test_vendor ): """Test stats filtered by vendor.""" stats = admin_customer_service.get_customer_stats(db, vendor_id=test_vendor.id) assert stats["total"] == 1 def test_get_customer_stats_avg_order_value( self, db, admin_customer_service, customer_with_orders ): """Test average order value calculation.""" stats = admin_customer_service.get_customer_stats(db) # total_spent = 250, total_orders = 5 # avg = 250 / 5 = 50 assert stats["avg_order_value"] == 50.0 @pytest.mark.unit class TestAdminCustomerServiceGetCustomer: """Tests for get_customer method.""" def test_get_customer_success(self, db, admin_customer_service, test_customer): """Test getting customer by ID.""" customer = admin_customer_service.get_customer(db, test_customer.id) assert customer["id"] == test_customer.id assert customer["email"] == test_customer.email assert customer["first_name"] == test_customer.first_name assert customer["last_name"] == test_customer.last_name def test_get_customer_with_vendor_info( self, db, admin_customer_service, test_customer, test_vendor ): """Test vendor info in customer detail.""" customer = admin_customer_service.get_customer(db, test_customer.id) assert customer["vendor_name"] == test_vendor.name assert customer["vendor_code"] == test_vendor.vendor_code def test_get_customer_not_found(self, db, admin_customer_service): """Test error when customer not found.""" with pytest.raises(CustomerNotFoundException): admin_customer_service.get_customer(db, 99999) @pytest.mark.unit class TestAdminCustomerServiceToggleStatus: """Tests for toggle_customer_status method.""" def test_toggle_status_activate( self, db, admin_customer_service, test_customer, test_admin ): """Test activating an inactive customer.""" # Make customer inactive first test_customer.is_active = False db.commit() result = admin_customer_service.toggle_customer_status( db, test_customer.id, test_admin.email ) db.commit() assert result["id"] == test_customer.id assert result["is_active"] is True assert "activated" in result["message"] def test_toggle_status_deactivate( self, db, admin_customer_service, test_customer, test_admin ): """Test deactivating an active customer.""" test_customer.is_active = True db.commit() result = admin_customer_service.toggle_customer_status( db, test_customer.id, test_admin.email ) db.commit() assert result["id"] == test_customer.id assert result["is_active"] is False assert "deactivated" in result["message"] def test_toggle_status_not_found( self, db, admin_customer_service, test_admin ): """Test error when customer not found.""" with pytest.raises(CustomerNotFoundException): admin_customer_service.toggle_customer_status( db, 99999, test_admin.email )