From c6a99dc7d2d9a81231fe91eeff511cdd733db4b9 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 19 Aug 2022 12:46:21 +0200 Subject: [PATCH 1/2] API users: password, plus de tests unitaires, correctifs. --- app/api/users.py | 35 ++++++++++- tests/api/exemple-api-basic.py | 1 + tests/api/setup_test_api.py | 8 ++- tests/api/test_api_users.py | 102 ++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 6 deletions(-) diff --git a/app/api/users.py b/app/api/users.py index 8b34901c7..ee2cce248 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -14,9 +14,9 @@ from flask_login import current_user, login_required from app import db, log from app.api import api_bp as bp, api_web_bp -from app.models.etudiants import Identite from app.scodoc.sco_utils import json_error from app.auth.models import User, Role, UserRole +from app.auth.models import is_valid_password from app.decorators import scodoc, permission_required from app.models import Departement from app.scodoc.sco_exceptions import ScoValueError @@ -38,7 +38,7 @@ def user_info(uid: int): return json_error(404, "user not found") if g.scodoc_dept: allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersView) - if user.dept not in allowed_depts: + if (None not in allowed_depts) and (user.dept not in allowed_depts): return json_error(404, "user not found") return jsonify(user.to_dict()) @@ -109,7 +109,7 @@ def user_create(): if dept == "@all": dept = None allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) - if dept not in allowed_depts: + if (None not in allowed_depts) and (dept not in allowed_depts): return json_error(403, "user_create: departement non autorise") if (dept is not None) and ( Departement.query.filter_by(acronym=dept).first() is None @@ -168,6 +168,35 @@ def user_edit(uid: int): return jsonify(user.to_dict()) +@bp.route("/user//password", methods=["POST"]) +@api_web_bp.route("/user//password", methods=["POST"]) +@login_required +@scodoc +@permission_required(Permission.ScoUsersAdmin) +def user_password(uid: int): + """Modification du mot de passe d'un utilisateur + Champs modifiables: + { + "password": str + } + Si le mot de passe ne convient pas, erreur 400. + """ + data = request.get_json(force=True) # may raise 400 Bad Request + user: User = User.query.get_or_404(uid) + password = data.get("password") + if not password: + return json_error(404, "user_password: missing password") + if not is_valid_password(password): + return json_error(400, "user_password: invalid password") + allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) + if (None not in allowed_depts) and ((user.dept not in allowed_depts)): + return json_error(403, "user_password: departement non autorise") + user.set_password(password) + db.session.add(user) + db.session.commit() + return jsonify(user.to_dict()) + + @bp.route("/user//role//add", methods=["POST"]) @api_web_bp.route("/user//role//add", methods=["POST"]) @bp.route( diff --git a/tests/api/exemple-api-basic.py b/tests/api/exemple-api-basic.py index 022065a59..0ef58af47 100644 --- a/tests/api/exemple-api-basic.py +++ b/tests/api/exemple-api-basic.py @@ -51,6 +51,7 @@ print(f"API URL={API_URL}") HEADERS = get_auth_headers(API_USER, API_PASSWORD) +admin_h = get_auth_headers("admin_api", "admin_api") departements = GET("/departements", headers=HEADERS) pp(departements) diff --git a/tests/api/setup_test_api.py b/tests/api/setup_test_api.py index fb7517ce8..bc1a58fbc 100644 --- a/tests/api/setup_test_api.py +++ b/tests/api/setup_test_api.py @@ -76,10 +76,14 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None): return r.json() # decode la reponse JSON -def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None): +def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None, dept=None): """Post""" + if dept: + url = SCODOC_URL + f"/ScoDoc/{dept}/api" + path + else: + url = API_URL + path r = requests.post( - API_URL + path, + url, json=data, headers=headers or {}, verify=CHECK_CERTIFICATE, diff --git a/tests/api/test_api_users.py b/tests/api/test_api_users.py index 82c6cdefc..e9fe5aa4b 100644 --- a/tests/api/test_api_users.py +++ b/tests/api/test_api_users.py @@ -8,6 +8,7 @@ Utilisation : from tests.api.setup_test_api import ( API_URL, + APIError, CHECK_CERTIFICATE, GET, POST_JSON, @@ -83,7 +84,7 @@ def test_edit_users(api_admin_headers): assert (nb_users + 1) == len(GET("/users/query", headers=admin_h)) # Change le dept et rend inactif user = POST_JSON( - f"/user/edit/{user['id']}", + f"/user/{user['id']}/edit", {"active": False, "dept": "TAPI"}, headers=admin_h, ) @@ -129,3 +130,102 @@ def test_roles(api_admin_headers): assert set(role["permissions"]) == {"ScoView", "ScoAbsAddBillet"} ans = POST_JSON("/role/Test_Y/delete", headers=admin_h) assert ans["OK"] is True + + +def test_modif_users_depts(api_admin_headers): + """ + Ce test vise à vérifier qu'un admin déclaré sur deux départements peut + bien modifier les utilisateurs de ses départements mais pas ceux des autres. + """ + admin_h = api_admin_headers + depts = GET("/departements", headers=admin_h) + assert len(depts) > 2 + dept1, dept2, dept3 = depts[:3] + # On va utiliser les 3 1er dept (TAPI, AA, BB) + # On crée un nouvel utilisateur "chef2", admin dans les 2 premiers dept + # puis un utilisateur lambda, dans le dept 2 (AA) + chef2 = POST_JSON( + "/user/create", + { + "user_name": "chef2", + "nom": "Chef", + "prenom": "Test", + "dept": dept1["acronym"], # rattaché à dept1 + }, + headers=admin_h, + ) + role_chef = POST_JSON( + "/role/create/chef", + {"permissions": ["ScoView", "ScoUsersAdmin", "ScoUsersView"]}, + headers=admin_h, + ) + _ = POST_JSON( + f"/user/{chef2['id']}/role/chef/add/departement/{dept1['acronym']}", + headers=admin_h, + ) + _ = POST_JSON( + f"/user/{chef2['id']}/role/chef/add/departement/{dept2['acronym']}", + headers=admin_h, + ) + # Un mot de passe trop simple: + ok = False + try: + _ = POST_JSON( + f"/user/{chef2['id']}/password", + {"password": "123456"}, + headers=admin_h, + ) + except APIError as exc: + if exc.args[1]["status"] == 400: + ok = True + assert ok + # Un "vrai" mot de passe: + chef2_password = "17HIOPpYhabb8qw'E:/jd7FFddjd" + _ = POST_JSON( + f"/user/{chef2['id']}/password", + {"password": chef2_password}, + headers=admin_h, + ) + # Création user lambda: + u_lambda = POST_JSON( + "/user/create", + { + "user_name": "lambda", + "nom": "Lambda", + "prenom": "Test", + "dept": dept2["acronym"], + }, + headers=admin_h, + ) + + # Le chef va modifier u_lambda: + chef_h = get_auth_headers(chef2["user_name"], chef2_password) + # on utilise une URL avec département car on n'a pas le droit sur tous: + u = POST_JSON( + f"/user/{u_lambda['id']}/edit", + {"nom": "toto"}, + headers=chef_h, + dept=dept1["acronym"], + ) + assert u["nom"] == "toto" + # Dans l'autre ? + u = POST_JSON( + f"/user/{u_lambda['id']}/edit", + {"nom": "toto"}, + headers=chef_h, + dept=dept2["acronym"], + ) + # mais pas dans le troisième: + ok = False + try: + u = POST_JSON( + f"/user/{u_lambda['id']}/edit", + {"nom": "toto"}, + headers=chef_h, + dept=dept3["acronym"], + ) + except APIError as exc: + if exc.args[1]["status"] == 401: + ok = True + assert ok + # Nettoyage: From 60aba5a878aeee622401152e5e96c4c9f05bf673 Mon Sep 17 00:00:00 2001 From: Jean-Marie PLACE Date: Sun, 21 Aug 2022 09:17:45 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix=20api=20logo=20selon=20conventions=20+?= =?UTF-8?q?=20jeux=20de=20test=20+=20normalise=20des=20samples=20pour=20do?= =?UTF-8?q?c=20par=20post-traitement=20des=20r=C3=A9sultats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/logos.py | 56 ++++--- tests/api/make_samples.py | 57 +++++-- tests/api/samples.csv | 24 ++- tests/api/setup_test_api.py | 21 ++- tests/api/test_api_logos.py | 142 +++++++++++++++--- .../fakedatabase/create_test_api_database.py | 28 ++++ 6 files changed, 268 insertions(+), 60 deletions(-) diff --git a/app/api/logos.py b/app/api/logos.py index ef3a7f973..49c0619c6 100644 --- a/app/api/logos.py +++ b/app/api/logos.py @@ -46,23 +46,16 @@ from app.scodoc.sco_permissions import Permission @bp.route("/logos") @scodoc -@permission_required(Permission.ScoView) +@permission_required(Permission.ScoSuperAdmin) def api_get_glob_logos(): - if not g.current_user.has_permission(Permission.ScoSuperAdmin, None): - return json_error(403, message="accès interdit") - required_format = requested_format() # json only - if required_format is None: - return json_error(400, "Illegal format") logos = list_logos()[None] return jsonify(list(logos.keys())) -@bp.route("/logos/") +@bp.route("/logo/") @scodoc -@permission_required(Permission.ScoView) +@permission_required(Permission.ScoSuperAdmin) def api_get_glob_logo(logoname): - if not g.current_user.has_permission(Permission.ScoSuperAdmin, None): - return json_error(403, message="accès interdit") logo = find_logo(logoname=logoname) if logo is None: return json_error(404, message="logo not found") @@ -74,25 +67,27 @@ def api_get_glob_logo(logoname): ) -@bp.route("/departements//logos") -@scodoc -@permission_required(Permission.ScoView) -def api_get_local_logos(departement): - dept_id = Departement.from_acronym(departement).id - if not g.current_user.has_permission(Permission.ScoChangePreferences, departement): - return json_error(403, message="accès interdit") +def core_get_logos(dept_id): logos = list_logos().get(dept_id, dict()) return jsonify(list(logos.keys())) -@bp.route("/departements//logos/") +@bp.route("/departement//logos") @scodoc -@permission_required(Permission.ScoView) -def api_get_local_logo(departement, logoname): - # format = requested_format("jpg", ['png', 'jpg']) XXX ? +@permission_required(Permission.ScoSuperAdmin) +def api_get_local_logos_by_acronym(departement): dept_id = Departement.from_acronym(departement).id - if not g.current_user.has_permission(Permission.ScoChangePreferences, departement): - return json_error(403, message="accès interdit") + return core_get_logos(dept_id) + + +@bp.route("/departement/id//logos") +@scodoc +@permission_required(Permission.ScoSuperAdmin) +def api_get_local_logos_by_id(dept_id): + return core_get_logos(dept_id) + + +def core_get_logo(dept_id, logoname): logo = find_logo(logoname=logoname, dept_id=dept_id) if logo is None: return json_error(404, message="logo not found") @@ -102,3 +97,18 @@ def api_get_local_logo(departement, logoname): mimetype=f"image/{logo.suffix}", last_modified=datetime.now(), ) + + +@bp.route("/departement//logo/") +@scodoc +@permission_required(Permission.ScoSuperAdmin) +def api_get_local_logo_dept_by_acronym(departement, logoname): + dept_id = Departement.from_acronym(departement).id + return core_get_logo(dept_id, logoname) + + +@bp.route("/departement/id//logo/") +@scodoc +@permission_required(Permission.ScoSuperAdmin) +def api_get_local_logo_dept_by_id(dept_id, logoname): + return core_get_logo(dept_id, logoname) diff --git a/tests/api/make_samples.py b/tests/api/make_samples.py index fbb198283..614b40c47 100644 --- a/tests/api/make_samples.py +++ b/tests/api/make_samples.py @@ -6,8 +6,9 @@ Usage: cd /opt/scodoc/tests/api - python make_samples.py + python make_samples.py [entry_names] +si entry_names est spécifié, la génération est restreints aux exemples cités. expl: `python make_samples departements departement-formsemestres` doit être exécutée immédiatement apres une initialisation de la base pour test API! (car dépendant des identifiants générés lors de la création des objets) cd /opt/scodoc/tests/api tools/create_database.sh --drop SCODOC_TEST_API && flask db upgrade &&flask sco-db-init --erase && flask init-test-database @@ -41,6 +42,8 @@ TODO: ajouter un argument au script permettant de ne générer qu'un seul fichie """ import os import shutil +import sys +import re from collections import defaultdict from pprint import pprint as pp from pprint import pformat as pf @@ -69,6 +72,7 @@ class Sample: self.url = url self.method = method self.result = None + self.output = "json" if permission == "ScoView": HEADERS = get_auth_headers("test", "test") elif permission == "ScoSuperAdmin": @@ -87,16 +91,16 @@ class Sample: self.result = POST_JSON(self.url, json.loads(self.content), HEADERS) elif self.method[0] != "#": raise Exception(f"Bad method : {self.method}") - else: # method begin with # => comment - print(" pass") self.shorten() file = open(f"sample_TEST.json.md", "tw") self.dump(file) file.close() - def _shorten(self, item): + def _shorten( + self, item + ): # abrege les longues listes (limite à 2 éléments et affiche "... etc. à la place" if isinstance(item, list): - return [self._shorten(child) for child in item[:2]] + return [self._shorten(child) for child in item[:2]] + ["... etc."] return item def shorten(self): @@ -117,19 +121,36 @@ class Sample: file.write(f"> `{self.content}`\n\n") file.write("```json\n") - file.write(json.dumps(self.result, indent=4)) + content = json.dumps(self.result, indent=4, sort_keys=True) + content = content.replace("... etc.", "...") + # regexp for date like: "2022-08-14T10:01:44.043869+02:00" + regexp = re.compile( + r'"(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?"' + ) + content = regexp.sub('"2022-08-20T12:00:00.000000+02:00"', content) + file.write(content) file.write("\n```\n\n") class Samples: - def __init__(self): + def __init__(self, entry_names): + """Entry_names: la liste des entrées à reconstruire. + si None, la totalité des lignes de samples.csv est prise en compte + """ self.entries = defaultdict(lambda: set()) + self.entry_names = entry_names def add_sample(self, entry, url, method="GET", permission="ScoView", content=None): - show_content = "" if content == "" else f": '{content}'" - print(f"{entry:50} {method:5} {url:50} {show_content}") - sample = Sample(url, method, permission, content) - self.entries[entry].add(sample) + if self.entry_names is None or entry in self.entry_names: + if method[0] == "#": + detail = "**ignored**" + elif content == "": + detail = "" + else: + detail = f": {content}" + print(f"{entry:50} {method:5} {url:50} {detail}") + sample = Sample(url, method, permission, content) + self.entries[entry].add(sample) def pp(self): for entry, samples in self.entries.items(): @@ -141,12 +162,18 @@ class Samples: for entry, samples in self.entries.items(): file = open(f"{DATA_DIR}sample_{entry}.json.md", "tw") file.write(f"### {entry}\n\n") - for sample in samples: + for sample in sorted( + samples, key=lambda s: s.url + ): # sorted de façon à rendre le fichier résultat déterministe (i.e. indépendant de l ordre d arrivée des résultats) sample.dump(file) file.close() def make_samples(): + if len(sys.argv) == 1: + entry_names = None + else: + entry_names = sys.argv[1:] if os.path.exists(DATA_DIR): if not os.path.isdir(DATA_DIR): raise f"{DATA_DIR} existe déjà et n'est pas un répertoire" @@ -157,9 +184,9 @@ def make_samples(): else: os.mkdir("/tmp/samples") - samples = Samples() - # samples.pp() - with open("samples.csv") as f: + samples = Samples(entry_names) + samples_file = os.path.dirname(__file__) + "/samples.csv" + with open(samples_file) as f: L = [x[:-1].split("\t") for x in f] for line in L[1:]: entry_name = line[0] diff --git a/tests/api/samples.csv b/tests/api/samples.csv index 341bda1e8..864591b43 100644 --- a/tests/api/samples.csv +++ b/tests/api/samples.csv @@ -26,6 +26,7 @@ etudiant_formsemestres /etudiant/nip/11/formsemestres GET etudiant-formsemestre-bulletin /etudiant/etudid/11/formsemestre/1/bulletin GET etudiant-formsemestre-bulletin /etudiant/ine/INE11/formsemestre/1/bulletin GET etudiant-formsemestre-bulletin /etudiant/nip/11/formsemestre/1/bulletin GET +etudiant-formsemestre-bulletin /etudiant/nip/11/formsemestre/1/bulletin/short/pdf GET etudiant-formsemestre-groups /etudiant/etudid/11/formsemestre/1/groups GET formations /formations GET formations_ids /formations_ids GET @@ -67,11 +68,30 @@ partition-groups-order /partition/1/groups/order ScoSuperAdmin POST [ 1 ] partition-delete /partition/2/delete ScoSuperAdmin POST user-create /user/create ScoSuperAdmin POST {"user_name": "alain", "dept": null, "nom": "alain", "prenom": "bruno", "active": true } user-edit /user/10/edit ScoSuperAdmin POST { "dept": "TAPI", "nom": "alain2", "prenom": "bruno2", "active": false } -user-role-add /user/10/role/Observateur/add ScoSuperAdmin POST +user-password /user/3/password ScoSuperAdmin POST { "password": "rePlaCemeNT456averylongandcomplicated" } +user-password /user/3/password ScoSuperAdmin POST { "password": "too_simple" } +user-role-add /user/10/role/Observateur/add ScoSuperAdmin POST user-role-remove /user/10/role/Observateur/remove ScoSuperAdmin POST role-create /role/create/customRole ScoSuperAdmin POST {"permissions": ["ScoView", "ScoUsersView"]} role-remove_permission /role/customRole/remove_permission/ScoUsersView ScoSuperAdmin POST role-add_permission /role/customRole/add_permission/ScoUsersView ScoSuperAdmin POST role-edit /role/customRole/edit ScoSuperAdmin POST { "name" : "LaveurDeVitres", "permissions" : [ "ScoView", "APIView" ] } -role-edit /role/customRole/edit ScoSuperAdmin POST { "name" : "LaveurDeVitres", "permissions" : [ "ScoView", "APIView" ] } role-delete /role/customRole/delete ScoSuperAdmin POST +logos /logos ScoSuperAdmin GET +logo /logo/demo ScoSuperAdmin GET +departement-logos /departement/TAPI/logos ScoSuperAdmin GET +departement-logos /departement/id/1/logos ScoSuperAdmin GET +departement-logo /departement/TAPI/logo/demo ScoSuperAdmin GET +departement-logo /departement/id/1/logo/demo ScoSuperAdmin GET +test-pdf /etudiant/nip/11/formsemestre/1/bulletin/pdf GET +test-pdf /etudiant/nip/11/formsemestre/1/bulletin/pdf GET +test-pdf /etudiant/etudid/11/formsemestre/1/bulletin/short/pdf GET +test-pdf /etudiant/ine/INE11/formsemestre/1/bulletin/short/pdf GET +test-pdf /etudiant/nip/11/formsemestre/1/bulletin/short/pdf GET +test-pdf /etudiant/etudid/11/formsemestre/1/bulletin/pdf GET +test-pdf /etudiant/etudid/11/formsemestre/1/bulletin/short GET +test-pdf /etudiant/ine/INE11/formsemestre/1/bulletin/short GET +test-pdf /etudiant/nip/11/formsemestre/1/bulletin/short GET +test-pdf /etudiant/etudid/11/formsemestre/1/bulletin GET +test-pdf /etudiant/ine/INE11/formsemestre/1/bulletin GET +test-pdf /etudiant/nip/11/formsemestre/1/bulletin GET diff --git a/tests/api/setup_test_api.py b/tests/api/setup_test_api.py index bc1a58fbc..ab41f8308 100644 --- a/tests/api/setup_test_api.py +++ b/tests/api/setup_test_api.py @@ -65,7 +65,9 @@ def api_admin_headers() -> dict: def GET(path: str, headers: dict = None, errmsg=None, dept=None): - """Get and returns as JSON""" + """Get and returns as JSON + Special case for non json result (image or pdf): return Content-Disposition string (inline or attachment) + """ if dept: url = SCODOC_URL + f"/ScoDoc/{dept}/api" + path else: @@ -73,6 +75,23 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None): r = requests.get(url, headers=headers or {}, verify=CHECK_CERTIFICATE) if r.status_code != 200: raise APIError(errmsg or f"""erreur status={r.status_code} !""", r.json()) + + if r.headers.get("Content-Type", None) == "application/json": + return r.json() # decode la reponse JSON + elif r.headers.get("Content-Type", None) in [ + "image/jpg", + "image/png", + "application/pdf", + ]: + retval = { + "Content-Type": r.headers.get("Content-Type", None), + "Content-Disposition": r.headers.get("Content-Disposition", None), + } + return retval + else: + raise APIError( + "Unknown returned content {r.headers.get('Content-Type', None} !\n" + ) return r.json() # decode la reponse JSON diff --git a/tests/api/test_api_logos.py b/tests/api/test_api_logos.py index d12ceb98c..f8b497579 100644 --- a/tests/api/test_api_logos.py +++ b/tests/api/test_api_logos.py @@ -14,7 +14,7 @@ utilisation: # Ce test a une logique très différente des autres : A UNIFIER -from tests.api.setup_test_api import API_URL +from tests.api.setup_test_api import API_URL, api_admin_headers, api_headers from scodoc import app from tests.unit.config_test_logos import ( @@ -26,48 +26,152 @@ from tests.unit.config_test_logos import ( ) -def test_super_access(create_super_token): +def test_super_access(api_admin_headers): """ Route: /logos """ - dept1, dept2, dept3, token = create_super_token - headers = {"Authorization": f"Bearer {token}"} - with app.test_client() as client: + headers = api_admin_headers + with app.test_client(api_admin_headers) as client: response = client.get(API_URL + "/logos", headers=headers) assert response.status_code == 200 assert response.json is not None -def test_admin_access(create_admin_token): +def test_admin_access(api_headers): """ - Route: + Route: /logos """ - dept1, dept2, dept3, token = create_admin_token - headers = {"Authorization": f"Bearer {token}"} + headers = api_headers with app.test_client() as client: response = client.get(API_URL + "/logos", headers=headers) - assert response.status_code == 403 + assert response.status_code == 401 -def test_lambda_access(create_lambda_token): +def test_lambda_access(api_headers): """ - Route: + Route: /logos """ - dept1, dept2, dept3, token = create_lambda_token - headers = {"Authorization": f"Bearer {token}"} + headers = api_headers with app.test_client() as client: response = client.get(API_URL + "/logos", headers=headers) - assert response.status_code == 403 + assert response.status_code == 401 -def test_initial_with_header_and_footer(create_super_token): +def test_global_logos(api_admin_headers): """ Route: """ - dept1, dept2, dept3, token = create_super_token - headers = {"Authorization": f"Bearer {token}"} + headers = api_admin_headers with app.test_client() as client: response = client.get(API_URL + "/logos", headers=headers) assert response.status_code == 200 assert response.json is not None - assert len(response.json) == 7 + assert ( + len(response.json) == 4 + ) # 4 items in fakelogo context: ['header', 'footer', 'logo_B', 'logo_C'] + + +def test_local_by_id_logos(api_admin_headers): + """ + Route: /departement/id/1/logos + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/id/1/logos", headers=headers) + assert response.status_code == 200 + assert response.json is not None + assert ( + len(response.json) == 2 + ) # 2 items in dept(1, TAPI) fakelogo context: ['logo_A', 'logo_D'] + + +def test_local_by_name_logos(api_admin_headers): + """ + Route: /departement/TAPI/logos + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/TAPI/logos", headers=headers) + assert response.status_code == 200 + assert response.json is not None + assert ( + len(response.json) == 2 + ) # 2 items in dept(1, TAPI) fakelogo context: ['logo_A', 'logo_D'] + + +def test_local_png_by_id_logo(api_admin_headers): + """ + Route: /departement/id/1/logo/D + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/id/1/logo/D", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/png" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_D.png" in response.headers["Content-Disposition"] + + +def test_global_png_logo(api_admin_headers): + """ + Route: /logo/C + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/logo/C", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/png" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_C.png" in response.headers["Content-Disposition"] + + +def test_global_jpg_logo(api_admin_headers): + """ + Route: /logo/B + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/logo/B", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/jpg" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_B.jpg" in response.headers["Content-Disposition"] + + +def test_local_png_by_name_logo(api_admin_headers): + """ + Route: /departement/TAPI/logo/A + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/TAPI/logo/D", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/png" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_D.png" in response.headers["Content-Disposition"] + + +def test_local_jpg_by_id_logo(api_admin_headers): + """ + Route: /departement/id/1/logo/D + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/id/1/logo/A", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/jpg" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_A.jpg" in response.headers["Content-Disposition"] + + +def test_local_jpg_by_name_logo(api_admin_headers): + """ + Route: /departement/TAPI/logo/A + """ + headers = api_admin_headers + with app.test_client() as client: + response = client.get(API_URL + "/departement/TAPI/logo/A", headers=headers) + assert response.status_code == 200 + assert response.headers["Content-Type"] == "image/jpg" + assert response.headers["Content-Disposition"].startswith("inline") + assert "logo_A.jpg" in response.headers["Content-Disposition"] diff --git a/tools/fakedatabase/create_test_api_database.py b/tools/fakedatabase/create_test_api_database.py index 03e4f7bf9..bd4bb0919 100644 --- a/tools/fakedatabase/create_test_api_database.py +++ b/tools/fakedatabase/create_test_api_database.py @@ -8,7 +8,9 @@ """ import datetime +import os import random +import shutil import time import sys @@ -45,6 +47,10 @@ REFCOMP_FILENAME = ( "ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml" ) +# la réserve de logos +LOGOS_STOCK = "/opt/scodoc/tests/ressources/test_logos/" +LOGOS_DIR = "/opt/scodoc-data/config/logos/" + def create_departements(acronyms: list[str]) -> list[Departement]: "Create depts" @@ -353,6 +359,27 @@ def create_etape_apo(formsemestre: FormSemestre): db.session.commit() +def create_logos(): + if not os.path.exists(LOGOS_DIR + "logos_1"): + os.mkdir(LOGOS_DIR + "logos_1") + shutil.copy( + LOGOS_STOCK + "logo_A.jpg", + LOGOS_DIR + "logos_1/logo_A.jpg", + ) + shutil.copy( + LOGOS_STOCK + "logo_D.png", + LOGOS_DIR + "logos_1/logo_D.png", + ) + shutil.copy( + LOGOS_STOCK + "logo_A.jpg", + LOGOS_DIR + "logo_B.jpg", + ) + shutil.copy( + LOGOS_STOCK + "logo_D.png", + LOGOS_DIR + "logo_C.png", + ) + + def init_test_database(): """Appelé par la commande `flask init-test-database` @@ -373,6 +400,7 @@ def init_test_database(): saisie_notes_evaluations(formsemestre, user_lecteur) add_absences(formsemestre) create_etape_apo(formsemestre) + create_logos() # à compléter # - groupes # - absences