Skip to content

Commit 12ca7d4

Browse files
martin056RadoRado
authored andcommitted
Add tests for rosters services
1 parent 5d43571 commit 12ca7d4

File tree

7 files changed

+297
-0
lines changed

7 files changed

+297
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# flake8: noqa
2+
3+
from .rosters import *
4+
from .school_courses import *
5+
from .students import *
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from datetime import date
2+
from typing import Optional
3+
4+
from django.core.exceptions import ValidationError
5+
6+
from styleguide_example.test_examples.models import Student, SchoolCourse, Roster
7+
8+
9+
ROSTER_VALIDATE_PERIOD_OUTSIDE_COURSE_PERIOD = 'Roster period cannot be outside {school_course} period'
10+
ROSTER_CREATE_DIFFERENT_SCHOOLS = 'Cannot roster {student} in {school_course}'
11+
12+
13+
def roster_validate_period(
14+
*,
15+
start_date: date,
16+
end_date: date,
17+
school_course: SchoolCourse
18+
) -> None:
19+
start_date_validation = start_date >= school_course.start_date and\
20+
start_date < school_course.end_date
21+
22+
end_date_validation = end_date <= school_course.end_date and\
23+
end_date > school_course.start_date and\
24+
end_date > start_date
25+
26+
if not start_date_validation or not end_date_validation:
27+
raise ValidationError(ROSTER_VALIDATE_PERIOD_OUTSIDE_COURSE_PERIOD.format(
28+
school_course=school_course
29+
))
30+
31+
32+
def roster_create(
33+
*,
34+
student: Student,
35+
school_course: SchoolCourse,
36+
start_date: Optional[date] = None,
37+
end_date: Optional[date] = None
38+
) -> Roster:
39+
if student.school != school_course.school:
40+
raise ValidationError(
41+
ROSTER_CREATE_DIFFERENT_SCHOOLS.format(student=student, school_course=school_course)
42+
)
43+
44+
start_date = start_date or school_course.start_date
45+
end_date = end_date or school_course.end_date
46+
47+
roster_validate_period(
48+
start_date=start_date,
49+
end_date=end_date,
50+
school_course=school_course
51+
)
52+
53+
roster = Roster(
54+
student=student,
55+
school_course=school_course,
56+
start_date=start_date,
57+
end_date=end_date
58+
)
59+
roster.full_clean()
60+
roster.save()
61+
62+
return roster
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import Optional, Iterable
2+
from datetime import date
3+
4+
from django.db import transaction
5+
from django.utils.text import slugify
6+
7+
from styleguide_example.test_examples.models import (
8+
SchoolCourse,
9+
School,
10+
Student,
11+
Roster
12+
)
13+
14+
15+
@transaction.atomic
16+
def school_course_create(
17+
*,
18+
name: str,
19+
slug: Optional[str] = None,
20+
school: School,
21+
students: Iterable[Student],
22+
start_date: date,
23+
end_date: date
24+
) -> SchoolCourse:
25+
slug = slug or slugify(name)
26+
27+
school_course = SchoolCourse(
28+
name=name,
29+
slug=slug,
30+
start_date=start_date,
31+
end_date=end_date,
32+
school=school
33+
)
34+
school_course.full_clean()
35+
school_course.save()
36+
37+
rosters = []
38+
for student in students:
39+
roster = Roster(
40+
student=student,
41+
school_course=school_course,
42+
start_date=start_date,
43+
end_date=end_date
44+
)
45+
roster.full_clean()
46+
rosters.append(roster)
47+
48+
Roster.objects.bulk_create(rosters, batch_size=50)
49+
50+
return school_course
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from datetime import date
2+
from typing import Optional
3+
4+
from django.utils import timezone
5+
from django.db import transaction
6+
7+
from styleguide_example.test_examples.models import Student, School
8+
from styleguide_example.test_examples.services.rosters import roster_create
9+
from styleguide_example.test_examples.selectors.schools import school_list_school_courses
10+
11+
12+
@transaction.atomic
13+
def student_create(
14+
*,
15+
email: str,
16+
school: School,
17+
start_date: Optional[date] = None
18+
) -> Student:
19+
student = Student(
20+
email=email,
21+
school=school
22+
)
23+
student.full_clean()
24+
student.save()
25+
26+
start_date = start_date or timezone.now()
27+
28+
school_courses = school_list_school_courses(
29+
school=school,
30+
start_date=start_date
31+
).select_related('school_course__school')
32+
33+
for school_course in school_courses:
34+
roster_create(
35+
student=student,
36+
school_course=school_course,
37+
start_date=None
38+
)
39+
40+
return student

styleguide_example/test_examples/tests/services/__init__.py

