153 lines
4.3 KiB
Python
153 lines
4.3 KiB
Python
"""Pytest configuration and shared fixtures for all tests."""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_hosts() -> dict[str, object]:
|
|
"""Provide mock browser host configurations for testing."""
|
|
from guide.app.core.config import BrowserHostConfig, HostKind
|
|
|
|
return {
|
|
"demo-headless": BrowserHostConfig(
|
|
id="demo-headless",
|
|
kind=HostKind.HEADLESS,
|
|
browser="chromium",
|
|
),
|
|
"demo-cdp": BrowserHostConfig(
|
|
id="demo-cdp",
|
|
kind=HostKind.CDP,
|
|
host="localhost",
|
|
port=9222,
|
|
),
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_personas() -> dict[str, object]:
|
|
"""Provide mock persona configurations for testing."""
|
|
from guide.app.models.personas.models import (
|
|
DemoPersona,
|
|
PersonaRole,
|
|
LoginMethod,
|
|
)
|
|
|
|
return {
|
|
"buyer1": DemoPersona(
|
|
id="buyer1",
|
|
role=PersonaRole.BUYER,
|
|
email="buyer1@example.com",
|
|
login_method=LoginMethod.MFA_EMAIL,
|
|
browser_host_id="demo-headless",
|
|
),
|
|
"supplier1": DemoPersona(
|
|
id="supplier1",
|
|
role=PersonaRole.SUPPLIER,
|
|
email="supplier1@example.com",
|
|
login_method=LoginMethod.MFA_EMAIL,
|
|
browser_host_id="demo-cdp",
|
|
),
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def app_settings(
|
|
mock_browser_hosts: dict[str, object],
|
|
mock_personas: dict[str, object],
|
|
) -> object:
|
|
"""Provide application settings with mock configuration.
|
|
|
|
Note: Fixtures are typed as object to avoid circular import issues.
|
|
Runtime casting is necessary because dict invariance prevents direct assignment.
|
|
"""
|
|
from typing import cast as type_cast
|
|
from guide.app.core.config import AppSettings
|
|
|
|
return AppSettings(
|
|
raindrop_base_url="https://app.raindrop.com",
|
|
raindrop_graphql_url="https://app.raindrop.com/graphql",
|
|
default_browser_host_id="demo-headless",
|
|
browser_hosts=type_cast(dict[str, object], mock_browser_hosts), # type: ignore[arg-type]
|
|
personas=type_cast(dict[str, object], mock_personas), # type: ignore[arg-type]
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def persona_store(app_settings: object) -> object:
|
|
"""Provide PersonaStore instance with mock settings.
|
|
|
|
Note: app_settings is AppSettings but typed as object to avoid circular imports.
|
|
"""
|
|
from guide.app.models.personas.store import PersonaStore
|
|
|
|
# app_settings is AppSettings at runtime but typed as object for circular import avoidance
|
|
return PersonaStore(app_settings) # type: ignore[arg-type]
|
|
|
|
|
|
@pytest.fixture
|
|
def action_registry(persona_store: object) -> object:
|
|
"""Provide ActionRegistry instance with DI context."""
|
|
from guide.app.actions.registry import ActionRegistry
|
|
|
|
registry = ActionRegistry(
|
|
di_context={
|
|
"persona_store": persona_store,
|
|
"login_url": "https://app.raindrop.com",
|
|
}
|
|
)
|
|
return registry
|
|
|
|
|
|
@pytest.fixture
|
|
def action_context(_persona_store: object) -> object:
|
|
"""Provide ActionContext instance for testing action execution.
|
|
|
|
Args:
|
|
_persona_store: Unused, but required for fixture dependency order.
|
|
"""
|
|
from guide.app.models.domain.models import ActionContext
|
|
|
|
return ActionContext(
|
|
action_id="test-action",
|
|
persona_id="buyer1",
|
|
browser_host_id="demo-headless",
|
|
correlation_id="test-correlation-123",
|
|
shared_state={},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_page() -> AsyncMock:
|
|
"""Provide mock Playwright page instance."""
|
|
page = AsyncMock()
|
|
page.goto = AsyncMock()
|
|
page.wait_for_selector = AsyncMock()
|
|
page.fill = AsyncMock()
|
|
page.click = AsyncMock()
|
|
page.screenshot = AsyncMock(return_value=b"fake-image-data")
|
|
page.content = AsyncMock(return_value="<html><body>Mock</body></html>")
|
|
page.on = MagicMock()
|
|
return page
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_client() -> AsyncMock:
|
|
"""Provide mock BrowserClient instance."""
|
|
client = AsyncMock()
|
|
client.open_page = AsyncMock()
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_pool() -> AsyncMock:
|
|
"""Provide mock BrowserPool instance."""
|
|
pool = AsyncMock()
|
|
pool.initialize = AsyncMock()
|
|
pool.close = AsyncMock()
|
|
return pool
|