test: add tests for merchant dashboard metrics and fix invoice template location
Move invoice PDF template from app/templates/invoices/ to app/modules/orders/templates/invoices/ where InvoicePDFService expects it. Expand invoice PDF tests to validate template path and existence. Add unit tests for get_merchant_metrics() in tenancy, billing, and customer metrics providers. Add unit tests for StatsAggregatorService merchant methods. Add integration tests for the merchant dashboard stats endpoint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
149
app/modules/billing/tests/unit/test_billing_metrics.py
Normal file
149
app/modules/billing/tests/unit/test_billing_metrics.py
Normal file
@@ -0,0 +1,149 @@
|
||||
# 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):
|
||||
p = Platform(
|
||||
code=f"bm_extra_{uuid.uuid4().hex[:8]}",
|
||||
name=f"Extra Platform {i}",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(p)
|
||||
platforms.append(p)
|
||||
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
|
||||
Reference in New Issue
Block a user