|
| 1 | +"""E2E test configuration and fixtures. |
| 2 | +
|
| 3 | +These tests make real API calls against the ScrapeBadger API. |
| 4 | +Requires SCRAPEBADGER_API_KEY and SCRAPEBADGER_BASE_URL environment variables. |
| 5 | +""" |
| 6 | + |
| 7 | +from __future__ import annotations |
| 8 | + |
| 9 | +import os |
| 10 | +from dataclasses import dataclass |
| 11 | +from typing import TYPE_CHECKING |
| 12 | + |
| 13 | +import pytest |
| 14 | + |
| 15 | +from scrapebadger import ScrapeBadger |
| 16 | + |
| 17 | +if TYPE_CHECKING: |
| 18 | + from collections.abc import AsyncGenerator |
| 19 | + |
| 20 | + |
| 21 | +def pytest_configure(config: pytest.Config) -> None: |
| 22 | + """Register custom markers.""" |
| 23 | + config.addinivalue_line( |
| 24 | + "markers", |
| 25 | + "e2e: mark test as end-to-end test requiring real API access", |
| 26 | + ) |
| 27 | + |
| 28 | + |
| 29 | +def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None: |
| 30 | + """Add e2e marker to all tests in e2e directory.""" |
| 31 | + for item in items: |
| 32 | + if "e2e" in str(item.fspath): |
| 33 | + item.add_marker(pytest.mark.e2e) |
| 34 | + |
| 35 | + |
| 36 | +@dataclass(frozen=True) |
| 37 | +class E2ETestData: |
| 38 | + """Test data for e2e tests. |
| 39 | +
|
| 40 | + Uses stable public accounts and resources that are unlikely to change. |
| 41 | + Override via environment variables if needed. |
| 42 | + """ |
| 43 | + |
| 44 | + # User test data |
| 45 | + username: str = "X" # Twitter/X official account (stable) |
| 46 | + username_alt: str = "elonmusk" # Elon Musk (active, stable) |
| 47 | + user_id: str = "783214" # @X user ID |
| 48 | + |
| 49 | + # Tweet test data - use recent tweets from stable accounts |
| 50 | + # These may need periodic updates if tweets are deleted |
| 51 | + tweet_id: str = os.environ.get("TEST_TWEET_ID", "1802331592918618529") |
| 52 | + |
| 53 | + # List test data - use public lists |
| 54 | + list_id: str = os.environ.get("TEST_LIST_ID", "1736495155853967360") |
| 55 | + |
| 56 | + # Community test data |
| 57 | + community_id: str = os.environ.get("TEST_COMMUNITY_ID", "1493016274714259462") |
| 58 | + |
| 59 | + # Geo test data |
| 60 | + place_id: str = "5a110d312052166f" # San Francisco |
| 61 | + lat: float = 37.7749 |
| 62 | + long: float = -122.4194 |
| 63 | + |
| 64 | + # Trend test data |
| 65 | + woeid_us: int = 23424977 # United States |
| 66 | + woeid_worldwide: int = 1 # Worldwide |
| 67 | + |
| 68 | + @classmethod |
| 69 | + def from_env(cls) -> E2ETestData: |
| 70 | + """Create test data from environment variables with fallbacks.""" |
| 71 | + return cls( |
| 72 | + username=os.environ.get("TEST_USERNAME", cls.username), |
| 73 | + username_alt=os.environ.get("TEST_USERNAME_ALT", cls.username_alt), |
| 74 | + user_id=os.environ.get("TEST_USER_ID", cls.user_id), |
| 75 | + tweet_id=os.environ.get("TEST_TWEET_ID", cls.tweet_id), |
| 76 | + list_id=os.environ.get("TEST_LIST_ID", cls.list_id), |
| 77 | + community_id=os.environ.get("TEST_COMMUNITY_ID", cls.community_id), |
| 78 | + place_id=os.environ.get("TEST_PLACE_ID", cls.place_id), |
| 79 | + ) |
| 80 | + |
| 81 | + |
| 82 | +@pytest.fixture(scope="session") |
| 83 | +def api_key() -> str: |
| 84 | + """Get API key from environment.""" |
| 85 | + key = os.environ.get("SCRAPEBADGER_API_KEY") |
| 86 | + if not key: |
| 87 | + pytest.skip("SCRAPEBADGER_API_KEY environment variable not set") |
| 88 | + return key |
| 89 | + |
| 90 | + |
| 91 | +@pytest.fixture(scope="session") |
| 92 | +def base_url() -> str: |
| 93 | + """Get base URL from environment.""" |
| 94 | + return os.environ.get("SCRAPEBADGER_BASE_URL", "https://scrapebadger.com") |
| 95 | + |
| 96 | + |
| 97 | +@pytest.fixture(scope="session") |
| 98 | +def test_data() -> E2ETestData: |
| 99 | + """Get test data with environment variable overrides.""" |
| 100 | + return E2ETestData.from_env() |
| 101 | + |
| 102 | + |
| 103 | +@pytest.fixture |
| 104 | +async def client(api_key: str, base_url: str) -> AsyncGenerator[ScrapeBadger, None]: |
| 105 | + """Create a ScrapeBadger client for e2e tests. |
| 106 | +
|
| 107 | + This fixture creates a new client for each test to ensure isolation. |
| 108 | + """ |
| 109 | + async with ScrapeBadger( |
| 110 | + api_key=api_key, |
| 111 | + base_url=base_url, |
| 112 | + timeout=60.0, # Longer timeout for e2e tests |
| 113 | + max_retries=2, |
| 114 | + ) as client: |
| 115 | + yield client |
| 116 | + |
| 117 | + |
| 118 | +@pytest.fixture(scope="session") |
| 119 | +async def session_client(api_key: str, base_url: str) -> AsyncGenerator[ScrapeBadger, None]: |
| 120 | + """Create a session-scoped ScrapeBadger client. |
| 121 | +
|
| 122 | + Use this for tests that can share a client to reduce connection overhead. |
| 123 | + """ |
| 124 | + async with ScrapeBadger( |
| 125 | + api_key=api_key, |
| 126 | + base_url=base_url, |
| 127 | + timeout=60.0, |
| 128 | + max_retries=2, |
| 129 | + ) as client: |
| 130 | + yield client |
0 commit comments