| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- """Tests unitaires des outils du catalogue.
-
- Ces tests sont FOURNIS COMPLETS — ils servent de spécification.
- Ils doivent tous passer une fois vos outils implémentés.
-
- Lancer : uv run pytest tests/unit/test_tools.py -v
-
- Progression recommandée :
- 1. Implémenter search_product → faire passer TestSearchProduct
- 2. Implémenter calculate_price → faire passer TestCalculatePrice
- 3. Implémenter list_products → faire passer TestListProducts
- 4. Implémenter check_stock (bonus) → faire passer TestCheckStock
- """
-
- import pytest
-
- from src.tools.catalogue import (
- calculate_price,
- check_stock,
- list_products,
- search_product,
- )
-
-
- # ── search_product ────────────────────────────────────────────────────────────
-
- class TestSearchProduct:
-
- def test_found_by_partial_name(self) -> None:
- result = search_product.invoke({"name": "laptop"})
- assert "Laptop Pro 15" in result
- assert "1299" in result
-
- def test_found_case_insensitive(self) -> None:
- assert "Souris Ergonomique" in search_product.invoke({"name": "SOURIS"})
- assert "Souris Ergonomique" in search_product.invoke({"name": "souris"})
-
- def test_not_found_returns_message(self) -> None:
- result = search_product.invoke({"name": "produit-xyz-inexistant"})
- assert "Aucun produit" in result
-
- def test_search_by_description_word(self) -> None:
- result = search_product.invoke({"name": "ergonomique"})
- assert "Souris Ergonomique" in result
-
- def test_multiple_results(self) -> None:
- # "casque" doit trouver au moins le casque audio
- result = search_product.invoke({"name": "casque"})
- assert "Casque Audio Pro" in result
-
-
- # ── calculate_price ───────────────────────────────────────────────────────────
-
- class TestCalculatePrice:
-
- def test_price_no_discount(self) -> None:
- result = calculate_price.invoke({"product_name": "souris", "quantity": 2})
- assert "99.80" in result # 2 × 49.90
-
- def test_price_with_discount(self) -> None:
- result = calculate_price.invoke({
- "product_name": "souris", "quantity": 2, "discount_percent": 10.0
- })
- assert "89.82" in result # 99.80 - 10%
-
- def test_quantity_one(self) -> None:
- result = calculate_price.invoke({"product_name": "clavier", "quantity": 1})
- assert "129" in result
-
- def test_invalid_quantity_zero(self) -> None:
- result = calculate_price.invoke({"product_name": "souris", "quantity": 0})
- assert "positif" in result.lower()
-
- def test_invalid_discount_over_100(self) -> None:
- result = calculate_price.invoke({
- "product_name": "souris", "quantity": 1, "discount_percent": 150.0
- })
- assert "100" in result
-
- def test_product_not_found(self) -> None:
- result = calculate_price.invoke({"product_name": "inexistant", "quantity": 1})
- assert "introuvable" in result.lower()
-
- def test_out_of_stock_warning(self) -> None:
- # enceinte-bluetooth est en stock=0
- result = calculate_price.invoke({"product_name": "enceinte", "quantity": 1})
- assert "rupture" in result.lower() or "RUPTURE" in result
-
-
- # ── list_products ─────────────────────────────────────────────────────────────
-
- class TestListProducts:
-
- def test_list_all(self) -> None:
- result = list_products.invoke({})
- assert "Laptop Pro 15" in result
- assert "Casque Audio Pro" in result
-
- def test_filter_informatique(self) -> None:
- result = list_products.invoke({"category": "informatique"})
- assert "Laptop Pro 15" in result
- assert "Souris" not in result
-
- def test_filter_audio(self) -> None:
- result = list_products.invoke({"category": "audio"})
- assert "Casque Audio Pro" in result
- assert "Laptop" not in result
-
- def test_filter_case_insensitive(self) -> None:
- assert "Casque" in list_products.invoke({"category": "AUDIO"})
-
- def test_unknown_category(self) -> None:
- result = list_products.invoke({"category": "meuble"})
- assert "meuble" in result.lower()
-
- def test_out_of_stock_shown(self) -> None:
- result = list_products.invoke({})
- assert "RUPTURE" in result # enceinte-bluetooth stock=0
-
-
- # ── check_stock (bonus) ────────────────────────────────────────────────────────
-
- class TestCheckStock:
-
- def test_sufficient_stock(self) -> None:
- result = check_stock.invoke({"product_name": "souris", "required_quantity": 5})
- assert "possible" in result.lower() or "✅" in result
-
- def test_insufficient_stock(self) -> None:
- # écran-4k a stock=3, on en demande 10
- result = check_stock.invoke({"product_name": "ecran", "required_quantity": 10})
- assert "insuffisant" in result.lower() or "⚠" in result
-
- def test_out_of_stock(self) -> None:
- result = check_stock.invoke({"product_name": "enceinte", "required_quantity": 1})
- assert "RUPTURE" in result or "impossible" in result.lower()
-
- def test_invalid_quantity(self) -> None:
- result = check_stock.invoke({"product_name": "souris", "required_quantity": -1})
- assert "positif" in result.lower()
-
- def test_product_not_found(self) -> None:
- result = check_stock.invoke({"product_name": "xyz", "required_quantity": 1})
- assert "introuvable" in result.lower()
|