From 0bafcacdad912a598cdf4e031cab5059efb30b18 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 23 Sep 2018 16:40:14 +0200 Subject: tests: ajout de tests pour le parseur UPS2018 Le parseur est une des parties les plus importantes de celcatsanitizer, mais ni le parseur 2017, ni le parseur 2018 n’ont eu de test unitaires à proprement parler. Jusqu’ici, pour tester ce composant, on ajoutait une source dans la base, on la récupérait, et on regardait si tout correspondait plus ou moins. Cette technique a plusieurs inconvénients : c’était une tâche rébarbative et pas systématiquement effectuée, ce qui a posé quelques problèmes par le passé, certains cas pouvaient ne pas se trouver dans la source au moment de la récupération, et ce n’était pas reproductible proprement. Rajouter des tests permettra donc de tester efficacement le parseur, avec tous les cas de figure, rapidement et en utilisant seulement des ressources locales. Pour éviter d’utiliser le réseau, le module requests est mocké lorsqu’on teste des fonctions qui l’utilisent. L’initialisation du parseur et ses fonctions __get_event(), get_events(), get_source() et get_update_date() (ainsi que la fonction find_events_list() de manière indirecte) sont testées. Signed-off-by: Alban Gruin --- tests.py | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) (limited to 'tests.py') diff --git a/tests.py b/tests.py index 2568688..a6c84d7 100644 --- a/tests.py +++ b/tests.py @@ -13,13 +13,42 @@ # You should have received a copy of the GNU Affero General Public License # along with celcatsanitizer. If not, see . +from unittest import mock + from django.test import TestCase from django.utils import timezone +from .management.parsers.ups2018 import Parser as UPS2018Parser from .models import Course, Group, Room, Source, Timetable, Year from .utils import tz_now import datetime +import os + + +def mock_requests_get(*args, **kwargs): + class MockedResponse: + def __init__(self, content=""): + self.encoding = "utf-8" + self.content = content + self.status = 200 + + def raise_for_status(self): + return + + def mocked_response_from_file(filename): + module_dir = os.path.dirname(__file__) + filepath = os.path.join(module_dir, filename) + with open(filepath, "r") as response: + return MockedResponse(response.read()) + + if args[0] == "https://example.org/2018": + if not kwargs["params"]: + return mocked_response_from_file("tests/data/2018/september.html") + elif kwargs["params"].get("Date") == "20181001": + return mocked_response_from_file("tests/data/2018/october.html") + else: + return MockedResponse("") class CourseTestCase(TestCase): @@ -345,3 +374,201 @@ class RoomTestCase(TestCase): self.assertNotIn(self.rooms[4], rooms) self.assertIn(self.rooms[5], rooms) self.assertIn(self.rooms[6], rooms) + + +class UPS2018ParserTestCase(TestCase): + @mock.patch("requests.get", side_effect=mock_requests_get) + def setUp(self, *args, **kwargs): + source = Source.objects.create(url="https://example.org/2018") + self.room = Room.objects.create(name="Salle quelconque") + self.room2 = Room.objects.create(name="Salle quelconque 2") + + self.group = Group(celcat_name="L3 Info s1 CMA") + self.group.source = source + self.group.save() + + self.parser = UPS2018Parser(source) + + def test_get_event(self): + get_event = self.parser._Parser__get_event + + event = get_event( + {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": "(10:00-12:00)
COURS/TD
Cours quelconque;AAA" + "
L3 Info s1 CMA;L3 Info s1 TDA2
" + "Salle quelconque;Salle quelconque 2
Commentaire"}, + timezone.make_aware(datetime.datetime(2018, 9, 21)), + timezone.make_aware(datetime.datetime(2018, 9, 1)), + timezone.make_aware(datetime.datetime(2018, 10, 1)), + 2018, 38) + + ngroup = Group.objects.filter(name="L3 Info s1 TDA2").first() + self.assertIsNotNone(ngroup) + + self.assertEqual(event.name, "Cours quelconque, AAA") + self.assertEqual(event.type, "COURS/TD") + self.assertIn(self.group, event.groups.all()) + self.assertIn(ngroup, event.groups.all()) + self.assertEqual(event.groups.count(), 2) + self.assertIn(self.room, event.rooms.all()) + self.assertIn(self.room2, event.rooms.all()) + self.assertEqual(event.rooms.count(), 2) + self.assertEqual(event.notes, "Commentaire") + self.assertEqual(event.begin, timezone.make_aware( + datetime.datetime(2018, 9, 21, 10, 0, 0))) + self.assertEqual(event.end, timezone.make_aware( + datetime.datetime(2018, 9, 21, 12, 0, 0))) + + events = [ + { + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 CMA
Salle quelconque", + "name": "Cours quelconque", "type": "COURS/TD", + "group": self.group, + "room": self.room, + }, + { + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 TDA2
Salle quelconque 3", + "name": "Cours quelconque", + "type": "COURS/TD", + "group": ngroup, + "notes": "Salle quelconque 3" + }, + { + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 CMA
Salle quelconque 3
Commentaire", + "name": "Cours quelconque", + "type": "COURS/TD", + "group": self.group, + "notes": "Salle quelconque 3\nCommentaire", + }, + { + "text": "(10:00-12:00)
COURS/TD" + "
L3 Info s1 CMA
Salle quelconque 3", + "name": "COURS/TD", + "group": self.group, + "notes": "Salle quelconque 3" + }, + { + "text": "COURS/TD
L3 Info s1 CMA
Salle quelconque 3", + "name": "COURS/TD", + "group": self.group, + "notes": "Salle quelconque 3" + }, + { + "text": "L3 Info s1 CMA
Salle quelconque", + "name": "Sans nom", + "group": self.group, + "room": self.room + }, + { + "text": "L3 Info s1 CMA
Salle quelconque 3", + "name": "Sans nom", + "group": self.group, + "notes": "Salle quelconque 3" + }, + { + "text": "(10:00-12:00)
L3 Info s1 CMA
Salle quelconque", + "name": "Sans nom", + "group": self.group, + "room": self.room + } + ] + + for e in events: + event = get_event( + {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": e["text"]}, + timezone.make_aware(datetime.datetime(2018, 9, 21)), + timezone.make_aware(datetime.datetime(2018, 9, 1)), + timezone.make_aware(datetime.datetime(2018, 10, 1)), + 2018, 38) + + self.assertEqual(event.name, e["name"]) + self.assertIn(e["group"], event.groups.all()) + self.assertEqual(event.groups.count(), 1) + + if "type" in e: + self.assertEqual(event.type, e["type"]) + else: + self.assertIsNone(event.type) + + if "room" in e: + self.assertIn(e["room"], event.rooms.all()) + self.assertEqual(event.rooms.count(), 1) + else: + self.assertEqual(event.rooms.count(), 0) + + if "notes" in e: + self.assertEqual(event.notes, e["notes"]) + else: + self.assertIsNone(event.notes) + + event = get_event( + {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": "Global Event"}, + timezone.make_aware(datetime.datetime(2018, 9, 21)), + timezone.make_aware(datetime.datetime(2018, 9, 1)), + timezone.make_aware(datetime.datetime(2018, 10, 1)), + 2018, 38) + self.assertIsNone(event) + + event = get_event( + {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": "L3 Info s1 CMA
Salle quelconque 2"}, + timezone.make_aware(datetime.datetime(2018, 9, 21)), + timezone.make_aware(datetime.datetime(2018, 9, 1)), + timezone.make_aware(datetime.datetime(2018, 10, 1)), + 2018, 39) + self.assertIsNone(event) + + @mock.patch("requests.get", side_effect=mock_requests_get) + def test_get_events(self, *args, **kwargs): + self.parser.get_source() + courses = [ + {"begin": + timezone.make_aware(datetime.datetime(2018, 9, 21, 10, 00, 00)), + "end": + timezone.make_aware(datetime.datetime(2018, 9, 21, 12, 00, 00))}, + {"begin": + timezone.make_aware(datetime.datetime(2018, 10, 22, 10, 00, 00)), + "end": + timezone.make_aware(datetime.datetime(2018, 10, 22, 12, 00, 00))} + ] + + for i, course in enumerate(self.parser.get_events( + timezone.make_aware(datetime.datetime(2018, 9, 21)))): + self.assertEqual(course.name, "Cours quelconque") + self.assertEqual(course.type, "COURS/TD") + self.assertIn(self.group, course.groups.all()) + self.assertEqual(course.groups.count(), 1) + self.assertIn(self.room, course.rooms.all()) + self.assertEqual(course.rooms.count(), 1) + self.assertIsNone(course.notes) + self.assertEqual(course.begin, courses[i]["begin"]) + self.assertEqual(course.end, courses[i]["end"]) + + self.assertEqual(i, len(courses) - 1) + + @mock.patch("requests.get", side_effect=mock_requests_get) + def test_get_source(self, *args, **kwargs): + events = self.parser.get_source() + self.assertEquals(events, [ + [{ + "start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 CMA
Salle quelconque" + }], [{ + "start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 CMA
Salle quelconque" + }, { + "start": "2018-10-22T10:00:00", "end": "2018-10-22T12:00:00", + "text": "(10:00-12:00)
COURS/TD
Cours quelconque" + "
L3 Info s1 CMA
Salle quelconque" + }], [], [], [], [], [], [], [], [], []]) + + def test_get_update_date(self): + # Pas de date de mise à jour dans ce format + self.assertIsNone(self.parser.get_update_date()) -- cgit v1.2.1 From 070d613b29359465dc8ffdd1dc3ffb116717fc3b Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 29 Sep 2018 20:32:26 +0200 Subject: tests: vérification du nombre de cours dans le test du parseur Dans certaines conditions, un nouveau cours doit être créé dans la base de données, mais parfois non. Pour vérifier qu’un seul cours a bien été rajouté (ou non, en fonction des cas), on teste aussi le nombre de cours présent dans la base de données. Signed-off-by: Alban Gruin --- tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tests.py') diff --git a/tests.py b/tests.py index a6c84d7..8ec90ba 100644 --- a/tests.py +++ b/tests.py @@ -391,6 +391,7 @@ class UPS2018ParserTestCase(TestCase): def test_get_event(self): get_event = self.parser._Parser__get_event + count = Course.objects.count() event = get_event( {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", @@ -419,6 +420,9 @@ class UPS2018ParserTestCase(TestCase): self.assertEqual(event.end, timezone.make_aware( datetime.datetime(2018, 9, 21, 12, 0, 0))) + self.assertEqual(count, Course.objects.count() - 1) + count += 1 + events = [ { "text": "(10:00-12:00)
COURS/TD
Cours quelconque" @@ -505,6 +509,9 @@ class UPS2018ParserTestCase(TestCase): else: self.assertIsNone(event.notes) + self.assertEqual(count, Course.objects.count() - 1) + count += 1 + event = get_event( {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", "text": "Global Event"}, @@ -513,6 +520,7 @@ class UPS2018ParserTestCase(TestCase): timezone.make_aware(datetime.datetime(2018, 10, 1)), 2018, 38) self.assertIsNone(event) + self.assertEqual(count, Course.objects.count()) event = get_event( {"start": "2018-09-21T10:00:00", "end": "2018-09-21T12:00:00", @@ -522,6 +530,7 @@ class UPS2018ParserTestCase(TestCase): timezone.make_aware(datetime.datetime(2018, 10, 1)), 2018, 39) self.assertIsNone(event) + self.assertEqual(count, Course.objects.count()) @mock.patch("requests.get", side_effect=mock_requests_get) def test_get_events(self, *args, **kwargs): -- cgit v1.2.1 From 4980d9cb47c99056b2f228408bab536393e100bb Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Tue, 9 Oct 2018 20:23:21 +0200 Subject: tests: test du comportement du parseur lorsque la source est mauvaise Il est possible que la source renvoie des pages vides ou incorrectes, il est donc nécessaire de tester le comportement du parseur dans ces cas-là. Ajout d’un modèle de page vide dans les données de test. Signed-off-by: Alban Gruin --- tests.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'tests.py') diff --git a/tests.py b/tests.py index 8ec90ba..89aefdb 100644 --- a/tests.py +++ b/tests.py @@ -18,6 +18,7 @@ from unittest import mock from django.test import TestCase from django.utils import timezone +from .management.parsers.abstractparser import ParserError from .management.parsers.ups2018 import Parser as UPS2018Parser from .models import Course, Group, Room, Source, Timetable, Year from .utils import tz_now @@ -43,12 +44,14 @@ def mock_requests_get(*args, **kwargs): return MockedResponse(response.read()) if args[0] == "https://example.org/2018": - if not kwargs["params"]: + if "params" not in kwargs or not kwargs["params"]: return mocked_response_from_file("tests/data/2018/september.html") elif kwargs["params"].get("Date") == "20181001": return mocked_response_from_file("tests/data/2018/october.html") else: - return MockedResponse("") + return mocked_response_from_file("tests/data/2018/empty.html") + + return MockedResponse("") class CourseTestCase(TestCase): @@ -581,3 +584,28 @@ class UPS2018ParserTestCase(TestCase): def test_get_update_date(self): # Pas de date de mise à jour dans ce format self.assertIsNone(self.parser.get_update_date()) + + +class UPS2018BrokenSourceTestCase(TestCase): + @mock.patch("requests.get") + def test_broken_source(self, mock_get): + mock_get.return_value = mock_requests_get("") + + source = Source.objects.create(url="https://example.org/2018") + with self.assertRaises(ParserError): + UPS2018Parser(source) + + @mock.patch("requests.get") + def test_half_broken_source(self, mock_get): + source = Source.objects.create(url="https://example.org/2018") + mock_get.side_effect = [ + mock_requests_get(""), + mock_requests_get(source.url) + ] + + parser = UPS2018Parser(source) + self.assertEqual(parser.months, [ + "September, 2018", "October, 2018", "November, 2018", + "December, 2018", "January, 2019", "February, 2019", "March, 2019", + "April, 2019", "May, 2019", "June, 2019", "July, 2019" + ]) -- cgit v1.2.1 From 44e17c23fc9285c24a0c1b3ea46e2ed27e55ae05 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 20 Sep 2018 23:57:21 +0200 Subject: tests: test du tag format_rooms Signed-off-by: Alban Gruin --- tests.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'tests.py') diff --git a/tests.py b/tests.py index 89aefdb..627602c 100644 --- a/tests.py +++ b/tests.py @@ -21,6 +21,7 @@ from django.utils import timezone from .management.parsers.abstractparser import ParserError from .management.parsers.ups2018 import Parser as UPS2018Parser from .models import Course, Group, Room, Source, Timetable, Year +from .templatetags.rooms import format_rooms from .utils import tz_now import datetime @@ -332,13 +333,13 @@ class RoomTestCase(TestCase): group = Group.objects.create(celcat_name="L1 info s2 CMA", source=self.source) - self.rooms = [Room.objects.create(name="0"), - Room.objects.create(name="1"), - Room.objects.create(name="2"), - Room.objects.create(name="3"), - Room.objects.create(name="4"), - Room.objects.create(name="5"), - Room.objects.create(name="6")] + self.rooms = [ + Room.objects.create(name=str(i)) + for i in range(5) + ] + [ + Room.objects.create(name="Amphi {}".format(i)) + for i in range(5, 7) + ] hours = [({"begin": datetime.time(hour=14, minute=0)},), ({"begin": datetime.time(hour=16, minute=0)},), @@ -362,6 +363,23 @@ class RoomTestCase(TestCase): course.groups.add(group) course.rooms.add(room) + def test_format(self): + amphis = self.rooms[-2:] + + self.assertEqual(format_rooms([]), "") + self.assertEqual(format_rooms(self.rooms[:1]), "Salle 0") + self.assertEqual(format_rooms(self.rooms[:2]), "Salles 0, 1") + self.assertEqual(format_rooms([amphis[0]]), "Amphi 5") + self.assertEqual(format_rooms(amphis), "Amphi 5, Amphi 6") + self.assertEqual(format_rooms([amphis[0]] + self.rooms[:1]), + "Amphi 5, salle 0") + self.assertEqual(format_rooms([amphis[0]] + self.rooms[:2]), + "Amphi 5, salles 0, 1") + self.assertEqual(format_rooms(amphis + self.rooms[:1]), + "Amphi 5, Amphi 6, salle 0") + self.assertEqual(format_rooms(amphis + self.rooms[:2]), + "Amphi 5, Amphi 6, salles 0, 1") + def test_qsjps(self): begin = timezone.make_aware(datetime.datetime.combine( self.day, datetime.time(hour=15, minute=0))) -- cgit v1.2.1