feat: improve SPA scraping and increase test coverage
- Add SPA support for Playwright with wait_for_network_idle and extra_wait_ms - Add BaseStore.get_spa_config() and requires_playwright() methods - Implement AliExpress SPA config with JSON price extraction patterns - Fix Amazon price parsing to prioritize whole+fraction combination - Fix AliExpress regex patterns (remove double backslashes) - Add CLI tests: detect, doctor, fetch, parse, run commands - Add API tests: auth, logs, products, scraping_logs, webhooks Tests: 417 passed, 85% coverage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
159
tests/api/test_webhooks_funcs.py
Normal file
159
tests/api/test_webhooks_funcs.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""Tests API endpoints webhooks."""
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
from pricewatch.app.api.main import (
|
||||
list_webhooks,
|
||||
create_webhook,
|
||||
update_webhook,
|
||||
delete_webhook,
|
||||
)
|
||||
from pricewatch.app.api.schemas import WebhookCreate, WebhookUpdate
|
||||
|
||||
|
||||
class MockWebhook:
|
||||
"""Mock Webhook model."""
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.get("id", 1)
|
||||
self.url = kwargs.get("url", "https://example.com/webhook")
|
||||
self.events = kwargs.get("events", ["price_change", "stock_change"])
|
||||
self.active = kwargs.get("active", True)
|
||||
self.created_at = kwargs.get("created_at", datetime.now())
|
||||
self.last_triggered_at = kwargs.get("last_triggered_at")
|
||||
|
||||
|
||||
class TestListWebhooks:
|
||||
"""Tests list_webhooks endpoint."""
|
||||
|
||||
def test_list_webhooks_empty(self):
|
||||
"""Liste vide de webhooks."""
|
||||
session = MagicMock()
|
||||
mock_query = MagicMock()
|
||||
mock_query.all.return_value = []
|
||||
session.query.return_value = mock_query
|
||||
|
||||
with patch("pricewatch.app.api.main._webhook_to_out") as mock_to_out:
|
||||
result = list_webhooks(session=session)
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestCreateWebhook:
|
||||
"""Tests create_webhook endpoint."""
|
||||
|
||||
def test_create_webhook_integrity_error(self):
|
||||
"""Erreur d'integrite lors de creation webhook leve 500."""
|
||||
# Note: le code actuel ne distingue pas IntegrityError de SQLAlchemyError
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=IntegrityError("duplicate", {}, None))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = WebhookCreate(
|
||||
event="price_change",
|
||||
url="https://example.com/webhook",
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.Webhook"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_webhook(payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
def test_create_webhook_db_error(self):
|
||||
"""Erreur DB lors de creation webhook leve 500."""
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = WebhookCreate(
|
||||
event="price_change",
|
||||
url="https://example.com/webhook",
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.Webhook"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_webhook(payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestUpdateWebhook:
|
||||
"""Tests update_webhook endpoint."""
|
||||
|
||||
def test_update_webhook_not_found(self):
|
||||
"""Update webhook non trouve leve 404."""
|
||||
session = MagicMock()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = None
|
||||
session.query.return_value = mock_query
|
||||
|
||||
payload = WebhookUpdate(active=False)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_webhook(99999, payload, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_update_webhook_db_error(self):
|
||||
"""Erreur DB lors d'update webhook leve 500."""
|
||||
session = MagicMock()
|
||||
mock_webhook = MockWebhook()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_webhook
|
||||
session.query.return_value = mock_query
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = WebhookUpdate(active=False)
|
||||
|
||||
with patch("pricewatch.app.api.main._webhook_to_out"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_webhook(1, payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestDeleteWebhook:
|
||||
"""Tests delete_webhook endpoint."""
|
||||
|
||||
def test_delete_webhook_not_found(self):
|
||||
"""Delete webhook non trouve leve 404."""
|
||||
session = MagicMock()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = None
|
||||
session.query.return_value = mock_query
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
delete_webhook(99999, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_delete_webhook_success(self):
|
||||
"""Delete webhook avec succes."""
|
||||
session = MagicMock()
|
||||
mock_webhook = MockWebhook()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_webhook
|
||||
session.query.return_value = mock_query
|
||||
session.delete = MagicMock()
|
||||
session.commit = MagicMock()
|
||||
|
||||
result = delete_webhook(1, session)
|
||||
assert result == {"status": "deleted"}
|
||||
session.delete.assert_called_once()
|
||||
|
||||
def test_delete_webhook_db_error(self):
|
||||
"""Erreur DB lors de delete webhook leve 500."""
|
||||
session = MagicMock()
|
||||
mock_webhook = MockWebhook()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_webhook
|
||||
session.query.return_value = mock_query
|
||||
session.delete = MagicMock()
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
delete_webhook(1, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
Reference in New Issue
Block a user