Some checks failed
Move all auth schemas (UserContext, UserLogin, LoginResponse, etc.) from legacy models/schema/auth.py to app/modules/tenancy/schemas/auth.py per MOD-019. Update 84 import sites across 14 modules. Legacy file now re-exports for backwards compatibility. Add missing tenancy service methods for cross-module consumers: - merchant_service.get_merchant_by_owner_id() - merchant_service.get_merchant_count_for_owner() - admin_service.get_user_by_id() (public, was private-only) - platform_service.get_active_store_count() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
277 lines
7.4 KiB
Python
277 lines
7.4 KiB
Python
# app/modules/loyalty/tests/conftest.py
|
|
"""
|
|
Module-specific fixtures for loyalty tests.
|
|
Core fixtures (db, client, etc.) are inherited from the root conftest.py.
|
|
"""
|
|
|
|
import uuid
|
|
from datetime import UTC, datetime
|
|
|
|
import pytest
|
|
|
|
from app.api.deps import get_current_merchant_api, get_merchant_for_current_user
|
|
from app.modules.loyalty.models import LoyaltyCard, LoyaltyProgram
|
|
from app.modules.loyalty.models.loyalty_program import LoyaltyType
|
|
from app.modules.tenancy.models import Merchant, Platform, Store, User
|
|
from app.modules.tenancy.models.store import StoreUser
|
|
from app.modules.tenancy.models.store_platform import StorePlatform
|
|
from app.modules.tenancy.schemas.auth import UserContext
|
|
from main import app
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_platform(db):
|
|
"""Create a platform for loyalty store tests."""
|
|
platform = Platform(
|
|
code=f"loyalty_{uuid.uuid4().hex[:8]}",
|
|
name="Loyalty Test Platform",
|
|
is_active=True,
|
|
)
|
|
db.add(platform)
|
|
db.commit()
|
|
db.refresh(platform)
|
|
return platform
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_store_setup(db, loyalty_platform):
|
|
"""
|
|
Full store setup for loyalty integration tests.
|
|
|
|
Creates: User -> Merchant -> Store -> StoreUser -> StorePlatform
|
|
+ LoyaltyProgram + LoyaltyCard (with customer)
|
|
|
|
Returns dict with all objects for easy access.
|
|
"""
|
|
from app.modules.customers.models.customer import Customer
|
|
from middleware.auth import AuthManager
|
|
|
|
auth = AuthManager()
|
|
uid = uuid.uuid4().hex[:8]
|
|
|
|
# Owner user
|
|
owner = User(
|
|
email=f"loyaltyowner_{uid}@test.com",
|
|
username=f"loyaltyowner_{uid}",
|
|
hashed_password=auth.hash_password("storepass123"),
|
|
role="merchant_owner",
|
|
is_active=True,
|
|
is_email_verified=True,
|
|
)
|
|
db.add(owner)
|
|
db.commit()
|
|
db.refresh(owner)
|
|
|
|
# Merchant
|
|
merchant = Merchant(
|
|
name=f"Loyalty Merchant {uid}",
|
|
owner_user_id=owner.id,
|
|
contact_email=owner.email,
|
|
is_active=True,
|
|
is_verified=True,
|
|
)
|
|
db.add(merchant)
|
|
db.commit()
|
|
db.refresh(merchant)
|
|
|
|
# Store
|
|
store = Store(
|
|
merchant_id=merchant.id,
|
|
store_code=f"LOYTEST_{uid.upper()}",
|
|
subdomain=f"loytest{uid}",
|
|
name=f"Loyalty Test Store {uid}",
|
|
is_active=True,
|
|
is_verified=True,
|
|
)
|
|
db.add(store)
|
|
db.commit()
|
|
db.refresh(store)
|
|
|
|
# StoreUser
|
|
store_user = StoreUser(
|
|
store_id=store.id,
|
|
user_id=owner.id,
|
|
is_active=True,
|
|
)
|
|
db.add(store_user)
|
|
db.commit()
|
|
|
|
# StorePlatform
|
|
sp = StorePlatform(
|
|
store_id=store.id,
|
|
platform_id=loyalty_platform.id,
|
|
)
|
|
db.add(sp)
|
|
db.commit()
|
|
|
|
# Customer
|
|
customer = Customer(
|
|
email=f"customer_{uid}@test.com",
|
|
first_name="Test",
|
|
last_name="Customer",
|
|
hashed_password="!test!unused", # noqa: SEC-001
|
|
customer_number=f"CUST-{uid.upper()}",
|
|
store_id=store.id,
|
|
is_active=True,
|
|
)
|
|
db.add(customer)
|
|
db.commit()
|
|
db.refresh(customer)
|
|
|
|
# Loyalty Program
|
|
program = LoyaltyProgram(
|
|
merchant_id=merchant.id,
|
|
loyalty_type=LoyaltyType.POINTS.value,
|
|
points_per_euro=10,
|
|
welcome_bonus_points=50,
|
|
minimum_redemption_points=100,
|
|
minimum_purchase_cents=0,
|
|
points_expiration_days=365,
|
|
cooldown_minutes=0,
|
|
max_daily_stamps=10,
|
|
require_staff_pin=False,
|
|
card_name="Test Rewards",
|
|
card_color="#4F46E5",
|
|
is_active=True,
|
|
points_rewards=[
|
|
{"id": "reward_1", "name": "€5 off", "points_required": 100, "is_active": True},
|
|
],
|
|
)
|
|
db.add(program)
|
|
db.commit()
|
|
db.refresh(program)
|
|
|
|
# Loyalty Card
|
|
card = LoyaltyCard(
|
|
merchant_id=merchant.id,
|
|
program_id=program.id,
|
|
customer_id=customer.id,
|
|
enrolled_at_store_id=store.id,
|
|
card_number=f"TESTCARD-{uid.upper()}",
|
|
points_balance=100,
|
|
total_points_earned=150,
|
|
points_redeemed=50,
|
|
is_active=True,
|
|
last_activity_at=datetime.now(UTC),
|
|
)
|
|
db.add(card)
|
|
db.commit()
|
|
db.refresh(card)
|
|
|
|
return {
|
|
"owner": owner,
|
|
"merchant": merchant,
|
|
"store": store,
|
|
"platform": loyalty_platform,
|
|
"customer": customer,
|
|
"program": program,
|
|
"card": card,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_merchant_setup(db, loyalty_platform):
|
|
"""
|
|
Merchant-only setup for loyalty integration tests (no program).
|
|
|
|
Creates: User -> Merchant (no program yet).
|
|
Use this for testing program creation via merchant API.
|
|
"""
|
|
from middleware.auth import AuthManager
|
|
|
|
auth = AuthManager()
|
|
uid = uuid.uuid4().hex[:8]
|
|
|
|
owner = User(
|
|
email=f"merchowner_{uid}@test.com",
|
|
username=f"merchowner_{uid}",
|
|
hashed_password=auth.hash_password("merchpass123"),
|
|
role="merchant_owner",
|
|
is_active=True,
|
|
is_email_verified=True,
|
|
)
|
|
db.add(owner)
|
|
db.commit()
|
|
db.refresh(owner)
|
|
|
|
merchant = Merchant(
|
|
name=f"Loyalty Merchant {uid}",
|
|
owner_user_id=owner.id,
|
|
contact_email=owner.email,
|
|
is_active=True,
|
|
is_verified=True,
|
|
)
|
|
db.add(merchant)
|
|
db.commit()
|
|
db.refresh(merchant)
|
|
|
|
return {
|
|
"owner": owner,
|
|
"merchant": merchant,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_merchant_headers(loyalty_store_setup):
|
|
"""
|
|
Override auth dependencies to return merchant/user for the merchant owner.
|
|
Uses the full loyalty_store_setup which includes a program.
|
|
"""
|
|
owner = loyalty_store_setup["owner"]
|
|
merchant = loyalty_store_setup["merchant"]
|
|
|
|
user_context = UserContext(
|
|
id=owner.id,
|
|
email=owner.email,
|
|
username=owner.username,
|
|
role="merchant_owner",
|
|
is_active=True,
|
|
)
|
|
|
|
app.dependency_overrides[get_merchant_for_current_user] = lambda: merchant
|
|
app.dependency_overrides[get_current_merchant_api] = lambda: user_context
|
|
yield {"Authorization": "Bearer fake-token"}
|
|
app.dependency_overrides.pop(get_merchant_for_current_user, None)
|
|
app.dependency_overrides.pop(get_current_merchant_api, None)
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_merchant_headers_no_program(loyalty_merchant_setup):
|
|
"""
|
|
Override auth dependencies for a merchant that has no program yet.
|
|
"""
|
|
owner = loyalty_merchant_setup["owner"]
|
|
merchant = loyalty_merchant_setup["merchant"]
|
|
|
|
user_context = UserContext(
|
|
id=owner.id,
|
|
email=owner.email,
|
|
username=owner.username,
|
|
role="merchant_owner",
|
|
is_active=True,
|
|
)
|
|
|
|
app.dependency_overrides[get_merchant_for_current_user] = lambda: merchant
|
|
app.dependency_overrides[get_current_merchant_api] = lambda: user_context
|
|
yield {"Authorization": "Bearer fake-token"}
|
|
app.dependency_overrides.pop(get_merchant_for_current_user, None)
|
|
app.dependency_overrides.pop(get_current_merchant_api, None)
|
|
|
|
|
|
@pytest.fixture
|
|
def loyalty_store_headers(client, loyalty_store_setup):
|
|
"""
|
|
Get real JWT auth headers by logging in via store auth endpoint.
|
|
"""
|
|
owner = loyalty_store_setup["owner"]
|
|
response = client.post(
|
|
"/api/v1/store/auth/login",
|
|
json={
|
|
"email_or_username": owner.username,
|
|
"password": "storepass123",
|
|
},
|
|
)
|
|
assert response.status_code == 200, f"Store login failed: {response.text}"
|
|
token = response.json()["access_token"]
|
|
return {"Authorization": f"Bearer {token}"}
|