- Fix IPv6 host parsing with _strip_port() utility - Remove dangerous StorePlatform→Store.subdomain silent fallback - Close storefront gate bypass when frontend_type is None - Add custom subdomain management UI and API for stores - Add domain health diagnostic tool - Convert db.add() in loops to db.add_all() (24 PERF-006 fixes) - Add tests for all new functionality (18 subdomain service tests) - Add .github templates for validator compliance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
3.9 KiB
Python
127 lines
3.9 KiB
Python
# app/modules/customers/tests/unit/test_customer_metrics.py
|
|
"""Unit tests for CustomerMetricsProvider.get_merchant_metrics."""
|
|
|
|
import uuid
|
|
|
|
import pytest
|
|
|
|
from app.modules.customers.models.customer import Customer
|
|
from app.modules.customers.services.customer_metrics import CustomerMetricsProvider
|
|
from app.modules.tenancy.models import Merchant, Store, User
|
|
|
|
|
|
@pytest.fixture
|
|
def cust_merchant(db):
|
|
"""Create a merchant for customer metrics tests."""
|
|
from middleware.auth import AuthManager
|
|
|
|
auth = AuthManager()
|
|
user = User(
|
|
email=f"custowner_{uuid.uuid4().hex[:8]}@test.com",
|
|
username=f"custowner_{uuid.uuid4().hex[:8]}",
|
|
hashed_password=auth.hash_password("pass123"),
|
|
role="merchant_owner",
|
|
is_active=True,
|
|
)
|
|
db.add(user)
|
|
db.flush()
|
|
|
|
merchant = Merchant(
|
|
name="Customer Metrics Merchant",
|
|
owner_user_id=user.id,
|
|
contact_email=user.email,
|
|
is_active=True,
|
|
is_verified=True,
|
|
)
|
|
db.add(merchant)
|
|
db.commit()
|
|
db.refresh(merchant)
|
|
return merchant
|
|
|
|
|
|
@pytest.fixture
|
|
def cust_stores(db, cust_merchant):
|
|
"""Create 2 stores for the merchant."""
|
|
stores = []
|
|
for i in range(2):
|
|
uid = uuid.uuid4().hex[:8].upper()
|
|
stores.append(Store(
|
|
merchant_id=cust_merchant.id,
|
|
store_code=f"CSTORE_{uid}",
|
|
subdomain=f"cstore{uid.lower()}",
|
|
name=f"Cust Store {i}",
|
|
is_active=True,
|
|
is_verified=True,
|
|
))
|
|
db.add_all(stores)
|
|
db.commit()
|
|
for s in stores:
|
|
db.refresh(s)
|
|
return stores
|
|
|
|
|
|
@pytest.fixture
|
|
def cust_customers(db, cust_stores):
|
|
"""Create customers across the merchant's stores."""
|
|
customers = []
|
|
# 3 customers in store 0
|
|
for i in range(3):
|
|
uid = uuid.uuid4().hex[:8]
|
|
customers.append(Customer(
|
|
store_id=cust_stores[0].id,
|
|
email=f"cust_{uid}@test.com",
|
|
hashed_password="hashed", # noqa: SEC001
|
|
first_name=f"First{i}",
|
|
last_name=f"Last{i}",
|
|
customer_number=f"C{uid}",
|
|
is_active=True,
|
|
))
|
|
# 2 customers in store 1
|
|
for i in range(2):
|
|
uid = uuid.uuid4().hex[:8]
|
|
customers.append(Customer(
|
|
store_id=cust_stores[1].id,
|
|
email=f"cust_{uid}@test.com",
|
|
hashed_password="hashed", # noqa: SEC001
|
|
first_name=f"First{i}",
|
|
last_name=f"Last{i}",
|
|
customer_number=f"C{uid}",
|
|
is_active=True,
|
|
))
|
|
db.add_all(customers)
|
|
db.commit()
|
|
return customers
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.customers
|
|
class TestCustomerMetricsProviderMerchant:
|
|
"""Tests for CustomerMetricsProvider.get_merchant_metrics."""
|
|
|
|
def setup_method(self):
|
|
self.provider = CustomerMetricsProvider()
|
|
|
|
def test_total_customers_across_stores(self, db, cust_merchant, cust_stores, cust_customers):
|
|
"""Aggregates customers across all merchant stores."""
|
|
metrics = self.provider.get_merchant_metrics(db, cust_merchant.id)
|
|
by_key = {m.key: m.value for m in metrics}
|
|
assert by_key["customers.total"] == 5
|
|
|
|
def test_no_customers(self, db, cust_merchant, cust_stores):
|
|
"""Returns zero when stores have no customers."""
|
|
metrics = self.provider.get_merchant_metrics(db, cust_merchant.id)
|
|
by_key = {m.key: m.value for m in metrics}
|
|
assert by_key["customers.total"] == 0
|
|
|
|
def test_no_stores(self, db, cust_merchant):
|
|
"""Returns zero when merchant has no stores."""
|
|
metrics = self.provider.get_merchant_metrics(db, cust_merchant.id)
|
|
by_key = {m.key: m.value for m in metrics}
|
|
assert by_key["customers.total"] == 0
|
|
|
|
def test_nonexistent_merchant(self, db):
|
|
"""Returns zero for a non-existent merchant ID."""
|
|
metrics = self.provider.get_merchant_metrics(db, 999999)
|
|
by_key = {m.key: m.value for m in metrics}
|
|
assert by_key["customers.total"] == 0
|