github-actions
Deploy to HF
0f82473
Raw
History Blame Contribute Delete
8.25 kB
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)