DEMOAVISIA / test_app.py
Oxyb's picture
Rename test_app (2).py to test_app.py
6fa45f2 verified
Raw
History Blame Contribute Delete
6.47 kB
"""
test_app.py : tests de non-régression d'Avis'IA Resto
Usage : python test_app.py
Produit un rapport horodaté rapport_tests.txt
Fonctionne avec ou sans clé API Mistral (le mode secours est testé aussi).
"""
import os
from datetime import datetime
import app
RESULTATS = []
def verifier(nom, condition, detail=""):
RESULTATS.append((nom, bool(condition), detail))
print(f"{'✅' if condition else '❌'} {nom}" + (f" ({detail})" if detail else ""))
def main():
print("=" * 60)
print("TESTS AVIS'IA RESTO,", datetime.now().strftime("%d/%m/%Y %H:%M"))
print("=" * 60)
mode_ia = app.client is not None
# 1. Robustesse : avis vide
r, m = app.generer_reponse("", 3, "Test", "Test")
verifier("Avis vide refusé avec message clair", "⚠️" in m)
# 2. Avis négatif : une réponse est toujours produite
r_neg, m_neg = app.generer_reponse("Service très lent, plat froid, très déçu.",
1, "Le Normand Caen", "Marc, gérant")
verifier("Avis négatif : réponse produite", len(r_neg) > 30)
verifier("Mode affiché à l'utilisateur", ("✅" in m_neg) or ("🔶" in m_neg))
# 3. Avis positif
r_pos, m_pos = app.generer_reponse("Excellente tarte normande, accueil parfait !",
5, "Le Normand Caen", "Marc, gérant")
verifier("Avis positif : réponse produite", len(r_pos) > 30)
# 4. Rubrique qualité : longueur, absence de promesses interdites,
# personnalisation (uniquement significative en mode IA)
ok_neg, detail_neg = app._controle_qualite(
"Service très lent, plat froid, très déçu.", r_neg,
verifier_personnalisation=("✅" in m_neg))
verifier("Qualité rubrique : avis négatif", ok_neg, detail_neg)
ok_pos, detail_pos = app._controle_qualite(
"Excellente tarte normande, accueil parfait !", r_pos,
verifier_personnalisation=("✅" in m_pos))
verifier("Qualité rubrique : avis positif", ok_pos, detail_pos)
verifier("Réponse négative différente de la réponse positive",
r_neg.strip() != r_pos.strip())
# 5. Toutes les notes ont un template de secours
verifier("Templates de secours pour les 5 notes",
all(n in app.TEMPLATES for n in range(1, 6)))
# 6. Analyse CSV multi-restaurants (onglet 2), séparateur virgule
exemple = "avis_restaurant_exemple.csv"
if os.path.exists(exemple):
class F:
name = exemple
s, f1, f2, f3, f4, f5, f6, f7 = app.analyser_csv(F())
verifier("CSV multi-restaurants : analyse sans erreur", s.startswith("###"))
figs_ok = all(hasattr(f, "size") and f.size[0] > 0 and f.size[1] > 0
for f in (f1, f2, f3, f4, f5, f6, f7))
verifier("Les 7 graphiques contiennent des données", figs_ok)
fonds_ok = all(f.mode in ("RGB", "RGBA") for f in (f1, f2, f3, f4, f5, f6, f7))
verifier("Les 7 graphiques sont des images valides (RGB/RGBA)", fonds_ok)
else:
verifier("CSV multi-restaurants présent", False, "fichier introuvable")
# 7. Analyse CSV, séparateur point-virgule
if os.path.exists(exemple):
import pandas as pd
pd.read_csv(exemple).to_csv("_test_pv.csv", sep=";", index=False)
class G:
name = "_test_pv.csv"
s2, *_ = app.analyser_csv(G())
verifier("Séparateur point-virgule détecté automatiquement",
s2.startswith("###"))
os.remove("_test_pv.csv")
# 8. CSV d'analyse invalide (colonnes manquantes)
with open("_test_ko.csv", "w", encoding="utf-8") as f:
f.write("colonne_inconnue\nvaleur\n")
class H:
name = "_test_ko.csv"
s3, *_ = app.analyser_csv(H())
verifier("CSV d'analyse invalide : message d'erreur explicite", s3.startswith("❌"))
os.remove("_test_ko.csv")
# 9. Traitement en lot (onglet 1, dépôt CSV) avec le CSV d'exemple
lot = "avis_a_repondre_exemple.csv"
if os.path.exists(lot):
class I:
name = lot
statut_lot, apercu, chemin = app.generer_reponses_batch(I())
verifier("CSV en lot : traitement sans erreur", statut_lot.startswith("✅"))
verifier("CSV en lot : une réponse par ligne",
apercu is not None and len(apercu) > 0
and apercu["reponse_generee"].notna().all())
verifier("CSV en lot : fichier de sortie créé",
chemin is not None and os.path.exists(chemin))
tableau_html = app._df_vers_tableau_html(apercu)
verifier("Tableau HTML : vrai tableau sans coin arrondi",
"<table" in tableau_html and "border-radius: 0" in tableau_html)
else:
verifier("CSV en lot d'exemple présent", False, "fichier introuvable")
# 10. Traitement en lot : colonnes manquantes détectées proprement
with open("_test_lot_ko.csv", "w", encoding="utf-8") as f:
f.write("colonne_inconnue\nvaleur\n")
class J:
name = "_test_lot_ko.csv"
statut_ko, apercu_ko, chemin_ko = app.generer_reponses_batch(J())
verifier("CSV en lot invalide : message d'erreur explicite", statut_ko.startswith("❌"))
os.remove("_test_lot_ko.csv")
# 11. Traitement en lot : fichier absent
statut_vide, _, _ = app.generer_reponses_batch(None)
verifier("CSV en lot absent : message d'avertissement", statut_vide.startswith("⚠️"))
# 12. État de la clé API
statut = "configurée" if mode_ia else "absente, mode secours"
verifier("Gestion de la clé API sans plantage", True, statut)
# Rapport horodaté
ok = sum(1 for _, v, _ in RESULTATS if v)
total = len(RESULTATS)
with open("rapport_tests.txt", "w", encoding="utf-8") as f:
f.write("RAPPORT DE TESTS AVIS'IA RESTO\n")
f.write(f"Date : {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}\n")
f.write(f"Clé API Mistral : {statut}\n")
f.write(f"Résultat : {ok}/{total} tests passés\n")
f.write("-" * 50 + "\n")
for nom, v, detail in RESULTATS:
ligne = f"[{'OK' if v else 'ECHEC'}] {nom}"
if detail:
ligne += f" ({detail})"
f.write(ligne + "\n")
print("-" * 60)
print(f"RÉSULTAT : {ok}/{total} tests passés")
print("Rapport écrit dans rapport_tests.txt")
return 0 if ok == total else 1
if __name__ == "__main__":
raise SystemExit(main())