# tests/unit/services/test_customer_address_service.py """ Unit tests for CustomerAddressService. """ import pytest from app.modules.customers.exceptions import AddressLimitExceededException, AddressNotFoundException from app.modules.customers.services.customer_address_service import CustomerAddressService from app.modules.customers.models.customer import CustomerAddress from app.modules.customers.schemas import CustomerAddressCreate, CustomerAddressUpdate @pytest.fixture def address_service(): """Create CustomerAddressService instance.""" return CustomerAddressService() @pytest.fixture def multiple_addresses(db, test_vendor, test_customer): """Create multiple addresses for testing.""" addresses = [] for i in range(3): address = CustomerAddress( vendor_id=test_vendor.id, customer_id=test_customer.id, address_type="shipping" if i < 2 else "billing", first_name=f"First{i}", last_name=f"Last{i}", address_line_1=f"{i+1} Test Street", city="Luxembourg", postal_code=f"L-{1000+i}", country_name="Luxembourg", country_iso="LU", is_default=(i == 0), # First shipping is default ) db.add(address) addresses.append(address) db.commit() for a in addresses: db.refresh(a) return addresses @pytest.mark.unit class TestCustomerAddressServiceList: """Tests for list_addresses method.""" def test_list_addresses_empty(self, db, address_service, test_vendor, test_customer): """Test listing addresses when none exist.""" addresses = address_service.list_addresses( db, vendor_id=test_vendor.id, customer_id=test_customer.id ) assert addresses == [] def test_list_addresses_basic( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test basic address listing.""" addresses = address_service.list_addresses( db, vendor_id=test_vendor.id, customer_id=test_customer.id ) assert len(addresses) == 1 assert addresses[0].id == test_customer_address.id def test_list_addresses_ordered_by_default( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test addresses are ordered by default flag first.""" addresses = address_service.list_addresses( db, vendor_id=test_vendor.id, customer_id=test_customer.id ) # Default address should be first assert addresses[0].is_default is True def test_list_addresses_vendor_isolation( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test addresses are isolated by vendor.""" # Query with different vendor ID addresses = address_service.list_addresses( db, vendor_id=99999, customer_id=test_customer.id ) assert addresses == [] @pytest.mark.unit class TestCustomerAddressServiceGet: """Tests for get_address method.""" def test_get_address_success( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test getting address by ID.""" address = address_service.get_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=test_customer_address.id, ) assert address.id == test_customer_address.id assert address.first_name == test_customer_address.first_name def test_get_address_not_found( self, db, address_service, test_vendor, test_customer ): """Test error when address not found.""" with pytest.raises(AddressNotFoundException): address_service.get_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=99999, ) def test_get_address_wrong_customer( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test cannot get another customer's address.""" with pytest.raises(AddressNotFoundException): address_service.get_address( db, vendor_id=test_vendor.id, customer_id=99999, # Different customer address_id=test_customer_address.id, ) @pytest.mark.unit class TestCustomerAddressServiceGetDefault: """Tests for get_default_address method.""" def test_get_default_address_exists( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test getting default shipping address.""" address = address_service.get_default_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_type="shipping", ) assert address is not None assert address.is_default is True assert address.address_type == "shipping" def test_get_default_address_not_set( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test getting default billing when none is set.""" # Remove default from billing (none was set as default) address = address_service.get_default_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_type="billing", ) # The billing address exists but is not default assert address is None @pytest.mark.unit class TestCustomerAddressServiceCreate: """Tests for create_address method.""" def test_create_address_success( self, db, address_service, test_vendor, test_customer ): """Test creating a new address.""" address_data = CustomerAddressCreate( address_type="shipping", first_name="John", last_name="Doe", address_line_1="123 New Street", city="Luxembourg", postal_code="L-1234", country_name="Luxembourg", country_iso="LU", is_default=False, ) address = address_service.create_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_data=address_data, ) db.commit() assert address.id is not None assert address.first_name == "John" assert address.last_name == "Doe" assert address.country_iso == "LU" assert address.country_name == "Luxembourg" def test_create_address_with_company( self, db, address_service, test_vendor, test_customer ): """Test creating address with company name.""" address_data = CustomerAddressCreate( address_type="billing", first_name="Jane", last_name="Doe", company="Acme Corp", address_line_1="456 Business Ave", city="Luxembourg", postal_code="L-5678", country_name="Luxembourg", country_iso="LU", is_default=False, ) address = address_service.create_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_data=address_data, ) db.commit() assert address.company == "Acme Corp" def test_create_address_default_clears_others( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test creating default address clears other defaults of same type.""" # First address is default shipping assert multiple_addresses[0].is_default is True address_data = CustomerAddressCreate( address_type="shipping", first_name="New", last_name="Default", address_line_1="789 Main St", city="Luxembourg", postal_code="L-9999", country_name="Luxembourg", country_iso="LU", is_default=True, ) new_address = address_service.create_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_data=address_data, ) db.commit() # New address should be default assert new_address.is_default is True # Old default should be cleared db.refresh(multiple_addresses[0]) assert multiple_addresses[0].is_default is False def test_create_address_limit_exceeded( self, db, address_service, test_vendor, test_customer ): """Test error when max addresses reached.""" # Create 10 addresses (max limit) for i in range(10): addr = CustomerAddress( vendor_id=test_vendor.id, customer_id=test_customer.id, address_type="shipping", first_name=f"Test{i}", last_name="User", address_line_1=f"{i} Street", city="City", postal_code="12345", country_name="Luxembourg", country_iso="LU", ) db.add(addr) db.commit() # Try to create 11th address address_data = CustomerAddressCreate( address_type="shipping", first_name="Eleventh", last_name="User", address_line_1="11 Street", city="City", postal_code="12345", country_name="Luxembourg", country_iso="LU", is_default=False, ) with pytest.raises(AddressLimitExceededException): address_service.create_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_data=address_data, ) @pytest.mark.unit class TestCustomerAddressServiceUpdate: """Tests for update_address method.""" def test_update_address_success( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test updating an address.""" update_data = CustomerAddressUpdate( first_name="Updated", city="New City", ) address = address_service.update_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=test_customer_address.id, address_data=update_data, ) db.commit() assert address.first_name == "Updated" assert address.city == "New City" # Unchanged fields should remain assert address.last_name == test_customer_address.last_name def test_update_address_set_default( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test setting address as default clears others.""" # Second address is not default assert multiple_addresses[1].is_default is False update_data = CustomerAddressUpdate(is_default=True) address = address_service.update_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=multiple_addresses[1].id, address_data=update_data, ) db.commit() assert address.is_default is True # Old default should be cleared db.refresh(multiple_addresses[0]) assert multiple_addresses[0].is_default is False def test_update_address_not_found( self, db, address_service, test_vendor, test_customer ): """Test error when address not found.""" update_data = CustomerAddressUpdate(first_name="Test") with pytest.raises(AddressNotFoundException): address_service.update_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=99999, address_data=update_data, ) @pytest.mark.unit class TestCustomerAddressServiceDelete: """Tests for delete_address method.""" def test_delete_address_success( self, db, address_service, test_vendor, test_customer, test_customer_address ): """Test deleting an address.""" address_id = test_customer_address.id address_service.delete_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=address_id, ) db.commit() # Address should be gone with pytest.raises(AddressNotFoundException): address_service.get_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=address_id, ) def test_delete_address_not_found( self, db, address_service, test_vendor, test_customer ): """Test error when deleting non-existent address.""" with pytest.raises(AddressNotFoundException): address_service.delete_address( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=99999, ) @pytest.mark.unit class TestCustomerAddressServiceSetDefault: """Tests for set_default method.""" def test_set_default_success( self, db, address_service, test_vendor, test_customer, multiple_addresses ): """Test setting address as default.""" # Second shipping address is not default assert multiple_addresses[1].is_default is False assert multiple_addresses[1].address_type == "shipping" address = address_service.set_default( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=multiple_addresses[1].id, ) db.commit() assert address.is_default is True # Old default should be cleared db.refresh(multiple_addresses[0]) assert multiple_addresses[0].is_default is False def test_set_default_not_found( self, db, address_service, test_vendor, test_customer ): """Test error when address not found.""" with pytest.raises(AddressNotFoundException): address_service.set_default( db, vendor_id=test_vendor.id, customer_id=test_customer.id, address_id=99999, )