Some checks failed
Covers card lookup route ordering, func.replace normalization, customer name in transactions, self-enrollment creation, and earn points endpoint. 54 tests total (was 1). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
185 lines
4.7 KiB
Python
185 lines
4.7 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.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
|
|
|
|
|
|
@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_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}"}
|