# 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}"}