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:
53
tests/api/test_auth_simple.py
Normal file
53
tests/api/test_auth_simple.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Tests simples pour l'authentification API."""
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from pricewatch.app.api.main import require_token
|
||||
|
||||
|
||||
class FakeConfig:
|
||||
api_token = "valid-token"
|
||||
|
||||
|
||||
class FakeConfigNoToken:
|
||||
api_token = None
|
||||
|
||||
|
||||
def test_require_token_valid(monkeypatch):
|
||||
"""Token valide ne leve pas d'exception."""
|
||||
monkeypatch.setattr("pricewatch.app.api.main.get_config", lambda: FakeConfig())
|
||||
# Ne doit pas lever d'exception
|
||||
require_token("Bearer valid-token")
|
||||
|
||||
|
||||
def test_require_token_missing(monkeypatch):
|
||||
"""Token manquant leve 401."""
|
||||
monkeypatch.setattr("pricewatch.app.api.main.get_config", lambda: FakeConfig())
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
require_token(None)
|
||||
assert exc_info.value.status_code == 401
|
||||
|
||||
|
||||
def test_require_token_invalid_format(monkeypatch):
|
||||
"""Token sans Bearer leve 401."""
|
||||
monkeypatch.setattr("pricewatch.app.api.main.get_config", lambda: FakeConfig())
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
require_token("invalid-format")
|
||||
assert exc_info.value.status_code == 401
|
||||
|
||||
|
||||
def test_require_token_wrong_value(monkeypatch):
|
||||
"""Mauvais token leve 403."""
|
||||
monkeypatch.setattr("pricewatch.app.api.main.get_config", lambda: FakeConfig())
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
require_token("Bearer wrong-token")
|
||||
assert exc_info.value.status_code == 403
|
||||
|
||||
|
||||
def test_require_token_not_configured(monkeypatch):
|
||||
"""Token non configure leve 500."""
|
||||
monkeypatch.setattr("pricewatch.app.api.main.get_config", lambda: FakeConfigNoToken())
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
require_token("Bearer any-token")
|
||||
assert exc_info.value.status_code == 500
|
||||
26
tests/api/test_logs_endpoints.py
Normal file
26
tests/api/test_logs_endpoints.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Tests pour les endpoints de logs API."""
|
||||
|
||||
from pricewatch.app.api.main import list_backend_logs, BACKEND_LOGS
|
||||
from pricewatch.app.api.schemas import BackendLogEntry
|
||||
|
||||
|
||||
def test_list_backend_logs_empty():
|
||||
"""Liste des logs backend vide."""
|
||||
BACKEND_LOGS.clear()
|
||||
result = list_backend_logs()
|
||||
assert result == []
|
||||
|
||||
|
||||
def test_list_backend_logs_with_entries():
|
||||
"""Liste des logs backend avec entrees."""
|
||||
from datetime import datetime
|
||||
BACKEND_LOGS.clear()
|
||||
entry = BackendLogEntry(level="INFO", message="Test log", time=datetime(2026, 1, 17, 12, 0, 0))
|
||||
BACKEND_LOGS.append(entry)
|
||||
|
||||
result = list_backend_logs()
|
||||
assert len(result) == 1
|
||||
assert result[0].message == "Test log"
|
||||
assert result[0].level == "INFO"
|
||||
|
||||
BACKEND_LOGS.clear()
|
||||
267
tests/api/test_products_funcs.py
Normal file
267
tests/api/test_products_funcs.py
Normal file
@@ -0,0 +1,267 @@
|
||||
"""Tests fonctions API produits avec mocks."""
|
||||
|
||||
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 (
|
||||
create_product,
|
||||
get_product,
|
||||
update_product,
|
||||
delete_product,
|
||||
list_prices,
|
||||
create_price,
|
||||
update_price,
|
||||
delete_price,
|
||||
)
|
||||
from pricewatch.app.api.schemas import ProductCreate, ProductUpdate, PriceHistoryCreate, PriceHistoryUpdate
|
||||
|
||||
|
||||
class MockProduct:
|
||||
"""Mock Product model."""
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.get("id", 1)
|
||||
self.source = kwargs.get("source", "amazon")
|
||||
self.reference = kwargs.get("reference", "REF123")
|
||||
self.url = kwargs.get("url", "https://example.com")
|
||||
self.title = kwargs.get("title", "Test Product")
|
||||
self.category = kwargs.get("category")
|
||||
self.description = kwargs.get("description")
|
||||
self.currency = kwargs.get("currency", "EUR")
|
||||
self.msrp = kwargs.get("msrp")
|
||||
self.first_seen_at = kwargs.get("first_seen_at", datetime.now())
|
||||
self.last_updated_at = kwargs.get("last_updated_at", datetime.now())
|
||||
|
||||
|
||||
class MockPrice:
|
||||
"""Mock PriceHistory model."""
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.get("id", 1)
|
||||
self.product_id = kwargs.get("product_id", 1)
|
||||
self.price = kwargs.get("price", 99.99)
|
||||
self.shipping_cost = kwargs.get("shipping_cost")
|
||||
self.stock_status = kwargs.get("stock_status", "in_stock")
|
||||
self.fetch_method = kwargs.get("fetch_method", "http")
|
||||
self.fetch_status = kwargs.get("fetch_status", "success")
|
||||
self.fetched_at = kwargs.get("fetched_at", datetime.now())
|
||||
|
||||
|
||||
class TestCreateProduct:
|
||||
"""Tests create_product."""
|
||||
|
||||
def test_create_success(self):
|
||||
"""Cree un produit avec succes."""
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock()
|
||||
session.refresh = MagicMock()
|
||||
|
||||
payload = ProductCreate(
|
||||
source="amazon",
|
||||
reference="NEW123",
|
||||
url="https://amazon.fr/dp/NEW123",
|
||||
title="New Product",
|
||||
currency="EUR",
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.Product") as MockProductClass:
|
||||
mock_product = MockProduct(reference="NEW123")
|
||||
MockProductClass.return_value = mock_product
|
||||
|
||||
with patch("pricewatch.app.api.main._product_to_out") as mock_to_out:
|
||||
mock_to_out.return_value = MagicMock()
|
||||
result = create_product(payload, session)
|
||||
|
||||
session.add.assert_called_once()
|
||||
session.commit.assert_called_once()
|
||||
|
||||
def test_create_duplicate(self):
|
||||
"""Cree un produit duplique leve 409."""
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=IntegrityError("duplicate", {}, None))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = ProductCreate(
|
||||
source="amazon",
|
||||
reference="DUPE",
|
||||
url="https://amazon.fr/dp/DUPE",
|
||||
title="Duplicate",
|
||||
currency="EUR",
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.Product"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_product(payload, session)
|
||||
assert exc_info.value.status_code == 409
|
||||
|
||||
def test_create_db_error(self):
|
||||
"""Erreur DB leve 500."""
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("db error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = ProductCreate(
|
||||
source="amazon",
|
||||
reference="ERR",
|
||||
url="https://amazon.fr/dp/ERR",
|
||||
title="Error",
|
||||
currency="EUR",
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.Product"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_product(payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestGetProduct:
|
||||
"""Tests get_product."""
|
||||
|
||||
def test_get_not_found(self):
|
||||
"""Produit 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:
|
||||
get_product(99999, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
|
||||
class TestUpdateProduct:
|
||||
"""Tests update_product."""
|
||||
|
||||
def test_update_not_found(self):
|
||||
"""Update produit 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 = ProductUpdate(title="Updated")
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_product(99999, payload, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_update_db_error(self):
|
||||
"""Erreur DB lors d'update leve 500."""
|
||||
session = MagicMock()
|
||||
mock_product = MockProduct()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_product
|
||||
session.query.return_value = mock_query
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = ProductUpdate(title="Updated")
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_product(1, payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestDeleteProduct:
|
||||
"""Tests delete_product."""
|
||||
|
||||
def test_delete_not_found(self):
|
||||
"""Delete produit 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_product(99999, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_delete_success(self):
|
||||
"""Delete produit avec succes."""
|
||||
session = MagicMock()
|
||||
mock_product = MockProduct()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_product
|
||||
session.query.return_value = mock_query
|
||||
session.delete = MagicMock()
|
||||
session.commit = MagicMock()
|
||||
|
||||
result = delete_product(1, session)
|
||||
assert result == {"status": "deleted"}
|
||||
session.delete.assert_called_once()
|
||||
|
||||
def test_delete_db_error(self):
|
||||
"""Erreur DB lors de delete leve 500."""
|
||||
session = MagicMock()
|
||||
mock_product = MockProduct()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_product
|
||||
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_product(1, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestCreatePrice:
|
||||
"""Tests create_price."""
|
||||
|
||||
def test_create_price_db_error(self):
|
||||
"""Erreur DB lors de creation prix."""
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = PriceHistoryCreate(
|
||||
product_id=1,
|
||||
price=99.99,
|
||||
fetch_method="http",
|
||||
fetch_status="success",
|
||||
fetched_at=datetime.now(),
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.PriceHistory"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_price(payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestUpdatePrice:
|
||||
"""Tests update_price."""
|
||||
|
||||
def test_update_price_not_found(self):
|
||||
"""Update prix 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 = PriceHistoryUpdate(price=149.99)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_price(99999, payload, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
|
||||
class TestDeletePrice:
|
||||
"""Tests delete_price."""
|
||||
|
||||
def test_delete_price_not_found(self):
|
||||
"""Delete prix 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_price(99999, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
135
tests/api/test_scraping_logs.py
Normal file
135
tests/api/test_scraping_logs.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Tests API endpoints scraping logs."""
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from pricewatch.app.api.main import create_log, update_log, delete_log
|
||||
from pricewatch.app.api.schemas import ScrapingLogCreate, ScrapingLogUpdate
|
||||
|
||||
|
||||
class MockScrapingLog:
|
||||
"""Mock ScrapingLog model."""
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.get("id", 1)
|
||||
self.product_id = kwargs.get("product_id")
|
||||
self.url = kwargs.get("url", "https://example.com")
|
||||
self.source = kwargs.get("source", "amazon")
|
||||
self.reference = kwargs.get("reference", "REF123")
|
||||
self.fetch_method = kwargs.get("fetch_method", "http")
|
||||
self.fetch_status = kwargs.get("fetch_status", "success")
|
||||
self.fetched_at = kwargs.get("fetched_at", datetime.now())
|
||||
self.duration_ms = kwargs.get("duration_ms", 1500)
|
||||
self.html_size_bytes = kwargs.get("html_size_bytes", 50000)
|
||||
self.errors = kwargs.get("errors", [])
|
||||
self.notes = kwargs.get("notes", [])
|
||||
|
||||
|
||||
class TestCreateLog:
|
||||
"""Tests create_log endpoint."""
|
||||
|
||||
def test_create_log_db_error(self):
|
||||
"""Erreur DB lors de creation log leve 500."""
|
||||
from unittest.mock import patch
|
||||
|
||||
session = MagicMock()
|
||||
session.add = MagicMock()
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = ScrapingLogCreate(
|
||||
url="https://amazon.fr/dp/TEST",
|
||||
source="amazon",
|
||||
reference="TEST123",
|
||||
fetch_method="http",
|
||||
fetch_status="success",
|
||||
fetched_at=datetime.now(),
|
||||
)
|
||||
|
||||
with patch("pricewatch.app.api.main.ScrapingLog"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
create_log(payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestUpdateLog:
|
||||
"""Tests update_log endpoint."""
|
||||
|
||||
def test_update_log_not_found(self):
|
||||
"""Update log 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 = ScrapingLogUpdate(fetch_status="failed")
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_log(99999, payload, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_update_log_db_error(self):
|
||||
"""Erreur DB lors d'update log leve 500."""
|
||||
from unittest.mock import patch
|
||||
|
||||
session = MagicMock()
|
||||
mock_log = MockScrapingLog()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_log
|
||||
session.query.return_value = mock_query
|
||||
session.commit = MagicMock(side_effect=SQLAlchemyError("error"))
|
||||
session.rollback = MagicMock()
|
||||
|
||||
payload = ScrapingLogUpdate(fetch_status="failed")
|
||||
|
||||
with patch("pricewatch.app.api.main._log_to_out"):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
update_log(1, payload, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
|
||||
|
||||
class TestDeleteLog:
|
||||
"""Tests delete_log endpoint."""
|
||||
|
||||
def test_delete_log_not_found(self):
|
||||
"""Delete log 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_log(99999, session)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
def test_delete_log_success(self):
|
||||
"""Delete log avec succes."""
|
||||
session = MagicMock()
|
||||
mock_log = MockScrapingLog()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_log
|
||||
session.query.return_value = mock_query
|
||||
session.delete = MagicMock()
|
||||
session.commit = MagicMock()
|
||||
|
||||
result = delete_log(1, session)
|
||||
assert result == {"status": "deleted"}
|
||||
session.delete.assert_called_once()
|
||||
|
||||
def test_delete_log_db_error(self):
|
||||
"""Erreur DB lors de delete log leve 500."""
|
||||
session = MagicMock()
|
||||
mock_log = MockScrapingLog()
|
||||
mock_query = MagicMock()
|
||||
mock_query.filter.return_value.one_or_none.return_value = mock_log
|
||||
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_log(1, session)
|
||||
assert exc_info.value.status_code == 500
|
||||
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