Automatise les tests unitaires de l'API

This commit is contained in:
Emmanuel Viennet 2022-12-07 13:22:11 +01:00 committed by iziram
parent ba5b5cdb6f
commit f2ffd69fe6
8 changed files with 167 additions and 106 deletions

View File

@ -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, 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). 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 - 9.4.x est en production
- le prochain jalon est 9.4. Voir branches sur gitea. - le prochain jalon est 9.5. Voir branches sur gitea.
### Lignes de commandes ### Lignes de commandes

View File

@ -48,6 +48,7 @@ from app.scodoc.sco_permissions import Permission
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
def api_get_glob_logos(): def api_get_glob_logos():
"""Liste tous les logos"""
logos = list_logos()[None] logos = list_logos()[None]
return jsonify(list(logos.keys())) return jsonify(list(logos.keys()))

View File

@ -1,28 +1,44 @@
# Tests unitaires de l'API ScoDoc # 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 1. On génère une base SQL de test: voir
`tools/fakedatabase/create_test_api_database.py` `tools/fakedatabase/create_test_api_database.py`
1. modifier `/opt/scodoc/.env` pour indiquer 1. En tant qu'utilisateur scodoc, lancer:
```bash
FLASK_ENV=test_api
FLASK_DEBUG=1
```
2. En tant qu'utilisateur scodoc, lancer:
```bash ```bash
# evite de modifier /opt/scodoc/.env
export FLASK_ENV=test_api
export FLASK_DEBUG=1
tools/create_database.sh --drop SCODOC_TEST_API tools/create_database.sh --drop SCODOC_TEST_API
flask db upgrade flask db upgrade
flask sco-db-init --erase flask sco-db-init --erase
flask init-test-database flask init-test-database
``` ```
en plus court: ```bash en plus court:
tools/create_database.sh --drop SCODOC_TEST_API && flask db upgrade &&flask sco-db-init --erase && flask init-test-database
```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 2. On lance le serveur ScoDoc sur cette base

View File

@ -4,8 +4,11 @@
# et à remplir. # et à remplir.
# URL du serveur ScoDoc à interroger # 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 ? # Le client (python) doit-il vérifier le certificat SSL du serveur ?
# ou True si serveur de production avec certif SSL valide # ou True si serveur de production avec certif SSL valide
CHECK_CERTIFICATE = False CHECK_CERTIFICATE=False
API_USER="lecteur_api"
API_PASSWORD="azerty"

25
tests/api/start_api_server.sh Executable file
View File

@ -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

View File

@ -76,7 +76,6 @@ def test_etudiants_courant(api_headers):
etud = etudiants[-1] etud = etudiants[-1]
assert verify_fields(etud, fields) is True assert verify_fields(etud, fields) is True
assert etud["id"] == etud["etudid"]
assert isinstance(etud["id"], int) assert isinstance(etud["id"], int)
assert isinstance(etud["code_nip"], str) assert isinstance(etud["code_nip"], str)
assert isinstance(etud["nom"], str) assert isinstance(etud["nom"], str)

View File

