Bez popisu

test_tools.py 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """Tests unitaires des outils du catalogue.
  2. Ces tests sont FOURNIS COMPLETS — ils servent de spécification.
  3. Ils doivent tous passer une fois vos outils implémentés.
  4. Lancer : uv run pytest tests/unit/test_tools.py -v
  5. Progression recommandée :
  6. 1. Implémenter search_product → faire passer TestSearchProduct
  7. 2. Implémenter calculate_price → faire passer TestCalculatePrice
  8. 3. Implémenter list_products → faire passer TestListProducts
  9. 4. Implémenter check_stock (bonus) → faire passer TestCheckStock
  10. """
  11. import pytest
  12. from src.tools.catalogue import (
  13. calculate_price,
  14. check_stock,
  15. list_products,
  16. search_product,
  17. )
  18. # ── search_product ────────────────────────────────────────────────────────────
  19. class TestSearchProduct:
  20. def test_found_by_partial_name(self) -> None:
  21. result = search_product.invoke({"name": "laptop"})
  22. assert "Laptop Pro 15" in result
  23. assert "1299" in result
  24. def test_found_case_insensitive(self) -> None:
  25. assert "Souris Ergonomique" in search_product.invoke({"name": "SOURIS"})
  26. assert "Souris Ergonomique" in search_product.invoke({"name": "souris"})
  27. def test_not_found_returns_message(self) -> None:
  28. result = search_product.invoke({"name": "produit-xyz-inexistant"})
  29. assert "Aucun produit" in result
  30. def test_search_by_description_word(self) -> None:
  31. result = search_product.invoke({"name": "ergonomique"})
  32. assert "Souris Ergonomique" in result
  33. def test_multiple_results(self) -> None:
  34. # "casque" doit trouver au moins le casque audio
  35. result = search_product.invoke({"name": "casque"})
  36. assert "Casque Audio Pro" in result
  37. # ── calculate_price ───────────────────────────────────────────────────────────
  38. class TestCalculatePrice:
  39. def test_price_no_discount(self) -> None:
  40. result = calculate_price.invoke({"product_name": "souris", "quantity": 2})
  41. assert "99.80" in result # 2 × 49.90
  42. def test_price_with_discount(self) -> None:
  43. result = calculate_price.invoke({
  44. "product_name": "souris", "quantity": 2, "discount_percent": 10.0
  45. })
  46. assert "89.82" in result # 99.80 - 10%
  47. def test_quantity_one(self) -> None:
  48. result = calculate_price.invoke({"product_name": "clavier", "quantity": 1})
  49. assert "129" in result
  50. def test_invalid_quantity_zero(self) -> None:
  51. result = calculate_price.invoke({"product_name": "souris", "quantity": 0})
  52. assert "positif" in result.lower()
  53. def test_invalid_discount_over_100(self) -> None:
  54. result = calculate_price.invoke({
  55. "product_name": "souris", "quantity": 1, "discount_percent": 150.0
  56. })
  57. assert "100" in result
  58. def test_product_not_found(self) -> None:
  59. result = calculate_price.invoke({"product_name": "inexistant", "quantity": 1})
  60. assert "introuvable" in result.lower()
  61. def test_out_of_stock_warning(self) -> None:
  62. # enceinte-bluetooth est en stock=0
  63. result = calculate_price.invoke({"product_name": "enceinte", "quantity": 1})
  64. assert "rupture" in result.lower() or "RUPTURE" in result
  65. # ── list_products ─────────────────────────────────────────────────────────────
  66. class TestListProducts:
  67. def test_list_all(self) -> None:
  68. result = list_products.invoke({})
  69. assert "Laptop Pro 15" in result
  70. assert "Casque Audio Pro" in result
  71. def test_filter_informatique(self) -> None:
  72. result = list_products.invoke({"category": "informatique"})
  73. assert "Laptop Pro 15" in result
  74. assert "Souris" not in result
  75. def test_filter_audio(self) -> None:
  76. result = list_products.invoke({"category": "audio"})
  77. assert "Casque Audio Pro" in result
  78. assert "Laptop" not in result
  79. def test_filter_case_insensitive(self) -> None:
  80. assert "Casque" in list_products.invoke({"category": "AUDIO"})
  81. def test_unknown_category(self) -> None:
  82. result = list_products.invoke({"category": "meuble"})
  83. assert "meuble" in result.lower()
  84. def test_out_of_stock_shown(self) -> None:
  85. result = list_products.invoke({})
  86. assert "RUPTURE" in result # enceinte-bluetooth stock=0
  87. # ── check_stock (bonus) ────────────────────────────────────────────────────────
  88. class TestCheckStock:
  89. def test_sufficient_stock(self) -> None:
  90. result = check_stock.invoke({"product_name": "souris", "required_quantity": 5})
  91. assert "possible" in result.lower() or "✅" in result
  92. def test_insufficient_stock(self) -> None:
  93. # écran-4k a stock=3, on en demande 10
  94. result = check_stock.invoke({"product_name": "ecran", "required_quantity": 10})
  95. assert "insuffisant" in result.lower() or "⚠" in result
  96. def test_out_of_stock(self) -> None:
  97. result = check_stock.invoke({"product_name": "enceinte", "required_quantity": 1})
  98. assert "RUPTURE" in result or "impossible" in result.lower()
  99. def test_invalid_quantity(self) -> None:
  100. result = check_stock.invoke({"product_name": "souris", "required_quantity": -1})
  101. assert "positif" in result.lower()
  102. def test_product_not_found(self) -> None:
  103. result = check_stock.invoke({"product_name": "xyz", "required_quantity": 1})
  104. assert "introuvable" in result.lower()

Powered by TurnKey Linux.