"""Tests d'endpoints (curseur SQL simulé via la fixture `cur`).""" from auth import hash_password, create_access_token def test_health_ok(client, cur): cur._one = (1,) r = client.get("/health") assert r.status_code == 200 assert r.json()["api"] == "ok" def test_categories_requires_auth(client): # sans token -> 401 assert client.get("/categories").status_code == 401 def test_services_returns_list(client, cur, auth_headers): cur.description = [("id_service",), ("nom_service",)] cur._rows = [(1, "Contrat"), (2, "Fournisseur")] r = client.get("/services", headers=auth_headers) assert r.status_code == 200 assert {"id_service": 1, "nom_service": "Contrat"} in r.json() def test_dashboard_filtres_ne_renvoie_que_les_valeurs_utilisees(client, cur, auth_headers): # VUE_CONSO ne contient que Contrat / Fournisseur : les services du # référentiel sans monitoring rattaché ne doivent pas remonter. cur.description = [("service",), ("categorie",)] cur._rows = [ ("Contrat", "DOM-TOM"), ("Contrat", "Tiers-payeurs"), ("Fournisseur", "Contreparties"), ] r = client.get("/dashboard/filtres", headers=auth_headers) assert r.status_code == 200 body = r.json() assert body["services"] == ["Contrat", "Fournisseur"] assert "Business Intelligence" not in body["services"] assert body["categories"] == ["Contreparties", "DOM-TOM", "Tiers-payeurs"] assert {"service": "Contrat", "categorie": "DOM-TOM"} in body["combinaisons"] def test_auth_login_success(client, cur): cur._one = (1, "admin", hash_password("Admin2026!"), "Admin", 1) r = client.post("/auth/login", data={"username": "admin", "password": "Admin2026!"}) assert r.status_code == 200 body = r.json() assert body["token_type"] == "bearer" assert body["user"]["role"] == "Admin" def test_auth_login_wrong_password(client, cur): cur._one = (1, "admin", hash_password("Admin2026!"), "Admin", 1) r = client.post("/auth/login", data={"username": "admin", "password": "WRONG"}) assert r.status_code == 401 def test_auth_me(client, auth_headers): r = client.get("/auth/me", headers=auth_headers) assert r.status_code == 200 assert r.json()["username"] == "admin" def test_monitoring_unknown_returns_404(client, cur, auth_headers): r = client.get("/monitorings/999/details", headers=auth_headers) assert r.status_code == 404 def test_create_user_role_invalide_rejete(client, cur, auth_headers): # Le rôle est contraint par l'enum UserRole : Pydantic rejette en 422 # avant d'atteindre la base, sans validation manuelle dans la route. r = client.post( "/admin/users", headers=auth_headers, json={"username": "x", "email": "x@x.fr", "password": "p", "role": "Root"}, ) assert r.status_code == 422 def test_create_user_role_valide_accepte(client, cur, auth_headers): r = client.post( "/admin/users", headers=auth_headers, json={"username": "x", "email": "x@x.fr", "password": "p", "role": "Superviseur"}, ) assert r.status_code == 201 def test_creation_service_reservee_admin(client, cur): # Un Consultant ne doit pas pouvoir écrire dans le référentiel. token = create_access_token({"sub": "bob", "role": "Consultant", "uid": 2}) r = client.post( "/services", headers={"Authorization": f"Bearer {token}"}, json={"nom_service": "Nouveau"}, ) assert r.status_code == 403 def test_creation_service_par_admin(client, cur, auth_headers): cur._one = (42,) r = client.post("/services", headers=auth_headers, json={"nom_service": "Comptabilité"}) assert r.status_code == 201 assert r.json() == {"id_service": 42, "nom_service": "Comptabilité"} def test_suppression_service_refusee_si_rattachements(client, cur, auth_headers): # 2 monitorings et 1 contact rattachés : la suppression doit être bloquée # explicitement plutôt que de laisser remonter une violation de clé étrangère. cur._one = (2, 1) r = client.delete("/services/1", headers=auth_headers) assert r.status_code == 409 assert "2 monitoring(s)" in r.json()["detail"] def test_suppression_service_sans_rattachement(client, cur, auth_headers): cur._one = (0, 0) r = client.delete("/services/1", headers=auth_headers) assert r.status_code == 200 assert r.json()["status"] == "deleted" def test_desactivation_monitoring_ne_supprime_pas(client, cur, auth_headers): # TABLE_FINAL référence id_monito : on désactive, on ne supprime jamais. r = client.delete("/monitorings/1", headers=auth_headers) assert r.status_code == 200 assert r.json()["status"] == "deactivated" requetes = " ".join(q for q, _ in cur.executed) assert "UPDATE NOMENCLATURE_MONITO SET actif = 0" in requetes assert "DELETE FROM NOMENCLATURE_MONITO" not in requetes def test_admin_forbidden_for_consultant(client): token = create_access_token({"sub": "bob", "role": "Consultant", "uid": 2}) r = client.get("/admin/users", headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 403