CI / lint-and-test (push) Has been cancelled
- Remove multi-marketplace from Phase 3 - Add FastAPI web UI on port 8766 with basic auth - Add 6 Jinja2 templates (dashboard, keywords, users, ads, stats) - Add pytest test suite (45 tests, 49% coverage) - Add GitHub Actions CI/CD workflow - Update docker-compose.yml to expose web UI port - Update Dockerfile to include tests
85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
"""Shared pytest fixtures for the willhaben-tracker test suite."""
|
|
|
|
from datetime import datetime, timezone
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
|
|
def pytest_configure(config):
|
|
"""Enable asyncio auto mode for all async tests."""
|
|
config.addinivalue_line("markers", "asyncio: mark test as async")
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_pool():
|
|
"""Mock asyncpg.Pool with fetch/fetchrow/execute/fetchval methods."""
|
|
pool = MagicMock()
|
|
pool.fetch = AsyncMock(return_value=[])
|
|
pool.fetchrow = AsyncMock(return_value=None)
|
|
pool.execute = AsyncMock(return_value=None)
|
|
pool.fetchval = AsyncMock(return_value=None)
|
|
return pool
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_bot():
|
|
"""Mock ExtBot with send_message/send_photo methods."""
|
|
bot = MagicMock()
|
|
bot.send_message = AsyncMock(return_value=MagicMock(message_id=123))
|
|
bot.send_photo = AsyncMock(return_value=MagicMock(message_id=123))
|
|
return bot
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_ad_data():
|
|
"""Sample willhaben ad JSON dict matching the API response format."""
|
|
return {
|
|
"id": "12345678",
|
|
"description": "A used bicycle",
|
|
"attributes": {
|
|
"attribute": [
|
|
{"name": "HEADING", "values": ["Mountain Bike 2024"]},
|
|
{"name": "PRICE/AMOUNT", "values": ["250"]},
|
|
{"name": "LOCATION", "values": ["Vienna"]},
|
|
{"name": "POSTCODE", "values": ["1010"]},
|
|
{"name": "SEO_URL", "values": ["mountain-bike-2024/12345678"]},
|
|
{"name": "PUBLISHED_String", "values": ["2024-01-15T10:30:00Z"]},
|
|
{"name": "CHANGED_String", "values": ["2024-01-15T12:00:00Z"]},
|
|
]
|
|
},
|
|
"advertImageList": {
|
|
"advertImage": [
|
|
{"referenceImageUrl": "https://img.willhaben.at/img123.jpg"}
|
|
]
|
|
},
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_fields():
|
|
"""Sample extracted ad fields dict as returned by extract_ad_fields()."""
|
|
return {
|
|
"wh_ad_id": "12345678",
|
|
"title": "Mountain Bike 2024",
|
|
"price": 250.0,
|
|
"location": "Vienna",
|
|
"url": "https://www.willhaben.at/iad/mountain-bike-2024/12345678",
|
|
"published_at": datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc),
|
|
"main_image_url": "https://img.willhaben.at/img123.jpg",
|
|
"postcode": "1010",
|
|
"modified_at": datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc),
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_kw_row():
|
|
"""Sample keyword row dict with filter settings."""
|
|
return {
|
|
"id": "kw-uuid-123",
|
|
"keyword": "bike",
|
|
"price_min": None,
|
|
"price_max": None,
|
|
"allowed_postcodes": None,
|
|
}
|