# app/modules/billing/tests/unit/test_billing_metrics.py """Unit tests for BillingMetricsProvider.get_merchant_metrics.""" import uuid from datetime import UTC, datetime, timedelta import pytest from app.modules.billing.models import ( MerchantSubscription, SubscriptionStatus, SubscriptionTier, ) from app.modules.billing.services.billing_metrics import BillingMetricsProvider from app.modules.tenancy.models import Merchant, Platform, User @pytest.fixture def billing_platform(db): """Create a platform for billing metrics tests.""" platform = Platform( code=f"bm_{uuid.uuid4().hex[:8]}", name="Billing Metrics Platform", is_active=True, ) db.add(platform) db.commit() db.refresh(platform) return platform @pytest.fixture def billing_merchant(db): """Create a merchant for billing metrics tests.""" from middleware.auth import AuthManager auth = AuthManager() user = User( email=f"billowner_{uuid.uuid4().hex[:8]}@test.com", username=f"billowner_{uuid.uuid4().hex[:8]}", hashed_password=auth.hash_password("pass123"), role="merchant_owner", is_active=True, ) db.add(user) db.flush() merchant = Merchant( name="Billing 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 billing_tier(db, billing_platform): """Create a subscription tier.""" tier = SubscriptionTier( code="professional", name="Professional", description="Pro tier", price_monthly_cents=2900, price_annual_cents=29000, display_order=1, is_active=True, is_public=True, platform_id=billing_platform.id, ) db.add(tier) db.commit() db.refresh(tier) return tier @pytest.fixture def billing_extra_platforms(db): """Create additional platforms for multiple subscriptions (unique constraint: merchant+platform).""" platforms = [] for i in range(2): platforms.append(Platform( code=f"bm_extra_{uuid.uuid4().hex[:8]}", name=f"Extra Platform {i}", is_active=True, )) db.add_all(platforms) db.commit() for p in platforms: db.refresh(p) return platforms @pytest.fixture def billing_subscriptions(db, billing_merchant, billing_platform, billing_tier, billing_extra_platforms): """Create subscriptions: 1 active, 1 trial, 1 cancelled (each on a different platform).""" platforms = [billing_platform, billing_extra_platforms[0], billing_extra_platforms[1]] subs = [] for status, platform in zip( [SubscriptionStatus.ACTIVE, SubscriptionStatus.TRIAL, SubscriptionStatus.CANCELLED], platforms, strict=False, ): sub = MerchantSubscription( merchant_id=billing_merchant.id, platform_id=platform.id, tier_id=billing_tier.id, status=status.value, is_annual=False, period_start=datetime.now(UTC), period_end=datetime.now(UTC) + timedelta(days=30), ) db.add(sub) subs.append(sub) db.commit() for s in subs: db.refresh(s) return subs @pytest.mark.unit @pytest.mark.billing class TestBillingMetricsProviderMerchant: """Tests for BillingMetricsProvider.get_merchant_metrics.""" def setup_method(self): self.provider = BillingMetricsProvider() def test_active_subscriptions_count(self, db, billing_merchant, billing_subscriptions): """Counts active + trial subscriptions, excludes cancelled.""" metrics = self.provider.get_merchant_metrics(db, billing_merchant.id) by_key = {m.key: m.value for m in metrics} assert by_key["billing.active_subscriptions"] == 2 def test_no_subscriptions(self, db, billing_merchant): """Returns zero when merchant has no subscriptions.""" metrics = self.provider.get_merchant_metrics(db, billing_merchant.id) by_key = {m.key: m.value for m in metrics} assert by_key["billing.active_subscriptions"] == 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["billing.active_subscriptions"] == 0