Spaces:
Sleeping
Sleeping
| """ | |
| Tests locaux pour Avis'IA Resto. | |
| Execution : | |
| python test_app.py | |
| Ces tests n'appellent pas l'API Mistral. Ils verifient que l'application peut etre | |
| importee, que le CSV exemple est conforme, que les graphiques sont generes et que | |
| le mode local produit une reponse exploitable sans cle API. | |
| """ | |
| from __future__ import annotations | |
| import unittest | |
| from pathlib import Path | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| import app | |
| class AvisIARestoTestCase(unittest.TestCase): | |
| def setUp(self) -> None: | |
| self.csv_path = Path(__file__).resolve().parent / "avis_restaurant_exemple.csv" | |
| self.raw = app.load_reviews(self.csv_path) | |
| self.prepared = app.prepare_reviews(self.raw) | |
| def test_csv_file_exists(self) -> None: | |
| self.assertTrue(self.csv_path.exists(), "Le CSV exemple doit exister dans le projet.") | |
| def test_required_columns_are_present(self) -> None: | |
| self.assertTrue(app.REQUIRED_COLUMNS.issubset(set(self.raw.columns))) | |
| def test_example_dataset_shape(self) -> None: | |
| self.assertEqual(len(self.prepared), 36) | |
| self.assertEqual(self.prepared["restaurant"].nunique(), 3) | |
| def test_sentiment_classification(self) -> None: | |
| sentiments = set(self.prepared["sentiment"].unique()) | |
| self.assertTrue({"Positif", "Mitige", "Negatif"}.issubset(sentiments)) | |
| self.assertEqual(app.classify_sentiment(5), "Positif") | |
| self.assertEqual(app.classify_sentiment(1), "Negatif") | |
| self.assertEqual(app.classify_sentiment(3), "Mitige") | |
| def test_theme_detection(self) -> None: | |
| themes = app.detect_themes("Service lent, plat froid, addition trop chere.") | |
| self.assertIn("Service", themes) | |
| self.assertIn("Cuisine", themes) | |
| self.assertIn("Prix", themes) | |
| def test_fallback_response_without_api_key(self) -> None: | |
| response = app.generate_review_response( | |
| "Service lent et plat froid. Tres decu.", | |
| 1, | |
| "Le Normand - Caen", | |
| "Google", | |
| use_ai=False, | |
| ) | |
| self.assertGreater(len(response), 80) | |
| self.assertIn("desoles", app.strip_accents(response)) | |
| self.assertIn("Le Normand - Caen", response) | |
| def test_five_plotly_figures_are_created(self) -> None: | |
| figures = app.build_figures(self.prepared) | |
| self.assertEqual(len(figures), 5) | |
| self.assertTrue(all(isinstance(fig, go.Figure) for fig in figures)) | |
| def test_dashboard_contract(self) -> None: | |
| outputs = app.build_dashboard(self.csv_path, use_ai=False) | |
| self.assertEqual(len(outputs), 7) | |
| self.assertIsInstance(outputs[0], str) | |
| self.assertTrue(all(isinstance(fig, go.Figure) for fig in outputs[1:6])) | |
| self.assertIn("Bilan", outputs[6]) | |
| def test_quality_checks_are_all_green(self) -> None: | |
| results = app.run_quality_checks() | |
| self.assertEqual(len(results), 7) | |
| failed = [check.name for check in results if not check.ok] | |
| self.assertEqual(failed, []) | |
| def test_gradio_demo_is_defined(self) -> None: | |
| self.assertTrue(hasattr(app, "demo")) | |
| def test_project_files_exist(self) -> None: | |
| expected_files = [ | |
| "README.md", | |
| "requirements.txt", | |
| "avis_restaurant_exemple.csv", | |
| "app.py", | |
| "test_app.py", | |
| ] | |
| root = Path(__file__).resolve().parent | |
| missing = [name for name in expected_files if not (root / name).exists()] | |
| self.assertEqual(missing, []) | |
| def test_readme_huggingface_metadata(self) -> None: | |
| readme = (Path(__file__).resolve().parent / "README.md").read_text(encoding="utf-8") | |
| self.assertIn("sdk: gradio", readme) | |
| self.assertIn("python_version: 3.11", readme) | |
| self.assertIn("sdk_version: 5.49.1", readme) | |
| self.assertIn("app_file: app.py", readme) | |
| self.assertIn("MISTRAL_API_KEY", readme) | |
| def test_readme_pins_huggingface_runtime(self) -> None: | |
| readme = (Path(__file__).resolve().parent / "README.md").read_text(encoding="utf-8") | |
| self.assertIn("python_version: 3.11", readme) | |
| self.assertIn("sdk_version: 5.49.1", readme) | |
| self.assertNotIn("sdk_version: 5.0.0", readme) | |
| def test_requirements_include_runtime_dependencies(self) -> None: | |
| requirements = (Path(__file__).resolve().parent / "requirements.txt").read_text(encoding="utf-8") | |
| for dependency in ["gradio", "huggingface_hub", "mistralai", "pandas", "plotly", "audioop-lts"]: | |
| self.assertIn(dependency, requirements) | |
| def test_requirements_pin_known_compatible_versions(self) -> None: | |
| requirements = (Path(__file__).resolve().parent / "requirements.txt").read_text(encoding="utf-8") | |
| expected_pins = [ | |
| "gradio==5.49.1", | |
| "huggingface_hub==0.35.3", | |
| "mistralai==2.4.9", | |
| "pandas==2.2.3", | |
| "plotly==6.5.2", | |
| "audioop-lts==0.2.2", | |
| ] | |
| for pin in expected_pins: | |
| self.assertIn(pin, requirements) | |
| self.assertNotIn("gradio==5.0.0", requirements) | |
| self.assertNotIn("gradio>=", requirements) | |
| def test_mistral_import_supports_sdk_v1_and_v2_paths(self) -> None: | |
| source = (Path(__file__).resolve().parent / "app.py").read_text(encoding="utf-8") | |
| self.assertIn("from mistralai.client import Mistral", source) | |
| self.assertIn("from mistralai import Mistral", source) | |
| def test_runtime_fix_is_documented(self) -> None: | |
| readme = (Path(__file__).resolve().parent / "README.md").read_text(encoding="utf-8") | |
| self.assertIn("Correctifs de déploiement verrouillés", readme) | |
| self.assertIn("huggingface_hub", readme) | |
| self.assertIn("audioop-lts", readme) | |
| if __name__ == "__main__": | |
| unittest.main(verbosity=2) | |