Files
notytex/tests/test_class_repository.py
2025-08-16 16:07:57 +02:00

553 lines
22 KiB
Python

import pytest
from datetime import date
from werkzeug.exceptions import NotFound
from models import db, ClassGroup, Student, Assessment
from repositories.class_repository import ClassRepository
from .conftest import create_student_with_enrollment
class TestClassRepository:
"""Tests complets pour ClassRepository."""
def test_get_or_404_success(self, app):
"""Test de récupération par ID avec succès."""
with app.app_context():
repo = ClassRepository()
# Créer une classe de test
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Test
found = repo.get_or_404(class_group.id)
assert found is not None
assert found.name == "6A"
assert found.year == "2023-2024"
assert found.id == class_group.id
def test_get_or_404_not_found(self, app):
"""Test de récupération par ID avec erreur 404."""
with app.app_context():
repo = ClassRepository()
# Test avec un ID inexistant
with pytest.raises(NotFound):
repo.get_or_404(999)
def test_find_by_name_success(self, app):
"""Test de recherche par nom avec succès."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
class_group1 = ClassGroup(name="6A", year="2023-2024")
class_group2 = ClassGroup(name="6B", year="2023-2024")
db.session.add_all([class_group1, class_group2])
db.session.commit()
# Test
found = repo.find_by_name("6A")
assert found is not None
assert found.name == "6A"
assert found.year == "2023-2024"
# Test avec nom inexistant
not_found = repo.find_by_name("6Z")
assert not_found is None
def test_exists_by_name_without_exclude(self, app):
"""Test d'existence par nom sans exclusion."""
with app.app_context():
repo = ClassRepository()
# Créer une classe de test
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Test existence
assert repo.exists_by_name("6A") is True
assert repo.exists_by_name("6Z") is False
def test_exists_by_name_with_exclude(self, app):
"""Test d'existence par nom avec exclusion d'un ID."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
class_group1 = ClassGroup(name="6A", year="2023-2024")
class_group2 = ClassGroup(name="6B", year="2023-2024")
db.session.add_all([class_group1, class_group2])
db.session.commit()
# Test sans exclusion
assert repo.exists_by_name("6A") is True
# Test avec exclusion de la classe même
assert repo.exists_by_name("6A", exclude_id=class_group1.id) is False
# Test avec exclusion d'une autre classe
assert repo.exists_by_name("6A", exclude_id=class_group2.id) is True
def test_find_all_ordered_year_name(self, app):
"""Test de recherche triée par année puis nom."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test avec différentes années
classes = [
ClassGroup(name="6B", year="2023-2024"),
ClassGroup(name="6A", year="2023-2024"),
ClassGroup(name="5B", year="2024-2025"),
ClassGroup(name="5A", year="2024-2025"),
]
db.session.add_all(classes)
db.session.commit()
# Test tri par année puis nom (défaut)
ordered = repo.find_all_ordered()
assert len(ordered) >= 4
# Extraire les classes créées pour ce test
test_classes = [c for c in ordered if c.year in ["2023-2024", "2024-2025"]]
# Vérifier l'ordre : année puis nom
expected_order = [
("2023-2024", "6A"),
("2023-2024", "6B"),
("2024-2025", "5A"),
("2024-2025", "5B"),
]
actual_order = [(c.year, c.name) for c in test_classes]
assert actual_order == expected_order
def test_find_all_ordered_name(self, app):
"""Test de recherche triée par nom uniquement."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
classes = [
ClassGroup(name="6C", year="2023-2024"),
ClassGroup(name="6A", year="2024-2025"),
ClassGroup(name="6B", year="2023-2024"),
]
db.session.add_all(classes)
db.session.commit()
# Test tri par nom
ordered = repo.find_all_ordered(order_by='name')
# Extraire les classes créées pour ce test
test_classes = [c for c in ordered if c.name in ["6A", "6B", "6C"]]
# Vérifier l'ordre : nom seulement
names = [c.name for c in test_classes]
assert names == ["6A", "6B", "6C"]
def test_find_all_ordered_year(self, app):
"""Test de recherche triée par année uniquement."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
classes = [
ClassGroup(name="6C", year="2024-2025"),
ClassGroup(name="6A", year="2023-2024"),
ClassGroup(name="6B", year="2025-2026"),
]
db.session.add_all(classes)
db.session.commit()
# Test tri par année
ordered = repo.find_all_ordered(order_by='year')
# Extraire les classes créées pour ce test
test_classes = [c for c in ordered if c.name in ["6A", "6B", "6C"]]
# Vérifier l'ordre : année seulement
years = [c.year for c in test_classes]
assert years == ["2023-2024", "2024-2025", "2025-2026"]
def test_find_all_ordered_invalid_sort(self, app):
"""Test de recherche avec critère de tri invalide (utilise le défaut)."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Test avec critère invalide (devrait utiliser tri par nom par défaut)
ordered = repo.find_all_ordered(order_by='invalid')
assert len(ordered) >= 1
def test_count_all(self, app):
"""Test de comptage total des classes."""
with app.app_context():
repo = ClassRepository()
# Compter avant ajout
initial_count = repo.count_all()
# Créer des classes de test
classes = [
ClassGroup(name="6A", year="2023-2024"),
ClassGroup(name="6B", year="2023-2024"),
ClassGroup(name="5A", year="2023-2024"),
]
db.session.add_all(classes)
db.session.commit()
# Compter après ajout
final_count = repo.count_all()
assert final_count == initial_count + 3
def test_can_be_deleted_empty_class(self, app):
"""Test de vérification de suppression pour une classe vide."""
with app.app_context():
repo = ClassRepository()
# Créer une classe vide
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Test
can_delete, dependencies = repo.can_be_deleted(class_group.id)
assert can_delete is True
assert dependencies['students'] == 0
assert dependencies['assessments'] == 0
def test_can_be_deleted_with_students(self, app):
"""Test de vérification de suppression pour une classe avec étudiants."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec étudiants
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Créer les étudiants (sans class_group_id dans le système temporel)
student1 = Student(first_name="Jean", last_name="Dupont")
student2 = Student(first_name="Marie", last_name="Martin")
db.session.add_all([student1, student2])
db.session.commit()
# Créer les inscriptions temporelles
from models import StudentEnrollment
from datetime import date
enrollment1 = StudentEnrollment(
student_id=student1.id,
class_group_id=class_group.id,
enrollment_date=date(2023, 9, 1)
)
enrollment2 = StudentEnrollment(
student_id=student2.id,
class_group_id=class_group.id,
enrollment_date=date(2023, 9, 1)
)
db.session.add_all([enrollment1, enrollment2])
db.session.commit()
# Test
can_delete, dependencies = repo.can_be_deleted(class_group.id)
assert can_delete is False
assert dependencies['students'] == 2
assert dependencies['assessments'] == 0
def test_can_be_deleted_with_assessments(self, app):
"""Test de vérification de suppression pour une classe avec évaluations."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec évaluations
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
assessments = [
Assessment(title="Test 1", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 15)),
Assessment(title="Test 2", trimester=2, class_group_id=class_group.id, date=date(2023, 12, 15)),
]
db.session.add_all(assessments)
db.session.commit()
# Test
can_delete, dependencies = repo.can_be_deleted(class_group.id)
assert can_delete is False
assert dependencies['students'] == 0
assert dependencies['assessments'] == 2
def test_can_be_deleted_with_both(self, app):
"""Test de vérification de suppression pour une classe avec étudiants et évaluations."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec étudiants et évaluations
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Utiliser le helper pour créer un étudiant avec inscription temporelle
student = create_student_with_enrollment("Jean", "Dupont", class_group.id)
assessment = Assessment(title="Test", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 15))
db.session.add_all([student, assessment])
db.session.commit()
# Test
can_delete, dependencies = repo.can_be_deleted(class_group.id)
assert can_delete is False
assert dependencies['students'] == 1
assert dependencies['assessments'] == 1
def test_find_with_students_ordered_success(self, app):
"""Test de recherche avec étudiants triés."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec étudiants
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Utiliser le helper pour créer des étudiants avec inscriptions temporelles
students = [
create_student_with_enrollment("Marie", "Zidane", class_group.id),
create_student_with_enrollment("Jean", "Dupont", class_group.id),
create_student_with_enrollment("Paul", "Martin", class_group.id),
]
db.session.commit()
# Test
found = repo.find_with_students_ordered(class_group.id)
assert found is not None
assert found.name == "6A"
assert hasattr(found, '_students_ordered')
# Vérifier le tri : Dupont, Martin, Zidane
ordered_names = [f"{s.last_name}, {s.first_name}" for s in found._students_ordered]
assert ordered_names == ["Dupont, Jean", "Martin, Paul", "Zidane, Marie"]
def test_find_with_students_ordered_not_found(self, app):
"""Test de recherche avec étudiants triés pour classe inexistante."""
with app.app_context():
repo = ClassRepository()
# Test avec ID inexistant
found = repo.find_with_students_ordered(999)
assert found is None
def test_find_with_recent_assessments_success(self, app):
"""Test de recherche avec évaluations récentes."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec évaluations
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
assessments = [
Assessment(title="Test 1", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 1)),
Assessment(title="Test 2", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 15)),
Assessment(title="Test 3", trimester=2, class_group_id=class_group.id, date=date(2023, 12, 1)),
Assessment(title="Test 4", trimester=2, class_group_id=class_group.id, date=date(2023, 12, 15)),
]
db.session.add_all(assessments)
db.session.commit()
# Test avec limite par défaut (5)
found = repo.find_with_recent_assessments(class_group.id)
assert found is not None
assert found.name == "6A"
assert hasattr(found, '_recent_assessments')
assert len(found._recent_assessments) == 4
# Vérifier le tri par date décroissante
dates = [a.date for a in found._recent_assessments]
assert dates == sorted(dates, reverse=True)
assert found._recent_assessments[0].title == "Test 4" # Plus récent
def test_find_with_recent_assessments_with_limit(self, app):
"""Test de recherche avec évaluations récentes avec limite."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec plusieurs évaluations
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
assessments = []
for i in range(7):
assessment = Assessment(
title=f"Test {i}",
trimester=1,
class_group_id=class_group.id,
date=date(2023, 10, i + 1)
)
assessments.append(assessment)
db.session.add_all(assessments)
db.session.commit()
# Test avec limite de 3
found = repo.find_with_recent_assessments(class_group.id, limit=3)
assert found is not None
assert len(found._recent_assessments) == 3
# Les 3 plus récentes : Test 6, Test 5, Test 4
titles = [a.title for a in found._recent_assessments]
assert titles == ["Test 6", "Test 5", "Test 4"]
def test_find_with_recent_assessments_not_found(self, app):
"""Test de recherche avec évaluations récentes pour classe inexistante."""
with app.app_context():
repo = ClassRepository()
# Test avec ID inexistant
found = repo.find_with_recent_assessments(999)
assert found is None
def test_find_with_full_details_success(self, app):
"""Test de recherche avec tous les détails."""
with app.app_context():
repo = ClassRepository()
# Créer une classe complète
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Ajouter étudiants et évaluations
student = create_student_with_enrollment("Jean", "Dupont", class_group.id)
assessment = Assessment(title="Test", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 15))
db.session.add_all([student, assessment])
db.session.commit()
# Test
found = repo.find_with_full_details(class_group.id)
assert found is not None
assert found.name == "6A"
# Vérifier que les relations sont chargées (pas de requête supplémentaire)
assert len(found.students) >= 1
assert len(found.assessments) >= 1
assert found.students[0].first_name == "Jean"
assert found.assessments[0].title == "Test"
def test_find_with_full_details_not_found(self, app):
"""Test de recherche avec tous les détails pour classe inexistante."""
with app.app_context():
repo = ClassRepository()
# Test avec ID inexistant
found = repo.find_with_full_details(999)
assert found is None
def test_get_students_count(self, app):
"""Test de comptage des étudiants d'une classe."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec étudiants
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Compter avant ajout
count_before = repo.get_students_count(class_group.id)
assert count_before == 0
# Ajouter des étudiants
students = [
create_student_with_enrollment("Jean", "Dupont", class_group.id),
create_student_with_enrollment("Marie", "Martin", class_group.id),
create_student_with_enrollment("Paul", "Durand", class_group.id),
]
db.session.add_all(students)
db.session.commit()
# Compter après ajout
count_after = repo.get_students_count(class_group.id)
assert count_after == 3
def test_get_assessments_count(self, app):
"""Test de comptage des évaluations d'une classe."""
with app.app_context():
repo = ClassRepository()
# Créer une classe avec évaluations
class_group = ClassGroup(name="6A", year="2023-2024")
db.session.add(class_group)
db.session.commit()
# Compter avant ajout
count_before = repo.get_assessments_count(class_group.id)
assert count_before == 0
# Ajouter des évaluations
assessments = [
Assessment(title="Test 1", trimester=1, class_group_id=class_group.id, date=date(2023, 10, 1)),
Assessment(title="Test 2", trimester=2, class_group_id=class_group.id, date=date(2023, 12, 1)),
]
db.session.add_all(assessments)
db.session.commit()
# Compter après ajout
count_after = repo.get_assessments_count(class_group.id)
assert count_after == 2
def test_find_for_form_choices(self, app):
"""Test de recherche pour les choix de formulaires."""
with app.app_context():
repo = ClassRepository()
# Créer des classes de test
classes = [
ClassGroup(name="6C", year="2023-2024"),
ClassGroup(name="6A", year="2023-2024"),
ClassGroup(name="6B", year="2023-2024"),
]
db.session.add_all(classes)
db.session.commit()
# Test
choices = repo.find_for_form_choices()
# Extraire les classes créées pour ce test
test_choices = [c for c in choices if c.name in ["6A", "6B", "6C"]]
# Vérifier le tri par nom
names = [c.name for c in test_choices]
assert names == ["6A", "6B", "6C"]
def test_inherited_methods(self, app):
"""Test des méthodes héritées du BaseRepository."""
with app.app_context():
repo = ClassRepository()
# Test find_by_id
class_group = ClassGroup(name="6A", year="2023-2024")
saved = repo.save(class_group)
repo.commit()
found = repo.find_by_id(class_group.id)
assert found is not None
assert found.name == "6A"
# Test find_all
all_classes = repo.find_all()
assert len(all_classes) >= 1
# Test delete
repo.delete(class_group)
repo.commit()
found_after_delete = repo.find_by_id(class_group.id)
assert found_after_delete is None