from datetime import timedelta from django.contrib.auth import get_user_model from django.db.models import Q from django.utils import timezone from django.urls import reverse from rest_framework.test import APIRequestFactory from rest_framework import status from event.models import Event, Place, ActivityTag, ActivityTemplate User = get_user_model() class DataBaseMixin(object): """ provides a datatabse for API tests """ @classmethod def setUpTestData(cls): # Users cls.user1_data = {'username': "user1", 'password': "pass1"} cls.user1 = User.objects.create_user(**cls.user1_data) def setUp(self): # Events self.event1_data = {'title': "event1", 'slug': "slug1", 'beginning_date': timezone.now() + timedelta(days=30), "description": "C'est trop cool !", 'ending_date': timezone.now()+timedelta(days=31), 'created_by': self.user1, } self.event2_data = {"title": "test event", "slug": "test-event", "description": "C'est trop cool !", "beginning_date": "2017-07-18T18:05:00Z", "ending_date": "2017-07-19T18:05:00Z", } self.event1 = Event.objects.create(**self.event1_data) # ActivityTags self.tag1_data = {"name": "tag1", "is_public": False, "color": "#111"} self.tag2_data = {"name": "tag2", "is_public": False, "color": "#222"} self.tag3_data = {"name": "tag3", "is_public": False, "color": "#333"} self.tag1 = ActivityTag.objects.create(**self.tag1_data) self.act_temp1_data = {'title': "act temp1", 'is_public': True, 'remarks': "test remark", 'event': self.event1} self.act_temp2_data = {'title': "act temp2", 'is_public': False, 'remarks': "test remark", 'tags': [self.tag2_data, ]} self.act_temp1 = ActivityTemplate.objects.create(**self.act_temp1_data) self.act_temp1.tags.add(self.tag1) class EventBasedModelTestMixin(DataBaseMixin): """ Note : need to define : `model`: the model served by the API `base_name`: the base_name used in the URL `initial_count`: (will disappear) inital count in the db `data_creation`: name in db used to create new instance `instance_name`: existing instance name in the db used for update/delete `field_tested`: name of field tested in the update test `serializer`: serialiser used for the API tests for models served by the API that are related to an event and whose API urls are nested under ../event//%model """ def user_create_extra(self): """ extra test in creation by a permited user """ pass def pre_update_extra(self, data): """ extra modification for the data sent for update """ return data def post_update_extra(self): """ extra test for updated model """ pass def test_user_create(self): """ ensure a permited user can create a new %model object using API """ data = getattr(self, self.data_creation) event_id = self.event1.id url = reverse('{base_name}-list'.format(base_name=self.base_name), kwargs={'event_pk': event_id}) self.client.force_authenticate(user=self.user1) response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(self.model.objects.count(), self.initial_count + 1) self.assertEqual(self.model.objects.get(id=self.initial_count+1).event, self.event1) self.user_create_extra() def test_user_update(self): """ ensure a permited user can update a new %model object using API """ instance = getattr(self, self.instance_name) factory = APIRequestFactory() instance_id = instance.id event_id = self.event1.id url = reverse('{base_name}-list'.format(base_name=self.base_name), kwargs={'event_pk': event_id}) url = "%s%d/" % (url, instance_id) request = factory.get(url) data = self.serializer(instance, context={'request': request}).data newvalue = "I'm a test" data[self.field_tested] = newvalue data = self.pre_update_extra(data) self.client.force_authenticate(user=self.user1) response = self.client.patch(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) instance.refresh_from_db() self.assertEqual(getattr(instance, self.field_tested), newvalue) self.post_update_extra(instance) def test_user_delete(self): """ ensure a permited user can delete a new %model object using API """ instance = getattr(self, self.instance_name) instance_id = instance.id event_id = self.event1.id url = reverse('{base_name}-list'.format(base_name=self.base_name), kwargs={'event_pk': event_id}) url = "%s%d/" % (url, instance_id) self.client.force_authenticate(user=self.user1) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(self.model.objects.count(), self.initial_count - 1) # TODO rajouter la gestion des permissions dans le Mixin # TODO rajouter un test pour s'assurer que les personnes non # connectées ne peuvent pas create/update/delete class EventSpecificTestMixin(object): """ Tests is the EventSpecifics querysets are rendered correctly using the API Note : need to define : `model`: the concerned model serve by the API `root_base_name`: the base_name used in the root-level urls `event_base_name`: the base_name used in the event-level urls tests for models served by the API that inherit EventSpecificMixin """ @classmethod def setUpTestData(cls): # Users cls.user1_data = {'username': "user1", 'password': "pass1"} cls.user1 = User.objects.create_user(**cls.user1_data) # Events cls.event1_data = {'title': "event1", 'slug': "slug1", 'beginning_date': timezone.now() + timedelta(days=30), 'ending_date': timezone.now()+timedelta(days=31), 'created_by': cls.user1, } cls.event1 = Event.objects.create(**cls.event1_data) def setUp(self): # Tag self.tag_root_data = {"name": "tag2", "is_public": False, "color": "#222"} self.tag_event_data = {"name": "tag3", "is_public": False, "color": "#333", 'event': self.event1} self.tag_root = ActivityTag.objects.create(**self.tag_root_data) self.tag_event = ActivityTag.objects.create(**self.tag_event_data) # Places self.place_root_data = {'name': "place1", 'event': None, } self.place_event_data = {'name': "place2", 'event': self.event1, } self.place_root = Place.objects.create(**self.place_root_data) self.place_event = Place.objects.create(**self.place_event_data) def test_lists(self): """ ensure that only root-level models are served under api/%root_base_name/ and that root-level and event-level models are served under api/event//%event_base_name/ """ event_id = self.event1.id root_count = self.model.objects.filter(event=None).count() event_count = (self.model.objects .filter(Q(event=self.event1) | Q(event=None)).count()) self.client.force_authenticate(user=self.user1) url = reverse('{base}-list'.format(base=self.root_base_name)) response = self.client.get(url, format='json') self.assertEqual(response.json()['count'], root_count) event_id = self.event1.id url = reverse('{base}-list'.format(base=self.event_base_name), kwargs={'event_pk': event_id}) response = self.client.get(url, format='json') self.assertEqual(response.json()['count'], event_count) # TODO rajouter la gestion des permissions dans le Mixin # TODO rajouter un test pour s'assurer que les personnes non # connectées ne peuvent pas create/update/delete # TODO? essayer de factoriser avec EventBasedMixin ? # FIXME not working, peut être que le problème vient # du fait que les dates sont mal envoyées dans le data ? A voir. class ModelTestMixin(DataBaseMixin): """ Note : need to define : `model`, `base_name`, `instance_name`, `field_tested`, `serializer` generic mixin for testing creation/update/delete of models served by the API """ def test_user_create(self): """ ensure a permited user can create a new %model object using API """ data = getattr(self, self.data_creation) initial_count = self.model.objects.count() url = reverse('{base_name}-list'.format(base_name=self.base_name)) self.client.force_authenticate(user=self.user1) response = self.client.post(url, data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(self.model.objects.count(), initial_count + 1) instance = self.model.objects.get(id=initial_count+1) for field in self.tested_fields.keys(): self.assertEqual(getattr(instance, field), data[field]) def test_user_update(self): """ ensure a permited user can update a new %model object using API """ instance = getattr(self, self.instance_name) factory = APIRequestFactory() instance_id = instance.id url = reverse('{base_name}-detail'.format(base_name=self.base_name), kwargs={'pk': instance_id}) request = factory.get(url) data = self.serializer(instance, context={'request': request}).data for field, value in self.tested_fields.items(): data[field] = value self.client.force_authenticate(user=self.user1) response = self.client.put(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) instance.refresh_from_db() for field, value in self.tested_fields.items(): self.assertEqual(getattr(instance, field), value) def test_user_delete(self): """ ensure a permited user can delete a new %model object using API """ instance = getattr(self, self.instance_name) initial_count = self.model.objects.count() instance_id = instance.id url = reverse('{base_name}-detail'.format(base_name=self.base_name), kwargs={'pk': instance_id}) self.client.force_authenticate(user=self.user1) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(self.model.objects.count(), initial_count - 1)