1 Commits

Author SHA1 Message Date
0a2b23f260 feat: Amorçage des tests automatiques [#64] 2025-06-05 17:20:59 +02:00
6 changed files with 246 additions and 53 deletions

4
Back-End/.gitignore vendored
View File

@ -4,4 +4,6 @@ documents
data
*.dmp
staticfiles
/*/Configuration/application*.json
/*/Configuration/application*.json
rapport_tests_back.json
tests_automatiques.json

View File

@ -66,8 +66,11 @@ urllib3==2.2.3
vine==5.1.0
wcwidth==0.2.13
webencodings==0.5.1
watchfiles
xhtml2pdf==0.2.16
channels==4.0.0
channels-redis==4.1.0
daphne==4.1.0
pytest
djangorestframework
pytest-django
pytest-json-report

View File

@ -1,6 +1,5 @@
import subprocess
import os
from watchfiles import run_process
def run_command(command):
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -12,7 +11,6 @@ def run_command(command):
return process.returncode
test_mode = os.getenv('test_mode', 'false').lower() == 'true'
watch_mode = os.getenv('DJANGO_WATCH', 'false').lower() == 'true'
commands = [
["python", "manage.py", "collectstatic", "--noinput"],
@ -34,55 +32,23 @@ test_commands = [
["python", "manage.py", "init_mock_datas"]
]
def run_daphne():
try:
result = subprocess.run([
"daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"
])
return result.returncode
except KeyboardInterrupt:
print("Arrêt de Daphne (KeyboardInterrupt)")
return 0
for command in commands:
if run_command(command) != 0:
exit(1)
if __name__ == "__main__":
for command in commands:
if run_command(command) != 0:
exit(1)
#if test_mode:
# for test_command in test_commands:
# if run_command(test_command) != 0:
# exit(1)
#if test_mode:
# for test_command in test_commands:
# if run_command(test_command) != 0:
# exit(1)
# Lancer les processus en parallèle
if watch_mode:
celery_worker = subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"])
celery_beat = subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"])
try:
run_process(
'.',
target=run_daphne
)
except KeyboardInterrupt:
print("Arrêt demandé (KeyboardInterrupt)")
finally:
celery_worker.terminate()
celery_beat.terminate()
celery_worker.wait()
celery_beat.wait()
else:
processes = [
subprocess.Popen([
"daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"
]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"])
]
try:
for process in processes:
process.wait()
except KeyboardInterrupt:
print("Arrêt demandé (KeyboardInterrupt)")
for process in processes:
process.terminate()
for process in processes:
process.wait()
processes = [
subprocess.Popen(["daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"])
]
# Attendre la fin des processus
for process in processes:
process.wait()

View File

@ -0,0 +1,39 @@
"""
Starter de tests automatiques pour valider tous les endpoints définis dans les fichiers urls.py du projet Django N3WT-SCHOOL.
Chaque endpoint est testé pour la réponse HTTP attendue (200, 401, 403, 404, etc.).
"""
import pytest
from django.urls import reverse, resolve
from rest_framework.test import APIClient
from django.conf import settings
from django.urls import get_resolver
@pytest.mark.django_db
class TestAllEndpoints:
@pytest.fixture(autouse=True)
def setup(self):
self.client = APIClient()
def get_all_url_patterns(self):
resolver = get_resolver()
patterns = resolver.url_patterns
urls = []
def extract(patterns, prefix=""):
for p in patterns:
if hasattr(p, 'url_patterns'):
extract(p.url_patterns, prefix + str(p.pattern))
else:
urls.append(prefix + str(p.pattern))
extract(patterns)
return urls
def test_all_endpoints_anonymous(self):
urls = self.get_all_url_patterns()
for url in urls:
if '<' in url: # skip dynamic urls for starter
continue
try:
response = self.client.get(url)
assert response.status_code in [200, 401, 403, 404]
except Exception as e:
print(f"Erreur sur {url}: {e}")

View File

@ -0,0 +1,124 @@
"""
Tests automatiques pour les endpoints Auth de l'API N3WT-SCHOOL.
- Teste les endpoints GET, y compris dynamiques.
- Teste l'authentification (login JWT) et l'accès aux endpoints protégés.
- Vérifie la structure JSON des réponses principales.
"""
import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from Auth.models import Profile, ProfileRole
from Establishment.models import Establishment
from django.contrib.auth.hashers import make_password
@pytest.mark.django_db
class TestAuthEndpoints:
@pytest.fixture(autouse=True)
def setup(self, db):
self.client = APIClient()
# Création d'un établissement de test
self.establishment = Establishment.objects.create(
name="Etablissement Test",
address="1 rue du test",
total_capacity=100,
establishment_type=[1],
evaluation_frequency=1,
licence_code="LIC123",
is_active=True
)
# Création d'un utilisateur de test
self.test_email = 'testuser@example.com'
self.test_password = 'testpass123'
self.profile = Profile.objects.create(
email=self.test_email,
username=self.test_email,
password=make_password(self.test_password)
)
self.profile_role = ProfileRole.objects.create(
profile=self.profile,
role_type=1, # ADMIN
establishment=self.establishment,
is_active=True
)
def test_csrf(self):
response = self.client.get('/Auth/csrf')
assert response.status_code == 200
assert 'csrfToken' in response.json()
def test_login(self):
response = self.client.post('/Auth/login', {
'email': self.test_email,
'password': self.test_password
}, format='json')
assert response.status_code in [200, 401]
if response.status_code == 200:
assert 'access' in response.json() or 'token' in response.json()
def test_profiles(self):
# GET /Auth/profiles
response = self.client.get(f'/Auth/profiles')
assert response.status_code in [200, 401, 403]
if response.status_code == 200:
# Vérifie que le profil de test existe dans la liste
emails = [p.get('email') for p in response.json() if isinstance(p, dict)]
assert self.test_email in emails
def test_profiles_id(self):
# GET /Auth/profiles/<id>
response = self.client.get(f'/Auth/profiles/{self.profile.id}')
assert response.status_code in [200, 401, 403, 404]
if response.status_code == 200:
data = response.json()
assert data.get('email') == self.test_email
def test_profile_roles(self):
# GET /Auth/profileRoles avec paramètres requis
params = {
'establishment_id': self.establishment.id,
'filter': 'school'
}
response = self.client.get('/Auth/profileRoles', params)
assert response.status_code in [200, 401, 403, 400]
if response.status_code == 200:
results = response.json()
# Adapter à la structure réelle de la réponse : clé 'profilesRoles'
if isinstance(results, dict) and 'profilesRoles' in results:
results = results['profilesRoles']
found = any(
r.get('profile') == self.profile.id and r.get('role_type') == 1
for r in results if isinstance(r, dict)
)
assert found
def test_profile_roles_id(self):
# GET /Auth/profileRoles/<id>
response = self.client.get(f'/Auth/profileRoles/{self.profile_role.id}')
assert response.status_code in [200, 401, 403, 404]
if response.status_code == 200:
data = response.json()
assert data.get('profile') == self.profile.id
assert data.get('role_type') == 1
def test_reset_password(self):
# POST /Auth/resetPassword/<code> (méthode attendue)
response = self.client.post('/Auth/resetPassword/ABCDEF', {
'password1': 'newpass123',
'password2': 'newpass123'
}, format='json')
assert response.status_code in [200, 400, 404]
# 400 attendu si le code est invalide ou expiré
def test_info_session(self):
# GET /Auth/infoSession (protégé)
login = self.client.post('/Auth/login', {
'email': self.test_email,
'password': self.test_password
}, format='json')
if login.status_code == 200 and ('access' in login.json() or 'token' in login.json()):
token = login.json().get('access') or login.json().get('token')
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
response = self.client.get('/Auth/infoSession')
assert response.status_code in [200, 401, 403]
else:
pytest.skip('Impossible de sauthentifier pour tester infoSession')

View File

@ -0,0 +1,59 @@
# Documentation des tests automatiques
Ce document décrit la structure et lutilisation du starter de tests automatiques pour valider tous les endpoints exposés par les fichiers `urls.py` des applications Django du Back-End.
## Objectif
- Vérifier automatiquement que chaque route définie dans le projet répond bien à une requête HTTP (statut attendu : 200, 401, 403, 404).
- Permettre une validation rapide de la couverture des endpoints lors de chaque pipeline CI.
## Structure
- Le fichier principal est `Back-End/test_all_endpoints.py`.
- Ce test utilise `pytest` et `pytest-django` pour parcourir toutes les routes du projet et effectuer une requête GET anonyme.
- Les routes dynamiques (avec paramètres) sont ignorées dans ce starter.
## Exécution
1. Installer les dépendances si besoin :
```sh
pip install pytest pytest-django djangorestframework
```
2. Lancer les tests :
```sh
pytest Back-End/test_all_endpoints.py
```
## Intégration CI
- Ajouter la commande de test dans votre pipeline CI (GitHub Actions, GitLab CI, Jenkins, etc.) :
```sh
pytest Back-End/test_all_endpoints.py
```
## Personnalisation
- Pour tester les routes nécessitant une authentification ou des paramètres, compléter le test avec des cas spécifiques.
- Pour chaque nouvelle route, le test sexécutera automatiquement.
## Ajout
- Ajout d'un fichier de tests automatiques dédié à Auth (`test_auth_endpoints.py`) pour tester tous les endpoints Auth (GET, dynamiques, login JWT, accès protégé, structure JSON).
- Les tests créent un utilisateur et un profilRole de test, réalisent un login, et vérifient les accès aux endpoints principaux, y compris les routes dynamiques et protégées.
## Utilisation
- Lancer les tests avec :
```bash
pytest --json-report --json-report-file=tests_automatiques.json
```
- Le rapport inclura les résultats détaillés pour chaque endpoint Auth.
## Extension
- Étendre sur le même modèle pour les autres applications (School, Subscriptions, etc.)
- Ajouter des tests POST/PUT/DELETE et des cas derreur/permissions.
---
Pour toute question ou évolution, se référer au ticket associé.