Whitespace-only changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from unittest.mock import patch
2+
from datetime import timedelta
3+
4+
from django.test import TestCase
5+
from django.core.exceptions import ValidationError
6+
from django.utils import timezone
7+
8+
from styleguide_example.test_examples.tests.factories import (
9+
StudentFactory,
10+
SchoolCourseFactory
11+
)
12+
13+
from styleguide_example.test_examples.models import Roster
14+
from styleguide_example.test_examples.services import (
15+
roster_create,
16+
ROSTER_CREATE_DIFFERENT_SCHOOLS
17+
)
18+
19+
20+
class RosterCreateTests(TestCase):
21+
def test_service_raises_error_if_different_schools(self):
22+
school_course = SchoolCourseFactory.build()
23+
student = StudentFactory.build()
24+
25+
with self.assertRaisesMessage(
26+
ValidationError,
27+
ROSTER_CREATE_DIFFERENT_SCHOOLS.format(student=student, school_course=school_course)
28+
):
29+
roster_create(student=student, school_course=school_course)
30+
31+
self.assertEqual(Roster.objects.count(), 0)
32+
33+
@patch('styleguide_example.test_examples.services.rosters.roster_validate_period')
34+
def test_service_does_not_create_roster_if_period_is_not_valid(self, roster_validate_period_mock):
35+
roster_validate_period_mock.side_effect = ValidationError('')
36+
37+
school_course = SchoolCourseFactory.build()
38+
student = StudentFactory.build()
39+
40+
with self.assertRaises(ValidationError):
41+
roster_create(student=student, school_course=school_course)
42+
43+
self.assertEqual(Roster.objects.count(), 0)
44+
45+
@patch('styleguide_example.test_examples.services.rosters.roster_validate_period')
46+
def test_service_uses_school_course_period_for_default_period(self, roster_validate_period_mock):
47+
school_course = SchoolCourseFactory()
48+
student = StudentFactory(school=school_course.school)
49+
50+
roster = roster_create(student=student, school_course=school_course)
51+
52+
self.assertEqual(roster.start_date, school_course.start_date)
53+
self.assertEqual(roster.end_date, school_course.end_date)
54+
55+
@patch('styleguide_example.test_examples.services.rosters.roster_validate_period')
56+
def test_service_doesn_not_school_course_period_if_dates_are_passed(self, roster_validate_period_mock):
57+
school_course = SchoolCourseFactory()
58+
student = StudentFactory(school=school_course.school)
59+
60+
start_date = timezone.now().date()
61+
end_date = school_course.end_date - timedelta(days=1)
62+
63+
roster = roster_create(
64+
student=student,
65+
school_course=school_course,
66+
start_date=start_date,
67+
end_date=end_date
68+
)
69+
70+
self.assertEqual(roster.start_date, start_date)
71+
self.assertEqual(roster.end_date, end_date)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from datetime import timedelta
2+
3+
from django.test import TestCase
4+
from django.core.exceptions import ValidationError
5+
6+
from styleguide_example.test_examples.tests.factories import SchoolCourseFactory
7+
from styleguide_example.test_examples.services import (
8+
roster_validate_period,
9+
ROSTER_VALIDATE_PERIOD_OUTSIDE_COURSE_PERIOD
10+
)
11+
12+
13+
class RosterValidatePeriodTests(TestCase):
14+
def test_service_does_not_raise_error_if_valid_period(self):
15+
course = SchoolCourseFactory.build()
16+
17+
roster_period_equal_to_course_period = {
18+
'start_date': course.start_date,
19+
'end_date': course.end_date
20+
}
21+
roster_period_inside_course_period = {
22+
'start_date': course.start_date + timedelta(days=1),
23+
'end_date': course.end_date - timedelta(days=1)
24+
}
25+
roster_period_end_inside_course_period = {
26+
'start_date': course.start_date,
27+
'end_date': course.end_date - timedelta(days=1)
28+
}
29+
roster_period_start_inside_course_period = {
30+
'start_date': course.start_date + timedelta(days=1),
31+
'end_date': course.end_date
32+
}
33+
34+
roster_validate_period(school_course=course, **roster_period_equal_to_course_period)
35+
roster_validate_period(school_course=course, **roster_period_inside_course_period)
36+
roster_validate_period(school_course=course, **roster_period_end_inside_course_period)
37+
roster_validate_period(school_course=course, **roster_period_start_inside_course_period)
38+
39+
def test_services_raises_error_for_rosters_outside_period(self):
40+
course = SchoolCourseFactory.build()
41+
42+
roster_period_end_before_course_start = {
43+
'start_date': course.start_date - timedelta(days=10),
44+
'end_date': course.start_date - timedelta(days=5)
45+
}
46+
roster_period_start_before_course_start = {
47+
'start_date': course.start_date - timedelta(days=10),
48+
'end_date': course.start_date + timedelta(days=1)
49+
}
50+
roster_period_start_after_course_end = {
51+
'start_date': course.end_date + timedelta(days=5),
52+
'end_date': course.end_date + timedelta(days=10)
53+
}
54+
roster_period_end_after_course_end = {
55+
'start_date': course.end_date - timedelta(days=1),
56+
'end_date': course.end_date + timedelta(days=10)
57+
}
58+
59+
for roster_period in [
60+
roster_period_end_before_course_start,
61+
roster_period_start_before_course_start,
62+
roster_period_start_after_course_end,
63+
roster_period_end_after_course_end
64+
]:
65+
with self.assertRaisesMessage(
66+
ValidationError,
67+
ROSTER_VALIDATE_PERIOD_OUTSIDE_COURSE_PERIOD.format(school_course=course)
68+
):
69+
roster_validate_period(school_course=course, **roster_period)

0 commit comments

Comments
 (0)