chore: sync project files
This commit is contained in:
288
tests/stores/test_cdiscount.py
Executable file
288
tests/stores/test_cdiscount.py
Executable file
@@ -0,0 +1,288 @@
|
||||
"""
|
||||
Tests pour pricewatch.app.stores.cdiscount.store
|
||||
|
||||
Vérifie match(), canonicalize(), extract_reference() et parse()
|
||||
pour le store Cdiscount.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from pricewatch.app.stores.cdiscount.store import CdiscountStore
|
||||
from pricewatch.app.core.schema import DebugStatus, StockStatus
|
||||
|
||||
|
||||
class TestCdiscountMatch:
|
||||
"""Tests de la méthode match() pour Cdiscount."""
|
||||
|
||||
@pytest.fixture
|
||||
def store(self) -> CdiscountStore:
|
||||
"""Fixture: CdiscountStore instance."""
|
||||
return CdiscountStore()
|
||||
|
||||
def test_match_cdiscount_com(self, store):
|
||||
"""cdiscount.com doit retourner 0.9."""
|
||||
score = store.match("https://www.cdiscount.com/informatique/example/f-123-sku.html")
|
||||
assert score == 0.9
|
||||
|
||||
def test_match_non_cdiscount(self, store):
|
||||
"""URL non-Cdiscount doit retourner 0.0."""
|
||||
score = store.match("https://www.amazon.fr/dp/B08N5WRWNW")
|
||||
assert score == 0.0
|
||||
|
||||
def test_match_empty_url(self, store):
|
||||
"""URL vide doit retourner 0.0."""
|
||||
score = store.match("")
|
||||
assert score == 0.0
|
||||
|
||||
def test_match_case_insensitive(self, store):
|
||||
"""Match doit être insensible à la casse."""
|
||||
score = store.match("https://www.CDISCOUNT.COM/product/f-123-sku.html")
|
||||
assert score == 0.9
|
||||
|
||||
|
||||
class TestCdiscountCanonicalize:
|
||||
"""Tests de la méthode canonicalize() pour Cdiscount."""
|
||||
|
||||
@pytest.fixture
|
||||
def store(self) -> CdiscountStore:
|
||||
"""Fixture: CdiscountStore instance."""
|
||||
return CdiscountStore()
|
||||
|
||||
def test_canonicalize_with_query_params(self, store):
|
||||
"""URL avec query params doit être normalisée."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/product/f-10709-sku.html?idOffre=123&sw=abc"
|
||||
canonical = store.canonicalize(url)
|
||||
assert canonical == "https://www.cdiscount.com/informatique/pc/product/f-10709-sku.html"
|
||||
assert "?" not in canonical
|
||||
|
||||
def test_canonicalize_already_canonical(self, store):
|
||||
"""URL déjà canonique ne change pas."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
canonical = store.canonicalize(url)
|
||||
assert canonical == url
|
||||
|
||||
def test_canonicalize_with_fragment(self, store):
|
||||
"""URL avec fragment doit être normalisée."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html#mpos=2"
|
||||
canonical = store.canonicalize(url)
|
||||
assert canonical == "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
assert "#" not in canonical
|
||||
|
||||
def test_canonicalize_empty_url(self, store):
|
||||
"""URL vide retourne URL vide."""
|
||||
canonical = store.canonicalize("")
|
||||
assert canonical == ""
|
||||
|
||||
|
||||
class TestCdiscountExtractReference:
|
||||
"""Tests de la méthode extract_reference() pour Cdiscount."""
|
||||
|
||||
@pytest.fixture
|
||||
def store(self) -> CdiscountStore:
|
||||
"""Fixture: CdiscountStore instance."""
|
||||
return CdiscountStore()
|
||||
|
||||
def test_extract_reference_standard_format(self, store):
|
||||
"""Extraction du SKU depuis format standard /f-{ID}-{SKU}.html."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-tuf608umrv004.html"
|
||||
ref = store.extract_reference(url)
|
||||
assert ref == "10709-tuf608umrv004"
|
||||
|
||||
def test_extract_reference_long_url(self, store):
|
||||
"""Extraction du SKU depuis URL longue avec chemin complet."""
|
||||
url = "https://www.cdiscount.com/informatique/ordinateurs-pc-portables/pc-portable-gamer-asus/f-10709-tuf608umrv004.html"
|
||||
ref = store.extract_reference(url)
|
||||
assert ref == "10709-tuf608umrv004"
|
||||
|
||||
def test_extract_reference_with_query_params(self, store):
|
||||
"""Extraction du SKU depuis URL avec query params."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku123.html?idOffre=456"
|
||||
ref = store.extract_reference(url)
|
||||
assert ref == "10709-sku123"
|
||||
|
||||
def test_extract_reference_invalid_url(self, store):
|
||||
"""URL sans SKU retourne None."""
|
||||
url = "https://www.cdiscount.com/informatique/"
|
||||
ref = store.extract_reference(url)
|
||||
assert ref is None
|
||||
|
||||
def test_extract_reference_empty_url(self, store):
|
||||
"""URL vide retourne None."""
|
||||
ref = store.extract_reference("")
|
||||
assert ref is None
|
||||
|
||||
|
||||
class TestCdiscountParse:
|
||||
"""Tests de la méthode parse() pour Cdiscount."""
|
||||
|
||||
@pytest.fixture
|
||||
def store(self) -> CdiscountStore:
|
||||
"""Fixture: CdiscountStore instance."""
|
||||
return CdiscountStore()
|
||||
|
||||
@pytest.fixture
|
||||
def minimal_html(self) -> str:
|
||||
"""Fixture: HTML Cdiscount minimal avec titre et prix."""
|
||||
return """
|
||||
<html>
|
||||
<head><title>Test Product</title></head>
|
||||
<body>
|
||||
<h1 data-e2e="title">PC Portable Test</h1>
|
||||
<div class="sc-83lijy-0 kwssIa SecondaryPrice-price">899,99 €</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def complete_html(self) -> str:
|
||||
"""Fixture: HTML Cdiscount plus complet."""
|
||||
return """
|
||||
<html>
|
||||
<head><title>Test Product</title></head>
|
||||
<body>
|
||||
<h1 data-e2e="title">PC Portable Gamer ASUS</h1>
|
||||
<div class="SecondaryPrice-price">1299,99 €</div>
|
||||
<img alt="PC Portable Gamer ASUS" src="https://www.cdiscount.com/pdt2/0/0/4/1/700x700/sku123/image1.jpg" />
|
||||
<img alt="PC Portable Gamer ASUS" src="https://www.cdiscount.com/pdt2/0/0/4/2/700x700/sku123/image2.jpg" />
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def empty_html(self) -> str:
|
||||
"""Fixture: HTML vide."""
|
||||
return "<html><body></body></html>"
|
||||
|
||||
def test_parse_minimal_html(self, store, minimal_html):
|
||||
"""Parse un HTML minimal avec titre et prix."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku123.html"
|
||||
snapshot = store.parse(minimal_html, url)
|
||||
|
||||
assert snapshot.source == "cdiscount"
|
||||
assert snapshot.url == "https://www.cdiscount.com/informatique/pc/f-10709-sku123.html"
|
||||
assert snapshot.title == "PC Portable Test"
|
||||
assert snapshot.price == 899.99
|
||||
assert snapshot.currency == "EUR"
|
||||
assert snapshot.is_complete() is True
|
||||
|
||||
def test_parse_complete_html(self, store, complete_html):
|
||||
"""Parse un HTML plus complet avec images."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-asus123.html"
|
||||
snapshot = store.parse(complete_html, url)
|
||||
|
||||
assert snapshot.title == "PC Portable Gamer ASUS"
|
||||
assert snapshot.price == 1299.99
|
||||
assert snapshot.reference == "10709-asus123"
|
||||
assert len(snapshot.images) >= 2
|
||||
assert snapshot.is_complete() is True
|
||||
assert snapshot.debug.status == DebugStatus.SUCCESS
|
||||
|
||||
def test_parse_empty_html(self, store, empty_html):
|
||||
"""Parse un HTML vide doit retourner un snapshot partiel."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(empty_html, url)
|
||||
|
||||
assert snapshot.source == "cdiscount"
|
||||
assert snapshot.title is None
|
||||
assert snapshot.price is None
|
||||
assert snapshot.is_complete() is False
|
||||
assert snapshot.debug.status == DebugStatus.PARTIAL
|
||||
|
||||
def test_parse_canonicalizes_url(self, store, minimal_html):
|
||||
"""Parse doit canonicaliser l'URL."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html?idOffre=123#mpos=2"
|
||||
snapshot = store.parse(minimal_html, url)
|
||||
|
||||
assert snapshot.url == "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
assert "?" not in snapshot.url
|
||||
assert "#" not in snapshot.url
|
||||
|
||||
def test_parse_extracts_reference_from_url(self, store, minimal_html):
|
||||
"""Parse doit extraire le SKU depuis l'URL."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-tuf608umrv004.html"
|
||||
snapshot = store.parse(minimal_html, url)
|
||||
|
||||
assert snapshot.reference == "10709-tuf608umrv004"
|
||||
|
||||
def test_parse_sets_fetched_at(self, store, minimal_html):
|
||||
"""Parse doit définir fetched_at."""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(minimal_html, url)
|
||||
|
||||
assert snapshot.fetched_at is not None
|
||||
|
||||
def test_parse_partial_status_without_title(self, store):
|
||||
"""Parse sans titre doit avoir status PARTIAL."""
|
||||
html = """
|
||||
<html><body>
|
||||
<div class="SecondaryPrice-price">299,99 €</div>
|
||||
</body></html>
|
||||
"""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(html, url)
|
||||
|
||||
assert snapshot.debug.status == DebugStatus.PARTIAL
|
||||
assert snapshot.title is None
|
||||
assert snapshot.price == 299.99
|
||||
|
||||
def test_parse_partial_status_without_price(self, store):
|
||||
"""Parse sans prix doit avoir status PARTIAL."""
|
||||
html = """
|
||||
<html><body>
|
||||
<h1 data-e2e="title">Test Product</h1>
|
||||
</body></html>
|
||||
"""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(html, url)
|
||||
|
||||
assert snapshot.debug.status == DebugStatus.PARTIAL
|
||||
assert snapshot.title == "Test Product"
|
||||
assert snapshot.price is None
|
||||
|
||||
def test_parse_price_with_comma_separator(self, store):
|
||||
"""Parse doit gérer les prix avec virgule (format français)."""
|
||||
html = """
|
||||
<html><body>
|
||||
<h1 data-e2e="title">Test</h1>
|
||||
<div class="price">1199,99 €</div>
|
||||
</body></html>
|
||||
"""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(html, url)
|
||||
|
||||
assert snapshot.price == 1199.99
|
||||
|
||||
def test_parse_price_with_dot_separator(self, store):
|
||||
"""Parse doit gérer les prix avec point (format international)."""
|
||||
html = """
|
||||
<html><body>
|
||||
<h1 data-e2e="title">Test</h1>
|
||||
<div class="price">1199.99 €</div>
|
||||
</body></html>
|
||||
"""
|
||||
url = "https://www.cdiscount.com/informatique/pc/f-10709-sku.html"
|
||||
snapshot = store.parse(html, url)
|
||||
|
||||
assert snapshot.price == 1199.99
|
||||
|
||||
|
||||
class TestCdiscountStoreInit:
|
||||
"""Tests de l'initialisation du store Cdiscount."""
|
||||
|
||||
def test_store_id(self):
|
||||
"""Le store_id doit être 'cdiscount'."""
|
||||
store = CdiscountStore()
|
||||
assert store.store_id == "cdiscount"
|
||||
|
||||
def test_selectors_loaded(self):
|
||||
"""Les sélecteurs doivent être chargés depuis selectors.yml."""
|
||||
store = CdiscountStore()
|
||||
assert isinstance(store.selectors, dict)
|
||||
assert len(store.selectors) > 0
|
||||
|
||||
def test_repr(self):
|
||||
"""Test de la représentation string."""
|
||||
store = CdiscountStore()
|
||||
repr_str = repr(store)
|
||||
assert "CdiscountStore" in repr_str
|
||||
assert "cdiscount" in repr_str
|
||||
Reference in New Issue
Block a user