From fedcc8ded538b1185499a7c5bdab70446fc29eae Mon Sep 17 00:00:00 2001 From: Rohan Rangray Date: Sat, 16 May 2015 17:29:51 -0700 Subject: [PATCH] Wrote tests for FlashcardMask and Flashcard.edit. Refactored some code. --- flashcards/models.py | 8 +++- flashcards/serializers.py | 2 +- flashcards/tests/test_api.py | 11 ++++-- flashcards/tests/test_models.py | 83 ++++++++++++++++++++++++++++++++++++----- flashcards/validators.py | 14 +++---- flashcards/views.py | 5 +-- 6 files changed, 96 insertions(+), 27 deletions(-) diff --git a/flashcards/models.py b/flashcards/models.py index 97543d4..f213a4d 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -48,7 +48,7 @@ class User(AbstractUser, SimpleEmailConfirmationUserMixin): sections = ManyToManyField('Section', help_text="The sections which the user is enrolled in") def is_in_section(self, section): - return section in self.sections.all() + return self.sections.filter(pk=section.pk).exists() def pull(self, flashcard): if not self.is_in_section(flashcard.section): @@ -57,6 +57,11 @@ class User(AbstractUser, SimpleEmailConfirmationUserMixin): user_card.pulled = datetime.now() user_card.save() + def get_deck(self, section): + if not self.is_in_section(section): + raise ObjectDoesNotExist("User not enrolled in section") + return Flashcard.objects.all().filter(userflashcard__user=self).filter(section=section) + class UserFlashcard(Model): """ @@ -151,6 +156,7 @@ class Flashcard(Model): self.pk = None if 'mask' in new_flashcard: self.mask = new_flashcard['mask'] + self.save() @classmethod def cards_visible_to(cls, user): diff --git a/flashcards/serializers.py b/flashcards/serializers.py index edd2558..59e844c 100644 --- a/flashcards/serializers.py +++ b/flashcards/serializers.py @@ -155,7 +155,7 @@ class FlashcardSerializer(ModelSerializer): return value def validate_mask(self, value): - if value.max_offset() >= len(self.text): + if len(self.data['text']) < value.max_offset(): raise serializers.ValidationError("Mask out of bounds") return value diff --git a/flashcards/tests/test_api.py b/flashcards/tests/test_api.py index ae00b98..d36f8a9 100644 --- a/flashcards/tests/test_api.py +++ b/flashcards/tests/test_api.py @@ -200,7 +200,8 @@ class SectionViewSetTest(APITestCase): def setUp(self): self.client.login(email='none@none.com', password='1234') self.user = User.objects.get(email='none@none.com') - flashcard = Flashcard(text="jason", section=Section.objects.get(pk=1), material_date=now(), author=self.user) + self.section = Section.objects.get(pk=1) + flashcard = Flashcard(text="jason", section=self.section, material_date=now(), author=self.user) flashcard.save() def test_list_sections(self): @@ -208,7 +209,7 @@ class SectionViewSetTest(APITestCase): self.assertEqual(response.status_code, HTTP_200_OK) def test_section_enroll(self): - section = Section.objects.get(pk=1) + section = self.section self.assertFalse(self.user.sections.filter(pk=section.pk)) # test enrolling in a section without a whitelist @@ -232,7 +233,7 @@ class SectionViewSetTest(APITestCase): self.assertTrue(self.user.sections.filter(pk=section.pk).exists()) def test_section_drop(self): - section = Section.objects.get(pk=3) + section = self.section # test dropping a section that the user isn't in response = self.client.post('/api/sections/%d/drop/' % section.pk) @@ -255,5 +256,7 @@ class SectionViewSetTest(APITestCase): self.assertEqual(response.status_code, HTTP_200_OK) def test_section_deck(self): + self.user.sections.add(self.section) + self.user.save() response = self.client.get('/api/sections/1/deck/') - self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.status_code, HTTP_200_OK) \ No newline at end of file diff --git a/flashcards/tests/test_models.py b/flashcards/tests/test_models.py index 3563617..2f59d8f 100644 --- a/flashcards/tests/test_models.py +++ b/flashcards/tests/test_models.py @@ -1,8 +1,8 @@ from datetime import datetime from django.test import TestCase -from flashcards.models import User, Section, Flashcard -from flashcards.validators import OverlapIntervalException +from flashcards.models import User, Section, Flashcard, UserFlashcard +from flashcards.validators import FlashcardMask, OverlapIntervalException class RegistrationTests(TestCase): @@ -37,24 +37,87 @@ class UserTests(TestCase): self.assertEqual(user.sections.count(), 0) +class FlashcardMaskTest(TestCase): + def test_iterable(self): + try: + FlashcardMask(1) + except TypeError as te: + self.assertEqual(te.message, "Interval not a valid iterable") + try: + FlashcardMask([1, 2, 4]) + except TypeError as te: + self.assertEqual(te.message, "Interval not a valid iterable") + + def test_interval(self): + try: + FlashcardMask([[1, 2, 3], [1]]) + except TypeError as te: + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end") + try: + FlashcardMask([[1, 2], [1, 2, 4]]) + except TypeError as te: + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end") + try: + FlashcardMask(([1, 2], [1])) + except TypeError as te: + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end") + try: + FlashcardMask("[1,2,3]") + except TypeError as te: + self.assertEqual(te.message, "Intervals must have exactly 2 elements, begin and end") + + def test_overlap(self): + try: + FlashcardMask({(1, 2), (2, 5)}) + except OverlapIntervalException as oie: + self.assertEqual(oie.message, "Invalid interval offsets in the mask") + try: + FlashcardMask({(1, 20), (12, 15)}) + except OverlapIntervalException as oie: + self.assertEqual(oie.message, "Invalid interval offsets in the mask") + try: + FlashcardMask({(2, 1), (5, 2)}) + except OverlapIntervalException as oie: + self.assertEqual(oie.message, "Invalid interval offsets in the mask") + + + class FlashcardTests(TestCase): def setUp(self): - user = User.objects.create_user(email="none@none.com", password="1234") section = Section.objects.create(department='dept', course_num='101a', course_title='how 2 test', instructor='George Lucas', quarter='SP15') - Flashcard.objects.create(text="This is the text of the Flashcard", - section=section, - author=user, - material_date=datetime.now(), - previous=None, - mask={(24,34), (0, 4)}) + user = User.objects.create_user(email="none@none.com", password="1234") + user.sections.add(section) + flashcard = Flashcard.objects.create(text="This is the text of the Flashcard", + section=section, + author=user, + material_date=datetime.now(), + previous=None, + mask={(24,34), (0, 4)}) + user.save() + section.save() + flashcard.save() - def test_mask_field(self): + def test_flashcard_edit(self): user = User.objects.get(email="none@none.com") + user2 = User.objects.create_user(email="wow@wow.com", password="wow") section = Section.objects.get(course_title='how 2 test') + user2.sections.add(section) + user2.save() + flashcard = Flashcard.objects.filter(author=user).get(text="This is the text of the Flashcard") + pk_backup = flashcard.pk + self.assertTrue(user.is_in_section(section)) + flashcard.edit(user, {}) + self.assertIsNotNone(flashcard.pk) + UserFlashcard.objects.create(user=user2, flashcard=flashcard).save() + flashcard.edit(user2, {'text': 'This is the new text'}) + self.assertNotEqual(flashcard.pk, pk_backup) + + def test_mask_field(self): + user = User.objects.get(email="none@none.com") flashcard = Flashcard.objects.filter(author=user).get(text="This is the text of the Flashcard") self.assertTrue(isinstance(flashcard.mask, set)) self.assertTrue(all([isinstance(interval, tuple) for interval in flashcard.mask])) diff --git a/flashcards/validators.py b/flashcards/validators.py index 3271c81..0653ad1 100644 --- a/flashcards/validators.py +++ b/flashcards/validators.py @@ -3,17 +3,17 @@ from collections import Iterable class FlashcardMask(set): def __init__(self, iterable, *args, **kwargs): + self._iterable_check(iterable) iterable = map(tuple, iterable) super(FlashcardMask, self).__init__(iterable, *args, **kwargs) - self._iterable_check() self._interval_check() self._overlap_check() def max_offset(self): return self._end - def _iterable_check(self): - if not all([isinstance(i, Iterable) for i in self]): + def _iterable_check(self, iterable): + if not isinstance(iterable, Iterable) or not all([isinstance(i, Iterable) for i in iterable]): raise TypeError("Interval not a valid iterable") def _interval_check(self): @@ -27,13 +27,13 @@ class FlashcardMask(set): if not (0 <= beg <= 255) or not (0 <= end <= 255) or not (beg <= end) or not (beg > p_end): raise OverlapIntervalException((beg, end), "Invalid interval offsets in the mask") p_beg, p_end = beg, end - self._end = end + self._end = p_end class OverlapIntervalException(Exception): - def __init__(self, interval, reason): + def __init__(self, interval, message): self.interval = interval - self.reason = reason + self.message = message def __str__(self): - return repr(self.reason) + ': ' + repr(self.interval) + return repr(self.message) + ': ' + repr(self.interval) diff --git a/flashcards/views.py b/flashcards/views.py index 9a2e8b2..843eef7 100644 --- a/flashcards/views.py +++ b/flashcards/views.py @@ -87,9 +87,7 @@ class SectionViewSet(ReadOnlyModelViewSet): """ Gets the contents of a user's deck for a given section. """ - qs = Flashcard.objects.all() - qs = qs.filter(userflashcard__user=request.user) - qs = qs.filter(section = self.get_object()) + qs = request.user.get_deck(self.get_object()) serializer = FlashcardSerializer(qs, many=True) return Response(serializer.data) @@ -323,7 +321,6 @@ class FlashcardViewSet(GenericViewSet, CreateModelMixin, RetrieveModelMixin): new_flashcard = data.validated_data flashcard.edit(user, new_flashcard) - flashcard.save() user_card, created = UserFlashcard.objects.get_or_create(user=user, flashcard=flashcard) user_card.mask = flashcard.mask -- 1.9.1