@ -13,8 +13,13 @@ utilisation:
# XXX TODO # XXX TODO
# Ce test a une logique très différente des autres : A UNIFIER # Ce test a une logique très différente des autres : A UNIFIER
import requests
from tests.api.setup_test_api import API_URL, api_admin_headers, api_headers from tests.api.setup_test_api import (
API_URL,
api_admin_headers,
api_headers,
CHECK_CERTIFICATE,
)
from scodoc import app from scodoc import app
from tests.unit.config_test_logos import ( from tests.unit.config_test_logos import (
@ -30,147 +35,159 @@ def test_super_access(api_admin_headers):
""" """
Route: /logos Route: /logos
""" """
headers = api_admin_headers response = requests.get(
with app.test_client(api_admin_headers) as client: API_URL + "/logos",
response = client.get(API_URL + "/logos", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.json is not None )
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
def test_lambda_access(api_headers): def test_lambda_access(api_headers):
""" """
Route: /logos Route: /logos
""" """
headers = api_headers response = requests.get(
with app.test_client() as client: API_URL + "/logos",
response = client.get(API_URL + "/logos", headers=headers) headers=api_headers,
assert response.status_code == 401 verify=CHECK_CERTIFICATE,
)
assert response.status_code == 401
def test_global_logos(api_admin_headers): def test_global_logos(api_admin_headers):
""" """
Route: Route:
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/logos",
response = client.get(API_URL + "/logos", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.json is not None )
assert "header" in response.json assert response.status_code == 200
assert "footer" in response.json assert response.json() is not None
assert "B" in response.json assert "header" in response.json()
assert "C" 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): def test_local_by_id_logos(api_admin_headers):
""" """
Route: /departement/id/1/logos Route: /departement/id/1/logos
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/departement/id/1/logos",
response = client.get(API_URL + "/departement/id/1/logos", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.json is not None )
assert "A" in response.json assert response.status_code == 200
assert "D" in response.json 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): def test_local_by_name_logos(api_admin_headers):
""" """
Route: /departement/TAPI/logos Route: /departement/TAPI/logos
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/departement/TAPI/logos",
response = client.get(API_URL + "/departement/TAPI/logos", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.json is not None )
assert "A" in response.json assert response.status_code == 200
assert "D" in response.json 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): def test_local_png_by_id_logo(api_admin_headers):
""" """
Route: /departement/id/1/logo/D Route: /departement/id/1/logo/D
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/departement/id/1/logo/D",
response = client.get(API_URL + "/departement/id/1/logo/D", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/png" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_D.png" in response.headers["Content-Disposition"] 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): def test_global_png_logo(api_admin_headers):
""" """
Route: /logo/C Route: /logo/C
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/logo/C",
response = client.get(API_URL + "/logo/C", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/png" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_C.png" in response.headers["Content-Disposition"] 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): def test_global_jpg_logo(api_admin_headers):
""" """
Route: /logo/B Route: /logo/B
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/logo/B",
response = client.get(API_URL + "/logo/B", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/jpg" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_B.jpg" in response.headers["Content-Disposition"] 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): def test_local_png_by_name_logo(api_admin_headers):
""" """
Route: /departement/TAPI/logo/A Route: /departement/TAPI/logo/D
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/departement/TAPI/logo/D",
response = client.get(API_URL + "/departement/TAPI/logo/D", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/png" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_D.png" in response.headers["Content-Disposition"] 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): 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 response = requests.get(
with app.test_client() as client: API_URL + "/departement/id/1/logo/A",
response = client.get(API_URL + "/departement/id/1/logo/A", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/jpg" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_A.jpg" in response.headers["Content-Disposition"] 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): def test_local_jpg_by_name_logo(api_admin_headers):
""" """
Route: /departement/TAPI/logo/A Route: /departement/TAPI/logo/A
""" """
headers = api_admin_headers response = requests.get(
with app.test_client() as client: API_URL + "/departement/TAPI/logo/A",
response = client.get(API_URL + "/departement/TAPI/logo/A", headers=headers) headers=api_admin_headers,
assert response.status_code == 200 verify=CHECK_CERTIFICATE,
assert response.headers["Content-Type"] == "image/jpg" )
assert response.headers["Content-Disposition"].startswith("inline") assert response.status_code == 200
assert "logo_A.jpg" in response.headers["Content-Disposition"] assert response.headers["Content-Type"] == "image/jpg"
assert response.headers["Content-Disposition"].startswith("inline")
assert "logo_A.jpg" in response.headers["Content-Disposition"]

View File

@ -15,7 +15,7 @@ if [ "$1" = "--drop" ]
then then
db_name="$2" db_name="$2"
echo "Dropping database $db_name..." echo "Dropping database $db_name..."
dropdb "$db_name" dropdb --if-exists "$db_name"
else else
db_name="$1" db_name="$1"
fi fi