From 1ea782102b55916c2cb8ef98cc63b582ecbe01cb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 7 Dec 2022 13:22:11 +0100 Subject: [PATCH] Automatise les tests unitaires de l'API --- README.md | 6 +- app/api/logos.py | 1 + tests/api/README.md | 38 +++++-- tests/api/dotenv_exemple | 7 +- tests/api/start_api_server.sh | 25 +++++ tests/api/test_api_etudiants.py | 1 - tests/api/test_api_logos.py | 193 +++++++++++++++++--------------- tools/create_database.sh | 2 +- 8 files changed, 167 insertions(+), 106 deletions(-) create mode 100755 tests/api/start_api_server.sh diff --git a/README.md b/README.md index 3cfbf39de..6c79f8bba 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ La version 9.0 s'efforce de reproduire presque à l'identique le fonctionnement de ScoDoc7, avec des composants logiciels différents (Debian 11, Python 3, Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes). -### État actuel (nov 22) +### État actuel (dec 22) -- 9.3.x est en production -- le prochain jalon est 9.4. Voir branches sur gitea. +- 9.4.x est en production +- le prochain jalon est 9.5. Voir branches sur gitea. ### Lignes de commandes diff --git a/app/api/logos.py b/app/api/logos.py index 49c0619c6..5c31ed758 100644 --- a/app/api/logos.py +++ b/app/api/logos.py @@ -48,6 +48,7 @@ from app.scodoc.sco_permissions import Permission @scodoc @permission_required(Permission.ScoSuperAdmin) def api_get_glob_logos(): + """Liste tous les logos""" logos = list_logos()[None] return jsonify(list(logos.keys())) diff --git a/tests/api/README.md b/tests/api/README.md index 535b6cdee..c1b7b86e8 100644 --- a/tests/api/README.md +++ b/tests/api/README.md @@ -1,28 +1,44 @@ # Tests unitaires de l'API ScoDoc -Démarche générale: +## Lancement des tests + +La première fois, copier le fichier `tests/api/dotenv_exemple` vers +`tests/api/.env`. Il est normalement inutile de modifier son contenu. + +Dans un shell, lancer le script `start_api_server.py`, qui se charge +d'initialiser une base SQL de test et de lancer le serveur ScoDoc approprié. + +```bash +tests/api/start_api_server.sh +``` + +Dans un autre shell, lancer les tests: + +```bash +pytest tests/api +``` + +## Notes sur la démarche 1. On génère une base SQL de test: voir `tools/fakedatabase/create_test_api_database.py` - 1. modifier `/opt/scodoc/.env` pour indiquer - - ```bash - FLASK_ENV=test_api - FLASK_DEBUG=1 - ``` - - 2. En tant qu'utilisateur scodoc, lancer: + 1. En tant qu'utilisateur scodoc, lancer: ```bash + # evite de modifier /opt/scodoc/.env + export FLASK_ENV=test_api + export FLASK_DEBUG=1 tools/create_database.sh --drop SCODOC_TEST_API flask db upgrade flask sco-db-init --erase flask init-test-database ``` - en plus court: ```bash - tools/create_database.sh --drop SCODOC_TEST_API && flask db upgrade &&flask sco-db-init --erase && flask init-test-database + en plus court: + + ```bash + export FLASK_ENV=test_api && tools/create_database.sh --drop SCODOC_TEST_API && flask db upgrade &&flask sco-db-init --erase && flask init-test-database ``` 2. On lance le serveur ScoDoc sur cette base diff --git a/tests/api/dotenv_exemple b/tests/api/dotenv_exemple index e1857bd81..e990d0c5c 100644 --- a/tests/api/dotenv_exemple +++ b/tests/api/dotenv_exemple @@ -4,8 +4,11 @@ # et à remplir. # URL du serveur ScoDoc à interroger -SCODOC_URL = "http://localhost:5000/" +SCODOC_URL="http://localhost:5000/" # Le client (python) doit-il vérifier le certificat SSL du serveur ? # ou True si serveur de production avec certif SSL valide -CHECK_CERTIFICATE = False +CHECK_CERTIFICATE=False + +API_USER="lecteur_api" +API_PASSWORD="azerty" diff --git a/tests/api/start_api_server.sh b/tests/api/start_api_server.sh new file mode 100755 index 000000000..67a3120a7 --- /dev/null +++ b/tests/api/start_api_server.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Script recreating the TEST API database and starting the serveur + +set -e + +# Le répertoire de ce script: + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +# récupère API_USER et API_PASSWORD +source "$SCRIPT_DIR"/.env + +export FLASK_ENV=test_api +export FLASK_DEBUG=1 +tools/create_database.sh --drop SCODOC_TEST_API +flask db upgrade +flask sco-db-init --erase +flask init-test-database + +flask user-create "$API_USER" LecteurAPI @all +flask user-password --password "$API_PASSWORD" "$API_USER" +flask edit-role LecteurAPI -a ScoView +flask user-role "$API_USER" -a LecteurAPI + +flask run --host 0.0.0.0 \ No newline at end of file diff --git a/tests/api/test_api_etudiants.py b/tests/api/test_api_etudiants.py index 720090936..b4fc97f3b 100644 --- a/tests/api/test_api_etudiants.py +++ b/tests/api/test_api_etudiants.py @@ -76,7 +76,6 @@ def test_etudiants_courant(api_headers): etud = etudiants[-1] assert verify_fields(etud, fields) is True - assert etud["id"] == etud["etudid"] assert isinstance(etud["id"], int) assert isinstance(etud["code_nip"], str) assert isinstance(etud["nom"], str) diff --git a/tests/api/test_api_logos.py b/tests/api/test_api_logos.py index 9ae59e87f..8277c1840 100644 --- a/tests/api/test_api_logos.py +++ b/tests/api/test_api_logos.py @@ -13,8 +13,13 @@ utilisation: # XXX TODO # Ce test a une logique très différente des autres : A UNIFIER - -from tests.api.setup_test_api import API_URL, api_admin_headers, api_headers +import requests +from tests.api.setup_test_api import ( + API_URL, + api_admin_headers, + api_headers, + CHECK_CERTIFICATE, +) from scodoc import app from tests.unit.config_test_logos import ( @@ -30,147 +35,159 @@ def test_super_access(api_admin_headers): """ Route: /logos """ - 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(api_headers): - """ - Route: /logos - """ - headers = api_headers - with app.test_client() as client: - response = client.get(API_URL + "/logos", headers=headers) - assert response.status_code == 401 + response = requests.get( + API_URL + "/logos", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + assert response.status_code == 200 + assert response.json() is not None def test_lambda_access(api_headers): """ Route: /logos """ - headers = api_headers - with app.test_client() as client: - response = client.get(API_URL + "/logos", headers=headers) - assert response.status_code == 401 + response = requests.get( + API_URL + "/logos", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert response.status_code == 401 def test_global_logos(api_admin_headers): """ Route: """ - 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 "header" in response.json - assert "footer" in response.json - assert "B" in response.json - assert "C" in response.json + response = requests.get( + API_URL + "/logos", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + assert response.status_code == 200 + assert response.json() is not None + assert "header" in response.json() + assert "footer" in response.json() + assert "B" in response.json() + assert "C" in response.json() 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 "A" in response.json - assert "D" in response.json + response = requests.get( + API_URL + "/departement/id/1/logos", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + assert response.status_code == 200 + assert response.json() is not None + assert "A" in response.json() + assert "D" in response.json() 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 "A" in response.json - assert "D" in response.json + response = requests.get( + API_URL + "/departement/TAPI/logos", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + assert response.status_code == 200 + assert response.json() is not None + assert "A" in response.json() + assert "D" in response.json() 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"] + response = requests.get( + API_URL + "/departement/id/1/logo/D", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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"] + response = requests.get( + API_URL + "/logo/C", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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"] + response = requests.get( + API_URL + "/logo/B", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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 + Route: /departement/TAPI/logo/D """ - 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"] + response = requests.get( + API_URL + "/departement/TAPI/logo/D", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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 + Route: /departement/id/1/logo/A """ - 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"] + response = requests.get( + API_URL + "/departement/id/1/logo/A", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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"] + response = requests.get( + API_URL + "/departement/TAPI/logo/A", + headers=api_admin_headers, + verify=CHECK_CERTIFICATE, + ) + 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/create_database.sh b/tools/create_database.sh index e1d7ffb95..6e9a939ed 100755 --- a/tools/create_database.sh +++ b/tools/create_database.sh @@ -15,7 +15,7 @@ if [ "$1" = "--drop" ] then db_name="$2" echo "Dropping database $db_name..." - dropdb "$db_name" + dropdb --if-exists "$db_name" else db_name="$1" fi