fix(billing): complete billing module — fix tier change, platform support, merchant portal
- Fix admin tier change: resolve tier_code→tier_id in update_subscription(), delegate to billing_service.change_tier() for Stripe-connected subs - Add platform support to admin tiers page: platform column, filter dropdown, platform selector in create/edit modal, platform_name in tier API response - Filter used platforms in create subscription modal on merchant detail page - Enrich merchant portal API responses with tier code, tier_name, platform_name - Add eager-load of platform relationship in get_merchant_subscription() - Remove stale store_name/store_code references from merchant templates - Add merchant tier change endpoint (POST /change-tier) and tier selector UI replacing broken requestUpgrade() button - Fix subscription detail link to use platform_id instead of sub.id Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
280
app/modules/customers/tests/unit/test_admin_customer_service.py
Normal file
280
app/modules/customers/tests/unit/test_admin_customer_service.py
Normal file
@@ -0,0 +1,280 @@
|
||||
# tests/unit/services/test_admin_customer_service.py
|
||||
"""
|
||||
Unit tests for AdminCustomerService.
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.customers.exceptions import CustomerNotFoundException
|
||||
from app.modules.customers.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_store, 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_store):
|
||||
"""Create multiple customers for testing."""
|
||||
customers = []
|
||||
for i in range(5):
|
||||
customer = Customer(
|
||||
store_id=test_store.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_store):
|
||||
"""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_store_info(
|
||||
self, db, admin_customer_service, test_customer, test_store
|
||||
):
|
||||
"""Test that store info is included."""
|
||||
customers, total = admin_customer_service.list_customers(db)
|
||||
|
||||
assert customers[0]["store_name"] == test_store.name
|
||||
assert customers[0]["store_code"] == test_store.store_code
|
||||
|
||||
def test_list_customers_filter_by_store(
|
||||
self, db, admin_customer_service, multiple_customers, test_store
|
||||
):
|
||||
"""Test filtering by store ID."""
|
||||
customers, total = admin_customer_service.list_customers(
|
||||
db, store_id=test_store.id
|
||||
)
|
||||
|
||||
assert total == 5
|
||||
for customer in customers:
|
||||
assert customer["store_id"] == test_store.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_store):
|
||||
"""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_store(
|
||||
self, db, admin_customer_service, test_customer, test_store
|
||||
):
|
||||
"""Test stats filtered by store."""
|
||||
stats = admin_customer_service.get_customer_stats(db, store_id=test_store.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_store_info(
|
||||
self, db, admin_customer_service, test_customer, test_store
|
||||
):
|
||||
"""Test store info in customer detail."""
|
||||
customer = admin_customer_service.get_customer(db, test_customer.id)
|
||||
|
||||
assert customer["store_name"] == test_store.name
|
||||
assert customer["store_code"] == test_store.store_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
|
||||
)
|
||||
@@ -0,0 +1,453 @@
|
||||
# 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_store, test_customer):
|
||||
"""Create multiple addresses for testing."""
|
||||
addresses = []
|
||||
for i in range(3):
|
||||
address = CustomerAddress(
|
||||
store_id=test_store.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_store, test_customer):
|
||||
"""Test listing addresses when none exist."""
|
||||
addresses = address_service.list_addresses(
|
||||
db, store_id=test_store.id, customer_id=test_customer.id
|
||||
)
|
||||
|
||||
assert addresses == []
|
||||
|
||||
def test_list_addresses_basic(
|
||||
self, db, address_service, test_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test basic address listing."""
|
||||
addresses = address_service.list_addresses(
|
||||
db, store_id=test_store.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_store, test_customer, multiple_addresses
|
||||
):
|
||||
"""Test addresses are ordered by default flag first."""
|
||||
addresses = address_service.list_addresses(
|
||||
db, store_id=test_store.id, customer_id=test_customer.id
|
||||
)
|
||||
|
||||
# Default address should be first
|
||||
assert addresses[0].is_default is True
|
||||
|
||||
def test_list_addresses_store_isolation(
|
||||
self, db, address_service, test_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test addresses are isolated by store."""
|
||||
# Query with different store ID
|
||||
addresses = address_service.list_addresses(
|
||||
db, store_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_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test getting address by ID."""
|
||||
address = address_service.get_address(
|
||||
db,
|
||||
store_id=test_store.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_store, test_customer
|
||||
):
|
||||
"""Test error when address not found."""
|
||||
with pytest.raises(AddressNotFoundException):
|
||||
address_service.get_address(
|
||||
db,
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_id=99999,
|
||||
)
|
||||
|
||||
def test_get_address_wrong_customer(
|
||||
self, db, address_service, test_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test cannot get another customer's address."""
|
||||
with pytest.raises(AddressNotFoundException):
|
||||
address_service.get_address(
|
||||
db,
|
||||
store_id=test_store.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_store, test_customer, multiple_addresses
|
||||
):
|
||||
"""Test getting default shipping address."""
|
||||
address = address_service.get_default_address(
|
||||
db,
|
||||
store_id=test_store.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_store, 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,
|
||||
store_id=test_store.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_store, 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,
|
||||
store_id=test_store.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_merchant(
|
||||
self, db, address_service, test_store, test_customer
|
||||
):
|
||||
"""Test creating address with merchant 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,
|
||||
store_id=test_store.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_store, 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,
|
||||
store_id=test_store.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_store, test_customer
|
||||
):
|
||||
"""Test error when max addresses reached."""
|
||||
# Create 10 addresses (max limit)
|
||||
for i in range(10):
|
||||
addr = CustomerAddress(
|
||||
store_id=test_store.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,
|
||||
store_id=test_store.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_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test updating an address."""
|
||||
update_data = CustomerAddressUpdate(
|
||||
first_name="Updated",
|
||||
city="New City",
|
||||
)
|
||||
|
||||
address = address_service.update_address(
|
||||
db,
|
||||
store_id=test_store.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_store, 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,
|
||||
store_id=test_store.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_store, test_customer
|
||||
):
|
||||
"""Test error when address not found."""
|
||||
update_data = CustomerAddressUpdate(first_name="Test")
|
||||
|
||||
with pytest.raises(AddressNotFoundException):
|
||||
address_service.update_address(
|
||||
db,
|
||||
store_id=test_store.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_store, test_customer, test_customer_address
|
||||
):
|
||||
"""Test deleting an address."""
|
||||
address_id = test_customer_address.id
|
||||
|
||||
address_service.delete_address(
|
||||
db,
|
||||
store_id=test_store.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,
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_id=address_id,
|
||||
)
|
||||
|
||||
def test_delete_address_not_found(
|
||||
self, db, address_service, test_store, test_customer
|
||||
):
|
||||
"""Test error when deleting non-existent address."""
|
||||
with pytest.raises(AddressNotFoundException):
|
||||
address_service.delete_address(
|
||||
db,
|
||||
store_id=test_store.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_store, 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,
|
||||
store_id=test_store.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_store, test_customer
|
||||
):
|
||||
"""Test error when address not found."""
|
||||
with pytest.raises(AddressNotFoundException):
|
||||
address_service.set_default(
|
||||
db,
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_id=99999,
|
||||
)
|
||||
247
app/modules/customers/tests/unit/test_customer_model.py
Normal file
247
app/modules/customers/tests/unit/test_customer_model.py
Normal file
@@ -0,0 +1,247 @@
|
||||
# tests/unit/models/database/test_customer.py
|
||||
"""Unit tests for Customer and CustomerAddress database models."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.database
|
||||
class TestCustomerModel:
|
||||
"""Test Customer model."""
|
||||
|
||||
def test_customer_creation(self, db, test_store):
|
||||
"""Test Customer model with store isolation."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="customer@example.com",
|
||||
hashed_password="hashed_password",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
customer_number="CUST001",
|
||||
is_active=True,
|
||||
)
|
||||
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.id is not None
|
||||
assert customer.store_id == test_store.id
|
||||
assert customer.email == "customer@example.com"
|
||||
assert customer.customer_number == "CUST001"
|
||||
assert customer.first_name == "John"
|
||||
assert customer.last_name == "Doe"
|
||||
assert customer.store.store_code == test_store.store_code
|
||||
|
||||
def test_customer_default_values(self, db, test_store):
|
||||
"""Test Customer model default values."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="defaults@example.com",
|
||||
hashed_password="hash",
|
||||
customer_number="CUST_DEFAULTS",
|
||||
)
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.is_active is True # Default
|
||||
assert customer.marketing_consent is False # Default
|
||||
assert customer.total_orders == 0 # Default
|
||||
assert customer.total_spent == 0 # Default
|
||||
|
||||
def test_customer_full_name_property(self, db, test_store):
|
||||
"""Test Customer full_name computed property."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="fullname@example.com",
|
||||
hashed_password="hash",
|
||||
customer_number="CUST_FULLNAME",
|
||||
first_name="Jane",
|
||||
last_name="Smith",
|
||||
)
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.full_name == "Jane Smith"
|
||||
|
||||
def test_customer_full_name_fallback_to_email(self, db, test_store):
|
||||
"""Test Customer full_name falls back to email when names not set."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="noname@example.com",
|
||||
hashed_password="hash",
|
||||
customer_number="CUST_NONAME",
|
||||
)
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.full_name == "noname@example.com"
|
||||
|
||||
def test_customer_optional_fields(self, db, test_store):
|
||||
"""Test Customer with optional fields."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="optional@example.com",
|
||||
hashed_password="hash",
|
||||
customer_number="CUST_OPT",
|
||||
phone="+352123456789",
|
||||
preferences={"language": "en", "currency": "EUR"},
|
||||
marketing_consent=True,
|
||||
)
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.phone == "+352123456789"
|
||||
assert customer.preferences == {"language": "en", "currency": "EUR"}
|
||||
assert customer.marketing_consent is True
|
||||
|
||||
def test_customer_store_relationship(self, db, test_store):
|
||||
"""Test Customer-Store relationship."""
|
||||
customer = Customer(
|
||||
store_id=test_store.id,
|
||||
email="relationship@example.com",
|
||||
hashed_password="hash",
|
||||
customer_number="CUST_REL",
|
||||
)
|
||||
db.add(customer)
|
||||
db.commit()
|
||||
db.refresh(customer)
|
||||
|
||||
assert customer.store is not None
|
||||
assert customer.store.id == test_store.id
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.database
|
||||
class TestCustomerAddressModel:
|
||||
"""Test CustomerAddress model."""
|
||||
|
||||
def test_customer_address_creation(self, db, test_store, test_customer):
|
||||
"""Test CustomerAddress model."""
|
||||
address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
is_default=True,
|
||||
)
|
||||
|
||||
db.add(address)
|
||||
db.commit()
|
||||
db.refresh(address)
|
||||
|
||||
assert address.id is not None
|
||||
assert address.store_id == test_store.id
|
||||
assert address.customer_id == test_customer.id
|
||||
assert address.address_type == "shipping"
|
||||
assert address.is_default is True
|
||||
|
||||
def test_customer_address_types(self, db, test_store, test_customer):
|
||||
"""Test CustomerAddress with different address types."""
|
||||
shipping_address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Shipping St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
db.add(shipping_address)
|
||||
|
||||
billing_address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="billing",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="456 Billing Ave",
|
||||
city="Luxembourg",
|
||||
postal_code="L-5678",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
db.add(billing_address)
|
||||
db.commit()
|
||||
|
||||
assert shipping_address.address_type == "shipping"
|
||||
assert billing_address.address_type == "billing"
|
||||
|
||||
def test_customer_address_optional_fields(self, db, test_store, test_customer):
|
||||
"""Test CustomerAddress with optional fields."""
|
||||
address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
company="ACME Corp",
|
||||
address_line_1="123 Main St",
|
||||
address_line_2="Suite 100",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
db.add(address)
|
||||
db.commit()
|
||||
db.refresh(address)
|
||||
|
||||
assert address.company == "ACME Corp"
|
||||
assert address.address_line_2 == "Suite 100"
|
||||
|
||||
def test_customer_address_default_values(self, db, test_store, test_customer):
|
||||
"""Test CustomerAddress default values."""
|
||||
address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
db.add(address)
|
||||
db.commit()
|
||||
db.refresh(address)
|
||||
|
||||
assert address.is_default is False # Default
|
||||
|
||||
def test_customer_address_relationships(self, db, test_store, test_customer):
|
||||
"""Test CustomerAddress relationships."""
|
||||
address = CustomerAddress(
|
||||
store_id=test_store.id,
|
||||
customer_id=test_customer.id,
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
db.add(address)
|
||||
db.commit()
|
||||
db.refresh(address)
|
||||
|
||||
assert address.customer is not None
|
||||
assert address.customer.id == test_customer.id
|
||||
358
app/modules/customers/tests/unit/test_customer_schema.py
Normal file
358
app/modules/customers/tests/unit/test_customer_schema.py
Normal file
@@ -0,0 +1,358 @@
|
||||
# tests/unit/models/schema/test_customer.py
|
||||
"""Unit tests for customer Pydantic schemas."""
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.modules.customers.schemas import (
|
||||
CustomerAddressCreate,
|
||||
CustomerAddressResponse,
|
||||
CustomerAddressUpdate,
|
||||
CustomerPreferencesUpdate,
|
||||
CustomerRegister,
|
||||
CustomerResponse,
|
||||
CustomerUpdate,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerRegisterSchema:
|
||||
"""Test CustomerRegister schema validation."""
|
||||
|
||||
def test_valid_registration(self):
|
||||
"""Test valid registration data."""
|
||||
customer = CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Password123",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert customer.email == "customer@example.com"
|
||||
assert customer.first_name == "John"
|
||||
assert customer.last_name == "Doe"
|
||||
|
||||
def test_email_normalized_to_lowercase(self):
|
||||
"""Test email is normalized to lowercase."""
|
||||
customer = CustomerRegister(
|
||||
email="Customer@Example.COM",
|
||||
password="Password123",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert customer.email == "customer@example.com"
|
||||
|
||||
def test_invalid_email(self):
|
||||
"""Test invalid email raises ValidationError."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerRegister(
|
||||
email="not-an-email",
|
||||
password="Password123",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert "email" in str(exc_info.value).lower()
|
||||
|
||||
def test_password_min_length(self):
|
||||
"""Test password must be at least 8 characters."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Pass1",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert "password" in str(exc_info.value).lower()
|
||||
|
||||
def test_password_requires_digit(self):
|
||||
"""Test password must contain at least one digit."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Password",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert "digit" in str(exc_info.value).lower()
|
||||
|
||||
def test_password_requires_letter(self):
|
||||
"""Test password must contain at least one letter."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="12345678",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert "letter" in str(exc_info.value).lower()
|
||||
|
||||
def test_first_name_required(self):
|
||||
"""Test first_name is required."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Password123",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert "first_name" in str(exc_info.value).lower()
|
||||
|
||||
def test_marketing_consent_default(self):
|
||||
"""Test marketing_consent defaults to False."""
|
||||
customer = CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Password123",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
)
|
||||
assert customer.marketing_consent is False
|
||||
|
||||
def test_optional_phone(self):
|
||||
"""Test optional phone field."""
|
||||
customer = CustomerRegister(
|
||||
email="customer@example.com",
|
||||
password="Password123",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
phone="+352 123 456",
|
||||
)
|
||||
assert customer.phone == "+352 123 456"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerUpdateSchema:
|
||||
"""Test CustomerUpdate schema validation."""
|
||||
|
||||
def test_partial_update(self):
|
||||
"""Test partial update with only some fields."""
|
||||
update = CustomerUpdate(first_name="Jane")
|
||||
assert update.first_name == "Jane"
|
||||
assert update.last_name is None
|
||||
assert update.email is None
|
||||
|
||||
def test_empty_update_is_valid(self):
|
||||
"""Test empty update is valid."""
|
||||
update = CustomerUpdate()
|
||||
assert update.model_dump(exclude_unset=True) == {}
|
||||
|
||||
def test_email_normalized_to_lowercase(self):
|
||||
"""Test email is normalized to lowercase."""
|
||||
update = CustomerUpdate(email="NewEmail@Example.COM")
|
||||
assert update.email == "newemail@example.com"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerResponseSchema:
|
||||
"""Test CustomerResponse schema."""
|
||||
|
||||
def test_from_dict(self):
|
||||
"""Test creating response from dict."""
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
data = {
|
||||
"id": 1,
|
||||
"store_id": 1,
|
||||
"email": "customer@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"phone": None,
|
||||
"customer_number": "CUST001",
|
||||
"marketing_consent": False,
|
||||
"preferred_language": "fr",
|
||||
"last_order_date": None,
|
||||
"total_orders": 5,
|
||||
"total_spent": Decimal("500.00"),
|
||||
"is_active": True,
|
||||
"created_at": datetime.now(),
|
||||
"updated_at": datetime.now(),
|
||||
}
|
||||
response = CustomerResponse(**data)
|
||||
assert response.id == 1
|
||||
assert response.customer_number == "CUST001"
|
||||
assert response.total_orders == 5
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerAddressCreateSchema:
|
||||
"""Test CustomerAddressCreate schema validation."""
|
||||
|
||||
def test_valid_shipping_address(self):
|
||||
"""Test valid shipping address creation."""
|
||||
address = CustomerAddressCreate(
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
assert address.address_type == "shipping"
|
||||
assert address.city == "Luxembourg"
|
||||
|
||||
def test_valid_billing_address(self):
|
||||
"""Test valid billing address creation."""
|
||||
address = CustomerAddressCreate(
|
||||
address_type="billing",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
assert address.address_type == "billing"
|
||||
|
||||
def test_invalid_address_type(self):
|
||||
"""Test invalid address_type raises ValidationError."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CustomerAddressCreate(
|
||||
address_type="delivery",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
assert "address_type" in str(exc_info.value).lower()
|
||||
|
||||
def test_is_default_defaults_to_false(self):
|
||||
"""Test is_default defaults to False."""
|
||||
address = CustomerAddressCreate(
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
assert address.is_default is False
|
||||
|
||||
def test_optional_address_line_2(self):
|
||||
"""Test optional address_line_2 field."""
|
||||
address = CustomerAddressCreate(
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
address_line_1="123 Main St",
|
||||
address_line_2="Apt 4B",
|
||||
city="Luxembourg",
|
||||
postal_code="L-1234",
|
||||
country_name="Luxembourg",
|
||||
country_iso="LU",
|
||||
)
|
||||
assert address.address_line_2 == "Apt 4B"
|
||||
|
||||
def test_required_fields(self):
|
||||
"""Test required fields."""
|
||||
with pytest.raises(ValidationError):
|
||||
CustomerAddressCreate(
|
||||
address_type="shipping",
|
||||
first_name="John",
|
||||
# missing last_name, address_line_1, city, postal_code, country
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerAddressUpdateSchema:
|
||||
"""Test CustomerAddressUpdate schema validation."""
|
||||
|
||||
def test_partial_update(self):
|
||||
"""Test partial update with only some fields."""
|
||||
update = CustomerAddressUpdate(city="Esch-sur-Alzette")
|
||||
assert update.city == "Esch-sur-Alzette"
|
||||
assert update.first_name is None
|
||||
|
||||
def test_empty_update_is_valid(self):
|
||||
"""Test empty update is valid."""
|
||||
update = CustomerAddressUpdate()
|
||||
assert update.model_dump(exclude_unset=True) == {}
|
||||
|
||||
def test_address_type_validation(self):
|
||||
"""Test address_type validation in update."""
|
||||
with pytest.raises(ValidationError):
|
||||
CustomerAddressUpdate(address_type="invalid")
|
||||
|
||||
def test_valid_address_type_update(self):
|
||||
"""Test valid address_type values in update."""
|
||||
shipping = CustomerAddressUpdate(address_type="shipping")
|
||||
billing = CustomerAddressUpdate(address_type="billing")
|
||||
assert shipping.address_type == "shipping"
|
||||
assert billing.address_type == "billing"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerAddressResponseSchema:
|
||||
"""Test CustomerAddressResponse schema."""
|
||||
|
||||
def test_from_dict(self):
|
||||
"""Test creating response from dict."""
|
||||
from datetime import datetime
|
||||
|
||||
data = {
|
||||
"id": 1,
|
||||
"store_id": 1,
|
||||
"customer_id": 1,
|
||||
"address_type": "shipping",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"company": None,
|
||||
"address_line_1": "123 Main St",
|
||||
"address_line_2": None,
|
||||
"city": "Luxembourg",
|
||||
"postal_code": "L-1234",
|
||||
"country_name": "Luxembourg",
|
||||
"country_iso": "LU",
|
||||
"is_default": True,
|
||||
"created_at": datetime.now(),
|
||||
"updated_at": datetime.now(),
|
||||
}
|
||||
response = CustomerAddressResponse(**data)
|
||||
assert response.id == 1
|
||||
assert response.is_default is True
|
||||
assert response.address_type == "shipping"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.schema
|
||||
class TestCustomerPreferencesUpdateSchema:
|
||||
"""Test CustomerPreferencesUpdate schema validation."""
|
||||
|
||||
def test_partial_update(self):
|
||||
"""Test partial update with only some fields."""
|
||||
update = CustomerPreferencesUpdate(marketing_consent=True)
|
||||
assert update.marketing_consent is True
|
||||
assert update.preferred_language is None
|
||||
|
||||
def test_language_update(self):
|
||||
"""Test language preference update."""
|
||||
update = CustomerPreferencesUpdate(preferred_language="fr")
|
||||
assert update.preferred_language == "fr"
|
||||
|
||||
def test_currency_update(self):
|
||||
"""Test currency preference update."""
|
||||
update = CustomerPreferencesUpdate(currency="EUR")
|
||||
assert update.currency == "EUR"
|
||||
|
||||
def test_notification_preferences(self):
|
||||
"""Test notification preferences dict."""
|
||||
update = CustomerPreferencesUpdate(
|
||||
notification_preferences={
|
||||
"email": True,
|
||||
"sms": False,
|
||||
"push": True,
|
||||
}
|
||||
)
|
||||
assert update.notification_preferences["email"] is True
|
||||
assert update.notification_preferences["sms"] is False
|
||||
Reference in New Issue
Block a user