refact: use repositories patterns for classes
This commit is contained in:
536
tests/test_class_repository.py
Normal file
536
tests/test_class_repository.py
Normal file
@@ -0,0 +1,536 @@
|
||||
import pytest
|
||||
from datetime import date
|
||||
from werkzeug.exceptions import NotFound
|
||||
from models import db, ClassGroup, Student, Assessment
|
||||
from repositories.class_repository import ClassRepository
|
||||
|
||||
|
||||
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()
|
||||
|
||||
students = [
|
||||
Student(first_name="Jean", last_name="Dupont", class_group_id=class_group.id),
|
||||
Student(first_name="Marie", last_name="Martin", class_group_id=class_group.id),
|
||||
]
|
||||
db.session.add_all(students)
|
||||
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()
|
||||
|
||||
student = Student(first_name="Jean", last_name="Dupont", class_group_id=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()
|
||||
|
||||
students = [
|
||||
Student(first_name="Marie", last_name="Zidane", class_group_id=class_group.id),
|
||||
Student(first_name="Jean", last_name="Dupont", class_group_id=class_group.id),
|
||||
Student(first_name="Paul", last_name="Martin", class_group_id=class_group.id),
|
||||
]
|
||||
db.session.add_all(students)
|
||||
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 = Student(first_name="Jean", last_name="Dupont", class_group_id=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 = [
|
||||
Student(first_name="Jean", last_name="Dupont", class_group_id=class_group.id),
|
||||
Student(first_name="Marie", last_name="Martin", class_group_id=class_group.id),
|
||||
Student(first_name="Paul", last_name="Durand", class_group_id=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
|
||||
@@ -2,6 +2,7 @@ import pytest
|
||||
from datetime import date
|
||||
from forms import AssessmentForm, ClassGroupForm, StudentForm
|
||||
from models import db, ClassGroup
|
||||
from repositories import ClassRepository
|
||||
|
||||
|
||||
class TestAssessmentForm:
|
||||
@@ -66,6 +67,8 @@ class TestAssessmentForm:
|
||||
|
||||
with app.test_request_context():
|
||||
form = AssessmentForm()
|
||||
class_repo = ClassRepository()
|
||||
form.populate_class_choices(class_repo)
|
||||
|
||||
assert len(form.class_group_id.choices) >= 2
|
||||
choice_names = [choice[1] for choice in form.class_group_id.choices]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
import json
|
||||
from unittest.mock import patch, MagicMock
|
||||
from models import db, Assessment, ClassGroup, Exercise, GradingElement
|
||||
from datetime import date
|
||||
|
||||
@@ -104,4 +105,169 @@ class TestAssessmentCreation:
|
||||
# Test POST to new route without proper data
|
||||
response = client.post('/assessments/new', data={})
|
||||
# Should return form with errors or redirect
|
||||
assert response.status_code in [200, 302, 400]
|
||||
assert response.status_code in [200, 302, 400]
|
||||
|
||||
|
||||
class TestAssessmentFormWithClassRepository:
|
||||
"""Test assessment routes that use ClassRepository for form population"""
|
||||
|
||||
def test_assessment_new_route_uses_class_repository(self, client, app):
|
||||
"""Test that the new assessment route uses ClassRepository to populate form choices"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.assessments.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
# Mock class objects with id and name attributes
|
||||
mock_class_obj = MagicMock()
|
||||
mock_class_obj.id = class_group.id
|
||||
mock_class_obj.name = "6A"
|
||||
mock_repo.find_for_form_choices.return_value = [mock_class_obj]
|
||||
|
||||
response = client.get('/assessments/new')
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify that ClassRepository was instantiated and used
|
||||
mock_repo_class.assert_called()
|
||||
|
||||
def test_assessment_edit_route_uses_class_repository(self, client, app):
|
||||
"""Test that the edit assessment route uses ClassRepository to populate form choices"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
assessment = Assessment(
|
||||
title="Test Math",
|
||||
description="Contrôle de mathématiques",
|
||||
date=date(2023, 10, 15),
|
||||
trimester=1,
|
||||
class_group_id=class_group.id
|
||||
)
|
||||
db.session.add(assessment)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.assessments.AssessmentRepository') as mock_assessment_repo_class:
|
||||
with patch('routes.assessments.ClassRepository') as mock_class_repo_class:
|
||||
# Mock assessment repository
|
||||
mock_assessment_repo = MagicMock()
|
||||
mock_assessment_repo_class.return_value = mock_assessment_repo
|
||||
mock_assessment_repo.get_with_full_details_or_404.return_value = assessment
|
||||
|
||||
# Mock class repository
|
||||
mock_class_repo = MagicMock()
|
||||
mock_class_repo_class.return_value = mock_class_repo
|
||||
# Mock class objects with id and name attributes
|
||||
mock_class_obj = MagicMock()
|
||||
mock_class_obj.id = class_group.id
|
||||
mock_class_obj.name = "6A"
|
||||
mock_class_repo.find_for_form_choices.return_value = [mock_class_obj]
|
||||
|
||||
response = client.get(f'/assessments/{assessment.id}/edit')
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify that both repositories were instantiated and used
|
||||
mock_assessment_repo_class.assert_called()
|
||||
mock_class_repo_class.assert_called()
|
||||
mock_assessment_repo.get_with_full_details_or_404.assert_called_once_with(assessment.id)
|
||||
|
||||
def test_assessment_list_route_uses_class_repository(self, client, app):
|
||||
"""Test that the assessments list route uses ClassRepository for filtering"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.assessments.AssessmentRepository') as mock_assessment_repo_class:
|
||||
with patch('routes.assessments.ClassRepository') as mock_class_repo_class:
|
||||
# Mock repositories
|
||||
mock_assessment_repo = MagicMock()
|
||||
mock_assessment_repo_class.return_value = mock_assessment_repo
|
||||
mock_assessment_repo.find_by_filters.return_value = []
|
||||
|
||||
mock_class_repo = MagicMock()
|
||||
mock_class_repo_class.return_value = mock_class_repo
|
||||
# Mock class objects with id and name attributes
|
||||
mock_class_obj = MagicMock()
|
||||
mock_class_obj.id = class_group.id
|
||||
mock_class_obj.name = "6A"
|
||||
mock_class_repo.find_for_form_choices.return_value = [mock_class_obj]
|
||||
|
||||
response = client.get('/assessments/')
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify that both repositories were instantiated and used
|
||||
mock_assessment_repo_class.assert_called()
|
||||
mock_class_repo_class.assert_called()
|
||||
mock_assessment_repo.find_by_filters.assert_called()
|
||||
mock_class_repo.find_for_form_choices.assert_called()
|
||||
|
||||
def test_assessment_form_validation_with_class_repository_integration(self, client, app):
|
||||
"""Test form validation with ClassRepository integration"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
# Test form with valid class_group_id
|
||||
form_data = {
|
||||
'title': 'Test Assessment',
|
||||
'description': 'Test Description',
|
||||
'date': '2023-10-15',
|
||||
'trimester': '1',
|
||||
'class_group_id': str(class_group.id),
|
||||
'coefficient': '1.0',
|
||||
'csrf_token': 'dummy'
|
||||
}
|
||||
|
||||
with patch('routes.assessments.ClassRepository') as mock_class_repo_class:
|
||||
mock_class_repo = MagicMock()
|
||||
mock_class_repo_class.return_value = mock_class_repo
|
||||
# Mock class objects with id and name attributes
|
||||
mock_class_obj = MagicMock()
|
||||
mock_class_obj.id = class_group.id
|
||||
mock_class_obj.name = "6A"
|
||||
mock_class_repo.find_for_form_choices.return_value = [mock_class_obj]
|
||||
|
||||
response = client.post('/assessments/new', data=form_data)
|
||||
# Should process successfully (redirect or success page)
|
||||
assert response.status_code in [200, 302]
|
||||
|
||||
# Verify ClassRepository was used
|
||||
mock_class_repo_class.assert_called()
|
||||
|
||||
|
||||
class TestAssessmentRepositoryIntegration:
|
||||
"""Test assessment routes integration with repositories"""
|
||||
|
||||
def test_assessment_detail_uses_repository(self, client, app):
|
||||
"""Test that assessment detail route uses AssessmentRepository"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
assessment = Assessment(
|
||||
title="Test Math",
|
||||
description="Contrôle de mathématiques",
|
||||
date=date(2023, 10, 15),
|
||||
trimester=1,
|
||||
class_group_id=class_group.id
|
||||
)
|
||||
db.session.add(assessment)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.assessments.AssessmentRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.get_with_full_details_or_404.return_value = assessment
|
||||
|
||||
response = client.get(f'/assessments/{assessment.id}')
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify that AssessmentRepository was used
|
||||
mock_repo_class.assert_called()
|
||||
mock_repo.get_with_full_details_or_404.assert_called_once_with(assessment.id)
|
||||
227
tests/test_routes_classes.py
Normal file
227
tests/test_routes_classes.py
Normal file
@@ -0,0 +1,227 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from models import db, ClassGroup
|
||||
from repositories.class_repository import ClassRepository
|
||||
|
||||
|
||||
class TestClassesRoutes:
|
||||
"""Tests for class management routes"""
|
||||
|
||||
def test_classes_list_route(self, client, app):
|
||||
"""Test the classes listing route"""
|
||||
with app.app_context():
|
||||
# Create test data
|
||||
class1 = ClassGroup(name="6A", year="2023-2024")
|
||||
class2 = ClassGroup(name="5B", year="2023-2024")
|
||||
db.session.add_all([class1, class2])
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/classes')
|
||||
assert response.status_code == 200
|
||||
assert b'6A' in response.data
|
||||
assert b'5B' in response.data
|
||||
|
||||
def test_classes_new_route(self, client, app):
|
||||
"""Test the new class form route"""
|
||||
with app.app_context():
|
||||
response = client.get('/classes/new')
|
||||
assert response.status_code == 200
|
||||
assert 'Créer une nouvelle classe'.encode('utf-8') in response.data
|
||||
|
||||
def test_classes_create_valid_data(self, client, app):
|
||||
"""Test creating a class with valid data"""
|
||||
with app.app_context():
|
||||
data = {
|
||||
'name': '6A',
|
||||
'description': 'Classe de 6ème A',
|
||||
'year': '2023-2024',
|
||||
'csrf_token': 'dummy' # CSRF disabled in tests
|
||||
}
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.exists_by_name.return_value = False
|
||||
|
||||
response = client.post('/classes/', data=data, follow_redirects=True)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify repository methods were called
|
||||
mock_repo.exists_by_name.assert_called_once_with('6A')
|
||||
|
||||
def test_classes_create_duplicate_name(self, client, app):
|
||||
"""Test creating a class with existing name"""
|
||||
with app.app_context():
|
||||
data = {
|
||||
'name': '6A',
|
||||
'description': 'Classe de 6ème A',
|
||||
'year': '2023-2024',
|
||||
'csrf_token': 'dummy'
|
||||
}
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.exists_by_name.return_value = True
|
||||
|
||||
response = client.post('/classes/', data=data)
|
||||
assert response.status_code == 200
|
||||
assert 'Une classe avec ce nom existe déjà'.encode('utf-8') in response.data
|
||||
|
||||
def test_classes_edit_route(self, client, app):
|
||||
"""Test the edit class form route"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.get_or_404.return_value = class_group
|
||||
|
||||
response = client.get(f'/classes/{class_group.id}/edit')
|
||||
assert response.status_code == 200
|
||||
assert 'Modifier la classe'.encode('utf-8') in response.data
|
||||
|
||||
mock_repo.get_or_404.assert_called_once_with(class_group.id)
|
||||
|
||||
def test_classes_update_valid_data(self, client, app):
|
||||
"""Test updating a class with valid data"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
data = {
|
||||
'name': '6A Modified',
|
||||
'description': 'Classe modifiée',
|
||||
'year': '2023-2024',
|
||||
'csrf_token': 'dummy'
|
||||
}
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.get_or_404.return_value = class_group
|
||||
mock_repo.exists_by_name.return_value = False
|
||||
|
||||
response = client.post(f'/classes/{class_group.id}', data=data, follow_redirects=True)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify repository methods were called
|
||||
mock_repo.get_or_404.assert_called_once_with(class_group.id)
|
||||
mock_repo.exists_by_name.assert_called_once_with('6A Modified', exclude_id=class_group.id)
|
||||
|
||||
def test_classes_delete_with_dependencies(self, client, app):
|
||||
"""Test deleting a class with dependencies"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.get_or_404.return_value = class_group
|
||||
mock_repo.can_be_deleted.return_value = (False, {'students': 5, 'assessments': 3})
|
||||
|
||||
response = client.post(f'/classes/{class_group.id}/delete', follow_redirects=True)
|
||||
assert response.status_code == 200
|
||||
assert 'Impossible de supprimer'.encode('utf-8') in response.data
|
||||
|
||||
# Verify repository methods were called
|
||||
mock_repo.get_or_404.assert_called_once_with(class_group.id)
|
||||
mock_repo.can_be_deleted.assert_called_once_with(class_group.id)
|
||||
|
||||
def test_classes_delete_success(self, client, app):
|
||||
"""Test successful deletion of a class"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.get_or_404.return_value = class_group
|
||||
mock_repo.can_be_deleted.return_value = (True, {'students': 0, 'assessments': 0})
|
||||
|
||||
response = client.post(f'/classes/{class_group.id}/delete', follow_redirects=True)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify repository methods were called
|
||||
mock_repo.get_or_404.assert_called_once_with(class_group.id)
|
||||
mock_repo.can_be_deleted.assert_called_once_with(class_group.id)
|
||||
|
||||
def test_classes_details_repository_usage(self, app):
|
||||
"""Test that the class details route uses ClassRepository correctly"""
|
||||
with app.app_context():
|
||||
class_group = ClassGroup(name="6A", year="2023-2024")
|
||||
db.session.add(class_group)
|
||||
db.session.commit()
|
||||
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
with patch('routes.classes.render_template') as mock_render:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.find_with_full_details.return_value = class_group
|
||||
mock_render.return_value = "rendered template"
|
||||
|
||||
from routes.classes import bp
|
||||
with app.test_client() as client:
|
||||
response = client.get(f'/classes/{class_group.id}/details')
|
||||
|
||||
# Verify repository was used correctly
|
||||
mock_repo.find_with_full_details.assert_called_once_with(class_group.id)
|
||||
|
||||
def test_classes_details_not_found_repository_usage(self, app):
|
||||
"""Test class details route with non-existent class uses repository correctly"""
|
||||
with app.app_context():
|
||||
with patch('routes.classes.ClassRepository') as mock_repo_class:
|
||||
with patch('flask.abort') as mock_abort:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.find_with_full_details.return_value = None
|
||||
|
||||
from routes.classes import bp
|
||||
with app.test_client() as client:
|
||||
try:
|
||||
client.get('/classes/999/details')
|
||||
except:
|
||||
pass # abort() raises an exception
|
||||
|
||||
# Verify repository was used and abort was called
|
||||
mock_repo.find_with_full_details.assert_called_once_with(999)
|
||||
mock_abort.assert_called_once_with(404)
|
||||
|
||||
|
||||
class TestMainClassesRoute:
|
||||
"""Tests for the main classes route in app.py"""
|
||||
|
||||
def test_main_classes_route_with_repository(self, client, app):
|
||||
"""Test the main /classes route uses ClassRepository"""
|
||||
with app.app_context():
|
||||
# Create test data
|
||||
class1 = ClassGroup(name="6A", year="2023-2024")
|
||||
class2 = ClassGroup(name="5B", year="2023-2024")
|
||||
db.session.add_all([class1, class2])
|
||||
db.session.commit()
|
||||
|
||||
# Test the actual implementation without mocking
|
||||
response = client.get('/classes')
|
||||
assert response.status_code == 200
|
||||
assert b'6A' in response.data
|
||||
assert b'5B' in response.data
|
||||
|
||||
def test_main_classes_route_error_handling(self, client, app):
|
||||
"""Test error handling in main classes route"""
|
||||
with app.app_context():
|
||||
with patch('app.ClassRepository') as mock_repo_class:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_class.return_value = mock_repo
|
||||
mock_repo.find_all_ordered.side_effect = Exception("Database error")
|
||||
|
||||
response = client.get('/classes')
|
||||
assert response.status_code == 500
|
||||
assert 'Erreur lors du chargement des classes'.encode('utf-8') in response.data
|
||||
Reference in New Issue
Block a user