mirror of
http://88.130.71.182:3000/BlitTech/badoHair_be.git
synced 2026-06-12 23:23:22 +00:00
203 lines
6.5 KiB
Python
203 lines
6.5 KiB
Python
"""
|
|
Shared fixtures for all tests.
|
|
|
|
Strategy:
|
|
- Override `get_db` with an async generator that yields a mock asyncpg connection.
|
|
- Override `get_current_user` / `require_admin` directly with plain async functions.
|
|
- Patch `app.database.get_pool` so the lifespan startup never touches a real DB.
|
|
- Patch `app.services.stripe_service` globally so no real Stripe calls are made.
|
|
"""
|
|
import asyncio
|
|
from datetime import datetime, date, time
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
from uuid import UUID
|
|
|
|
import pytest
|
|
from httpx import AsyncClient, ASGITransport
|
|
|
|
from main import app
|
|
from app.dependencies import get_db, get_current_user, get_current_user_optional, require_admin
|
|
|
|
# ── Sample data ───────────────────────────────────────────────────────────────
|
|
|
|
USER_ID = "11111111-1111-1111-1111-111111111111"
|
|
ADMIN_ID = "22222222-2222-2222-2222-222222222222"
|
|
PRODUCT_ID = "33333333-3333-3333-3333-333333333333"
|
|
SLOT_ID = "44444444-4444-4444-4444-444444444444"
|
|
BOOKING_ID = "55555555-5555-5555-5555-555555555555"
|
|
ORDER_ID = "66666666-6666-6666-6666-666666666666"
|
|
|
|
SAMPLE_USER = {
|
|
"id": USER_ID,
|
|
"email": "user@test.com",
|
|
"full_name": "Test User",
|
|
"phone": "+49123456789",
|
|
"role": "client",
|
|
"is_blocked": False,
|
|
}
|
|
|
|
SAMPLE_ADMIN = {
|
|
"id": ADMIN_ID,
|
|
"email": "admin@test.com",
|
|
"full_name": "Admin User",
|
|
"phone": None,
|
|
"role": "admin",
|
|
"is_blocked": False,
|
|
}
|
|
|
|
SAMPLE_PRODUCT = {
|
|
"id": UUID(PRODUCT_ID),
|
|
"name": "Extensions Clip-In Luxe",
|
|
"description": "Top quality",
|
|
"price": 189.0,
|
|
"original_price": 229.0,
|
|
"category": "clip-in",
|
|
"images": ["https://example.com/img.jpg"],
|
|
"_raw_images": ["https://example.com/img.jpg"],
|
|
"image": "https://example.com/img.jpg",
|
|
"colors": ["Black", "Brown"],
|
|
"lengths": ["40cm", "50cm"],
|
|
"features": ["100% Remy"],
|
|
"stock_quantity": 10,
|
|
"is_featured": False,
|
|
"is_hidden": False,
|
|
"is_new": True,
|
|
"is_bestseller": True,
|
|
"rating": 4.8,
|
|
"review_count": 124,
|
|
"created_at": datetime(2026, 1, 1),
|
|
"updated_at": datetime(2026, 1, 1),
|
|
}
|
|
|
|
SAMPLE_SLOT = {
|
|
"id": UUID(SLOT_ID),
|
|
"date": date(2026, 6, 1),
|
|
"start_time": time(10, 0),
|
|
"end_time": time(11, 0),
|
|
"is_blocked": False,
|
|
"block_reason": None,
|
|
"is_booked": False,
|
|
}
|
|
|
|
SAMPLE_BOOKING = {
|
|
"id": UUID(BOOKING_ID),
|
|
"user_id": UUID(USER_ID),
|
|
"slot_id": UUID(SLOT_ID),
|
|
"slot_date": date(2026, 6, 1),
|
|
"slot_start": "10:00",
|
|
"slot_end": "11:00",
|
|
"service_note": "Box braids",
|
|
"client_name": "Test User",
|
|
"client_email": "user@test.com",
|
|
"client_phone": "+49123456789",
|
|
"status": "confirmed",
|
|
"amount_paid": 50.0,
|
|
"stripe_payment_intent_id": "pi_test123",
|
|
"admin_notes": None,
|
|
"created_at": datetime(2026, 1, 1),
|
|
"updated_at": datetime(2026, 1, 1),
|
|
}
|
|
|
|
SAMPLE_SERVICE = {
|
|
"id": UUID("77777777-7777-7777-7777-777777777777"),
|
|
"name": "Pose complète",
|
|
"description": "Full extension installation",
|
|
"duration_minutes": 150,
|
|
"price": 150.0,
|
|
}
|
|
|
|
|
|
# ── Mock asyncpg transaction context manager ──────────────────────────────────
|
|
|
|
class MockTransaction:
|
|
async def __aenter__(self):
|
|
return self
|
|
async def __aexit__(self, *args):
|
|
return False
|
|
|
|
|
|
# ── DB mock factory ───────────────────────────────────────────────────────────
|
|
|
|
def make_mock_db() -> AsyncMock:
|
|
db = AsyncMock()
|
|
db.fetchrow = AsyncMock(return_value=None)
|
|
db.fetch = AsyncMock(return_value=[])
|
|
db.fetchval = AsyncMock(return_value=0)
|
|
db.execute = AsyncMock(return_value="INSERT 1")
|
|
db.transaction = MagicMock(return_value=MockTransaction())
|
|
return db
|
|
|
|
|
|
def db_override(mock_conn):
|
|
async def _override():
|
|
yield mock_conn
|
|
return _override
|
|
|
|
|
|
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
|
|
|
@pytest.fixture
|
|
def mock_db():
|
|
return make_mock_db()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def patch_pool():
|
|
"""Prevent lifespan from opening a real DB pool."""
|
|
with patch("app.database.get_pool", new_callable=AsyncMock) as m:
|
|
m.return_value = AsyncMock()
|
|
yield m
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def patch_stripe():
|
|
"""Prevent any real Stripe calls."""
|
|
intent = MagicMock()
|
|
intent.id = "pi_test123"
|
|
intent.client_secret = "pi_test123_secret"
|
|
with patch("app.services.stripe_service.create_payment_intent", new_callable=AsyncMock, return_value=intent):
|
|
with patch("app.services.stripe_service.create_refund", new_callable=AsyncMock):
|
|
with patch("app.services.stripe_service.verify_webhook") as mock_wh:
|
|
mock_wh.return_value = MagicMock(
|
|
type="payment_intent.succeeded",
|
|
get=MagicMock(return_value={
|
|
"object": {"id": "pi_test123", "metadata": {"entity_type": "order", "entity_id": ORDER_ID}}
|
|
}),
|
|
)
|
|
yield
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def patch_stripe_modify():
|
|
"""Patch the inline stripe.PaymentIntent.modify call in booking_service."""
|
|
with patch("stripe.PaymentIntent.modify", return_value=MagicMock()):
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
async def anon_client(mock_db):
|
|
app.dependency_overrides[get_db] = db_override(mock_db)
|
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
|
|
yield c
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
async def auth_client(mock_db):
|
|
app.dependency_overrides[get_db] = db_override(mock_db)
|
|
app.dependency_overrides[get_current_user] = lambda: SAMPLE_USER
|
|
app.dependency_overrides[get_current_user_optional] = lambda: SAMPLE_USER
|
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
|
|
yield c
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
async def admin_client(mock_db):
|
|
app.dependency_overrides[get_db] = db_override(mock_db)
|
|
app.dependency_overrides[get_current_user] = lambda: SAMPLE_ADMIN
|
|
app.dependency_overrides[require_admin] = lambda: SAMPLE_ADMIN
|
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
|
|
yield c
|
|
app.dependency_overrides.clear()
|