""" Test de l'api justificatif Ecrit par HARTMANN Matthias """ from random import randint import requests from tests.api.setup_test_api import ( API_URL, CHECK_CERTIFICATE, GET, POST_JSON, APIError, api_headers, api_admin_headers, ) ETUDID = 1 FAUX = 42069 JUSTIFICATIFS_FIELDS = { "justif_id": int, "etudid": int, "code_nip": str, "date_debut": str, "date_fin": str, "etat": str, "raison": str, "entry_date": str, "fichier": str, "user_id": int | None, "user_name": str | None, "user_nom_complet": str | None, "external_data": dict, } CREATE_FIELD = {"justif_id": int, "couverture": list} BATCH_FIELD = {"errors": list, "success": list} TO_REMOVE = [] def check_fields(data, fields: dict = None): """ Cette fonction permet de vérifier que le dictionnaire data contient les bonnes clés et les bons types de valeurs. Args: data (dict): un dictionnaire (json de retour de l'api) fields (dict, optional): Un dictionnaire représentant les clés et les types d'une réponse. """ if fields is None: fields = JUSTIFICATIFS_FIELDS assert set(data.keys()) == set(fields.keys()) for key in data: if key in ("raison", "fichier", "user_id", "external_data"): assert ( isinstance(data[key], fields[key]) or data[key] is None ), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]" else: assert isinstance( data[key], fields[key] ), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]" def check_failure_get(path, headers, err=None): """ Cette fonction vérifiée que la requête GET renvoie bien un 404 Args: path (str): la route de l'api headers (dict): le token d'auth de l'api err (str, optional): L'erreur qui est sensée être fournie par l'api. Raises: APIError: Une erreur car la requête a fonctionné (mauvais comportement) """ try: GET(path=path, headers=headers) # ^ Renvoi un 404 except APIError as api_err: if err is not None: assert api_err.payload["message"] == err else: raise APIError("Le GET n'aurait pas du fonctionner") def check_failure_post(path, headers, data, err=None): """ Cette fonction vérifiée que la requête POST renvoie bien un 404 Args: path (str): la route de l'api headers (dict): le token d'auth data (dict): un dictionnaire (json) à envoyer err (str, optional): L'erreur qui est sensée être fournie par l'api. Raises: APIError: Une erreur car la requête a fonctionné (mauvais comportement) """ try: data = POST_JSON(path=path, headers=headers, data=data) # ^ Renvoi un 404 except APIError as api_err: if err is not None: assert api_err.payload["message"] == err else: raise APIError("Le POST n'aurait pas du fonctionner") def create_data(etat: str, day: str, raison: str = None): """ Permet de créer un dictionnaire assiduité Args: etat (str): l'état du justificatif (VALIDE,NON_VALIDE,MODIFIE, ATTENTE) day (str): Le jour du justificatif raison (str, optional): Une description du justificatif (eg: motif retard ) Returns: dict: la représentation d'une assiduité """ data = { "date_debut": f"2022-01-{day}T08:00", "date_fin": f"2022-01-{day}T10:00", "etat": etat, } if raison is not None: data["desc"] = raison return data def test_route_justificatif(api_headers): """test de la route /justificatif/""" # Bon fonctionnement == id connu data = GET(path="/justificatif/1", headers=api_headers) check_fields(data) # Mauvais Fonctionnement == id inconnu check_failure_get( f"/justificatif/{FAUX}", api_headers, ) def test_route_justificatifs(api_headers): """test de la route /justificatifs/""" # Bon fonctionnement data = GET(path=f"/justificatifs/{ETUDID}", headers=api_headers) assert isinstance(data, list) for just in data: check_fields(just, JUSTIFICATIFS_FIELDS) data = GET(path=f"/justificatifs/{ETUDID}/query?", headers=api_headers) assert isinstance(data, list) for just in data: check_fields(just, JUSTIFICATIFS_FIELDS) # Mauvais fonctionnement check_failure_get(f"/justificatifs/{FAUX}", api_headers) check_failure_get(f"/justificatifs/{FAUX}/query?", api_headers) def test_route_create(api_admin_headers): """test de la route /justificatif//create""" # -== Unique ==- # Bon fonctionnement data = create_data("valide", "01") res = POST_JSON(f"/justificatif/{ETUDID}/create", [data], api_admin_headers) check_fields(res, BATCH_FIELD) assert len(res["success"]) == 1 TO_REMOVE.append(res["success"][0]["message"]["justif_id"]) data2 = create_data("modifie", "02", "raison") res = POST_JSON(f"/justificatif/{ETUDID}/create", [data2], api_admin_headers) check_fields(res, BATCH_FIELD) assert len(res["success"]) == 1 TO_REMOVE.append(res["success"][0]["message"]["justif_id"]) # Mauvais fonctionnement check_failure_post(f"/justificatif/{FAUX}/create", api_admin_headers, [data]) res = POST_JSON( f"/justificatif/{ETUDID}/create", [create_data("absent", "03")], api_admin_headers, ) check_fields(res, BATCH_FIELD) assert len(res["errors"]) == 1 assert res["errors"][0]["message"] == "param 'etat': invalide" # -== Multiple ==- # Bon Fonctionnement etats = ["valide", "modifie", "non_valide", "attente"] data = [ create_data(etats[d % 4], 10 + d, "raison" if d % 2 else None) for d in range(randint(3, 5)) ] res = POST_JSON(f"/justificatif/{ETUDID}/create", data, api_admin_headers) check_fields(res, BATCH_FIELD) for dat in res["success"]: check_fields(dat["message"], CREATE_FIELD) TO_REMOVE.append(dat["message"]["justif_id"]) # Mauvais Fonctionnement data2 = [ create_data(None, "25"), create_data("blabla", 26), create_data("valide", 32), ] res = POST_JSON(f"/justificatif/{ETUDID}/create", data2, api_admin_headers) check_fields(res, BATCH_FIELD) assert len(res["errors"]) == 3 assert res["errors"][0]["message"] == "param 'etat': manquant" assert res["errors"][1]["message"] == "param 'etat': invalide" assert ( res["errors"][2]["message"] == "param 'date_debut': format invalide, param 'date_fin': format invalide" ) def test_route_edit(api_admin_headers): """test de la route /justificatif//edit""" # Bon fonctionnement data = {"etat": "modifie", "raison": "test"} res = POST_JSON(f"/justificatif/{TO_REMOVE[0]}/edit", data, api_admin_headers) assert isinstance(res, dict) and "couverture" in res.keys() data["raison"] = None res = POST_JSON(f"/justificatif/{TO_REMOVE[1]}/edit", data, api_admin_headers) assert isinstance(res, dict) and "couverture" in res.keys() # Mauvais fonctionnement check_failure_post(f"/justificatif/{FAUX}/edit", api_admin_headers, data) data["etat"] = "blabla" check_failure_post( f"/justificatif/{TO_REMOVE[2]}/edit", api_admin_headers, data, err="param 'etat': invalide", ) def test_route_delete(api_admin_headers): """test de la route /justificatif/delete""" # -== Unique ==- # Bon fonctionnement data = TO_REMOVE[0] res = POST_JSON("/justificatif/delete", [data], api_admin_headers) check_fields(res, BATCH_FIELD) for dat in res["success"]: assert dat["message"] == "OK" # Mauvais fonctionnement res = POST_JSON("/justificatif/delete", [data], api_admin_headers) check_fields(res, BATCH_FIELD) assert len(res["errors"]) == 1 # -== Multiple ==- # Bon Fonctionnement data = TO_REMOVE[1:] res = POST_JSON("/justificatif/delete", data, api_admin_headers) check_fields(res, BATCH_FIELD) for dat in res["success"]: assert dat["message"] == "OK" # Mauvais Fonctionnement data2 = [ FAUX, FAUX + 1, FAUX + 2, ] res = POST_JSON("/justificatif/delete", data2, api_admin_headers) check_fields(res, BATCH_FIELD) assert len(res["errors"]) == 3 assert all(i["message"] == "Justificatif non existant" for i in res["errors"]) # Gestion de l'archivage def _send_file(justif_id: int, filename: str, headers): """ Envoi un fichier vers la route d'importation """ with open(filename, "rb") as file: url: str = API_URL + f"/justificatif/{justif_id}/import" req = requests.post( url, files={filename: file}, headers=headers, verify=CHECK_CERTIFICATE, timeout=30, ) if req.status_code != 200: raise APIError(f"erreur status={req.status_code} !", req.json()) return req.json() def _check_failure_send( justif_id: int, headers, filename: str = "tests/api/test_api_justificatif.txt", err: str = None, ): """ Vérifie si l'envoie d'un fichier renvoie bien un 404 Args: justif_id (int): l'id du justificatif headers (dict): token d'auth de l'api filename (str, optional): le chemin vers le fichier. Defaults to "tests/api/test_api_justificatif.txt". err (str, optional): l'erreur attendue. Raises: APIError: Si l'envoie fonction (mauvais comportement) """ try: _send_file(justif_id, filename, headers) # ^ Renvoi un 404 except APIError as api_err: if err is not None: assert api_err.payload["message"] == err else: raise APIError("Le POST n'aurait pas du fonctionner") def test_import_justificatif(api_admin_headers): """test de la route /justificatif//import""" # Bon fonctionnement filename: str = "tests/api/test_api_justificatif.txt" resp: dict = _send_file(1, filename, api_admin_headers) assert "filename" in resp assert resp["filename"] == "test_api_justificatif.txt" filename: str = "tests/api/test_api_justificatif2.txt" resp: dict = _send_file(1, filename, api_admin_headers) assert "filename" in resp assert resp["filename"] == "test_api_justificatif2.txt" # Mauvais fonctionnement _check_failure_send(FAUX, api_admin_headers) def test_list_justificatifs(api_admin_headers): """test de la route /justificatif//list""" # Bon fonctionnement res: list = GET("/justificatif/1/list", api_admin_headers) assert isinstance(res, dict) assert len(res["filenames"]) == 2 assert res["total"] == 2 res: list = GET("/justificatif/2/list", api_admin_headers) assert isinstance(res, dict) assert len(res["filenames"]) == 0 assert res["total"] == 0 # Mauvais fonctionnement check_failure_get(f"/justificatif/{FAUX}/list", api_admin_headers) def _post_export(justif_id: int, fname: str, api_headers): """ Envoie une requête poste sans data et la retourne Args: id (int): justif_id fname (str): nom du fichier (coté serv) api_headers (dict): token auth de l'api Returns: request: la réponse de l'api """ url: str = API_URL + f"/justificatif/{justif_id}/export/{fname}" res = requests.post(url, headers=api_headers) return res def test_export(api_admin_headers): """test de la route /justificatif//export/""" # Bon fonctionnement assert ( _post_export(1, "test_api_justificatif.txt", api_admin_headers).status_code == 200 ) # Mauvais fonctionnement assert ( _post_export(FAUX, "test_api_justificatif.txt", api_admin_headers).status_code == 404 ) assert _post_export(1, "blabla.txt", api_admin_headers).status_code == 404 assert _post_export(2, "blabla.txt", api_admin_headers).status_code == 404 def test_remove_justificatif(api_admin_headers): """test de la route /justificatif//remove""" # Bon fonctionnement filename: str = "tests/api/test_api_justificatif.txt" _send_file(2, filename, api_admin_headers) filename: str = "tests/api/test_api_justificatif2.txt" _send_file(2, filename, api_admin_headers) res: dict = POST_JSON( "/justificatif/1/remove", {"remove": "all"}, api_admin_headers ) assert res == {"response": "removed"} l = GET("/justificatif/1/list", api_admin_headers) assert isinstance(l, dict) assert l["total"] == 0 res: dict = POST_JSON( "/justificatif/2/remove", {"remove": "list", "filenames": ["test_api_justificatif2.txt"]}, api_admin_headers, ) assert res == {"response": "removed"} l = GET("/justificatif/2/list", api_admin_headers) assert isinstance(l, dict) assert l["total"] == 1 res: dict = POST_JSON( "/justificatif/2/remove", {"remove": "list", "filenames": ["test_api_justificatif.txt"]}, api_admin_headers, ) assert res == {"response": "removed"} l = GET("/justificatif/2/list", api_admin_headers) assert isinstance(l, dict) assert l["total"] == 0 # Mauvais fonctionnement check_failure_post("/justificatif/2/remove", api_admin_headers, {}) check_failure_post( f"/justificatif/{FAUX}/remove", api_admin_headers, {"remove": "all"} ) check_failure_post("/justificatif/1/remove", api_admin_headers, {"remove": "all"}) def test_justifies(api_admin_headers): """test la route /justificatif//justifies""" # Bon fonctionnement res: list = GET("/justificatif/1/justifies", api_admin_headers) assert isinstance(res, list) # Mauvais fonctionnement check_failure_get(f"/justificatif/{FAUX}/justifies", api_admin_headers)