"""Shared fixtures: file-backed SQLite DB per test, console email/SMS capture, seeded tenant, fake partner wiring.""" from __future__ import annotations import os import uuid os.environ.setdefault("GOGO_ENV", "test") os.environ.setdefault("GOGO_SECRET_KEY", "test-secret-key-for-tests-only") os.environ.setdefault("GOGO_BASE_URL", "http://testserver") import httpx # noqa: E402 import pytest # noqa: E402 import gogo.db as db # noqa: E402 from gogo.config import get_settings # noqa: E402 from gogo.crypto import encrypt # noqa: E402 from gogo.email.sender import ConsoleEmailProvider, set_email_provider # noqa: E402 from gogo.hours import DEFAULT_WORKING_HOURS # noqa: E402 from gogo.models import ProviderConfig, Service, Tenant # noqa: E402 from gogo.scheduling.mock import MockProvider # noqa: E402 from gogo.scheduling.partner_api import PartnerApiProvider # noqa: E402 from gogo.sms.base import ConsoleSmsProvider, set_sms_provider # noqa: E402 from tests import fake_partner # noqa: E402 @pytest.fixture async def session(tmp_path): """Fresh file-backed SQLite DB; the app's get_session sees the same file.""" db_path = tmp_path / "test.db" os.environ["GOGO_DATABASE_URL"] = f"sqlite+aiosqlite:///{db_path}" get_settings.cache_clear() db.reset_engine() engine = db.get_engine() async with engine.begin() as conn: await conn.run_sync(db.Base.metadata.create_all) async with db.get_sessionmaker()() as s: yield s await engine.dispose() db.reset_engine() @pytest.fixture def emails(): provider = ConsoleEmailProvider() set_email_provider(provider) yield provider.sent set_email_provider(None) @pytest.fixture def sms(): provider = ConsoleSmsProvider() set_sms_provider(provider) yield provider.sent set_sms_provider(None) @pytest.fixture def clean_mock_provider(): MockProvider.busy_blocks = {} MockProvider.now_override = None yield MockProvider MockProvider.busy_blocks = {} MockProvider.now_override = None @pytest.fixture async def tenant(session) -> Tenant: """Seeded mock-provider tenant 'Salon Merima' with two services.""" t = Tenant( name="Salon Merima", slug="salon-merima", city="Banja Luka", working_hours=DEFAULT_WORKING_HOURS, scheduling_provider="mock", notify_emails=["merima@example.ba"], ) session.add(t) await session.flush() session.add_all( [ Service( tenant_id=t.id, name="Šišanje i feniranje", duration_min=45, price_min=25, price_max=35, ), Service( tenant_id=t.id, name="Pedikir", duration_min=60, price_min=30, price_max=30, home_visit=True, ), ] ) await session.commit() return t @pytest.fixture async def partner_tenant(session) -> Tenant: """Tenant wired to the fake partner API via ASGI transport.""" t = Tenant( name="Salon Aida", slug="salon-aida", city="Sarajevo", working_hours=DEFAULT_WORKING_HOURS, scheduling_provider="partner_api", notify_emails=[], ) session.add(t) await session.flush() session.add( ProviderConfig( tenant_id=t.id, provider_type="partner_api", config={ "base_url": "http://partner.test", "api_key_encrypted": encrypt(fake_partner.API_KEY), "webhook_secret_encrypted": encrypt(fake_partner.WEBHOOK_SECRET), "catalog_sync": True, "email_to_owner": False, "polling_fallback": False, }, ) ) session.add( Service( tenant_id=t.id, name="Manikir", duration_min=45, price_min=20, price_max=25, partner_service_id="SRV-12", ) ) await session.commit() return t @pytest.fixture def wire_fake_partner(monkeypatch): """Point PartnerApiProvider's HTTP client at the fake partner ASGI app.""" fake_partner.state.reset() def factory(timeout): return httpx.AsyncClient( transport=httpx.ASGITransport(app=fake_partner.app), base_url="http://partner.test", timeout=timeout, ) monkeypatch.setattr(PartnerApiProvider, "client_factory", staticmethod(factory)) yield fake_partner.state fake_partner.state.reset() @pytest.fixture async def gogo_client(session): """HTTP client against the Gogo app (shares the test DB via GOGO_DATABASE_URL).""" from gogo.main import create_app app = create_app() async with httpx.AsyncClient( transport=httpx.ASGITransport(app=app), base_url="http://testserver" ) as client: yield client def make_uuid() -> str: return str(uuid.uuid4())