Spaces:
Sleeping
Sleeping
| from unittest.mock import patch, MagicMock | |
| from streamlit.testing.v1 import AppTest | |
| APP_PATH = "src/api/app.py" | |
| MOCK_CLIENT_USER = {"id": 2, "username": "client1", "role": "client"} | |
| MOCK_ADMIN_USER = {"id": 1, "username": "admin", "role": "administrateur"} | |
| THRESHOLD = 0.0913 | |
| def make_mock_supabase(data): | |
| mock_response = MagicMock() | |
| mock_response.data = data | |
| mock_query = MagicMock() | |
| mock_query.select.return_value = mock_query | |
| mock_query.eq.return_value = mock_query | |
| mock_query.execute.return_value = mock_response | |
| mock_client = MagicMock() | |
| mock_client.table.return_value = mock_query | |
| return mock_client | |
| def app_logged_in(user): | |
| at = AppTest.from_file(APP_PATH) | |
| at.session_state["user"] = user | |
| at.run() | |
| return at | |
| # --- Page de login (utilisateur non connecté) --- | |
| def test_login_affiche_le_titre(): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| assert at.title[0].value == "Prédiction remboursement de crédit" | |
| def test_login_affiche_sous_titre_connexion(): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| assert any("Connexion" in s.value for s in at.subheader) | |
| def test_login_affiche_deux_champs_texte(): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| assert len(at.text_input) == 2 | |
| def test_login_affiche_bouton_se_connecter(): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| assert any(b.label == "Se connecter" for b in at.button) | |
| def test_login_avertit_si_champs_vides(): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| next(b for b in at.button if b.label == "Se connecter").click() | |
| at.run() | |
| assert any("remplir" in w.value for w in at.warning) | |
| def test_login_erreur_si_identifiants_incorrects(): | |
| with patch("src.utils.auth.authenticate", return_value=None): | |
| at = AppTest.from_file(APP_PATH) | |
| at.run() | |
| at.text_input[0].set_value("mauvaisuser") | |
| at.text_input[1].set_value("mauvaismdp") | |
| next(b for b in at.button if b.label == "Se connecter").click() | |
| at.run() | |
| assert any("incorrects" in e.value for e in at.error) | |
| # --- Application principale (utilisateur connecté) --- | |
| def test_app_affiche_le_titre(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| assert at.title[0].value == "Prédiction remboursement de crédit" | |
| def test_app_affiche_champ_sk_id(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| assert len(at.text_input) == 1 | |
| assert "SK_ID_CURR" in at.text_input[0].label | |
| def test_app_affiche_bouton_rechercher(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| assert any(b.label == "Rechercher" for b in at.button) | |
| def test_app_erreur_si_champ_vide(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("invalide" in e.value for e in at.error) | |
| def test_app_erreur_si_saisie_non_numerique(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("abc") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("invalide" in e.value for e in at.error) | |
| def test_app_erreur_si_saisie_decimale(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("123.45") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("invalide" in e.value for e in at.error) | |
| def test_app_erreur_si_entier_hors_limite(): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("56586613315") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("invalide" in e.value for e in at.error) | |
| # proba_class_1 < THRESHOLD → classe 0 (remboursé) | |
| def test_app_client_trouve_credit_rembourse(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 0.97, | |
| "proba_class_1": 0.03, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100001") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("remboursé" in s.value for s in at.success) | |
| assert len(at.metric) == 2 | |
| # proba_class_1 >= THRESHOLD → classe 1 (défaut) | |
| def test_app_client_trouve_defaut_remboursement(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 0.2, | |
| "proba_class_1": 0.8, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100002") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("Défaut" in e.value for e in at.error) | |
| assert len(at.metric) == 2 | |
| def test_app_client_non_trouve(): | |
| mock_client = make_mock_supabase([]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("999999") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("introuvable" in e.value for e in at.error) | |
| def test_app_affiche_les_deux_metriques(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 0.72, | |
| "proba_class_1": 0.28, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100003") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| labels = [m.label for m in at.metric] | |
| assert any("remboursé" in label for label in labels) | |
| assert any("défaut" in label for label in labels) | |
| # Seuil exact : proba juste en dessous → remboursé | |
| def test_seuil_juste_en_dessous_donne_credit_rembourse(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 1 - 0.09, | |
| "proba_class_1": 0.09, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100004") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("remboursé" in s.value for s in at.success) | |
| # Seuil exact : proba égale au seuil → défaut | |
| def test_seuil_egal_donne_defaut(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 1 - THRESHOLD, | |
| "proba_class_1": THRESHOLD, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100005") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("Défaut" in e.value for e in at.error) | |
| def test_app_administrateur_voit_section_debug(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 0.9, | |
| "proba_class_1": 0.1, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_ADMIN_USER) | |
| at.text_input[0].set_value("100001") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert any("Debug" in c.value for c in at.caption) | |
| def test_app_client_ne_voit_pas_section_debug(): | |
| mock_client = make_mock_supabase([{ | |
| "proba_class_0": 0.9, | |
| "proba_class_1": 0.1, | |
| }]) | |
| with patch("src.utils.database.get_client", return_value=mock_client), \ | |
| patch("src.utils.logs.get_client", return_value=mock_client): | |
| at = app_logged_in(MOCK_CLIENT_USER) | |
| at.text_input[0].set_value("100001") | |
| next(b for b in at.button if b.label == "Rechercher").click() | |
| at.run() | |
| assert not any("Debug" in c.value for c in at.caption) | |