diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index a87cafb..0867ab2 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -1,4 +1,5 @@ """ Module generating useful models in 1 place """ +from inspect import signature from typing import Type, TypeVar from django.core.files.uploadedfile import SimpleUploadedFile @@ -11,26 +12,66 @@ T = TypeVar('T', bound=django.db.models.Model) def create(model: Type[T], **kwargs) -> T: if model is models.Video: - return models.Video.objects.create(**{**dict(title='Title', slug='slug', description='Description'), **kwargs}) + return _create_with_defaults(models.Video, kwargs, + title=lambda x: 'Title {}'.format(x), + slug=lambda x: 'slug-{}'.format(x), + description=lambda x: 'Description {}'.format(x), + ) if model is models.Transcoding: - video = create(models.Video, title='Title', slug='slug', description='Description') \ - if 'video' not in kwargs else None - defaults = dict( - video=video, + def url(): + # only URL if no upload for they are mutually exclusive + if 'upload' not in kwargs: + return 'https://some_url' + + return _create_with_defaults(models.Transcoding, kwargs, + video=lambda: create(models.Video), quality=models.qualities[0].name, type=str(models.transcoding_types[0]), + url=url, ) - if 'upload' not in kwargs: - # only URL if no upload for they are multually exclusive - defaults['url'] = 'https://some_url' - - return models.Transcoding.objects.create(**{**defaults, **kwargs}) if model is models.Upload: - file = SimpleUploadedFile('some_file.txt', b'some contents') \ - if 'file' not in kwargs else None - return models.Upload.objects.create(**{**dict(file=file), **kwargs}) + return _create_with_defaults(models.Upload, kwargs, file=SimpleUploadedFile('some_file.txt', b'some contents')) + + raise NotImplementedError('Factory for %s not implemented' % model) -# TODO fix annoying dict notation to something more gentle. +def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: + """ + Return created django model instance. + When providing lambda as default item, the result of the lambda will be taken. + The lambda will ONLY be executed when not provided in kwargs. + + When a lambda requires an argument, the primary key of the to be created object + will be provided to that argument. This is useful for generating unique fields. + + :param model: django model to create + :param kwargs: keyword arguments to fill the model + :param defaults: default keyword arguments to use when not mentioned in kwargs + """ + + _next_pk = 0 + def next_pk(): + # Queries next pk only one time during creation + nonlocal _next_pk + if _next_pk == 0: + _next_pk = _query_next_pk(model) + return _next_pk + + for k, v in defaults.items(): + if callable(v) and not k in kwargs: + if len(signature(v).parameters) == 1: + result = v(next_pk()) + else: + result = v() + defaults[k] = result + + return model.objects.create(**{**defaults, **kwargs}) + + +def _query_next_pk(model: Type[T]) -> int: + try: + return model.objects.order_by('-pk').first().pk + 1 + except AttributeError: + return 1 diff --git a/tests/videodinges/test_factories/test_create.py b/tests/videodinges/test_factories/test_create.py new file mode 100644 index 0000000..53b9ace --- /dev/null +++ b/tests/videodinges/test_factories/test_create.py @@ -0,0 +1,16 @@ +from django.db import models + +from tests.videodinges import factories +from django.test import TestCase + +class CreateTestCase(TestCase): + + def test_factory_returns_model(self): + + class NotImplementedModel(models.Model): + class Meta: + app_label = 'some_test_label' + + with self.assertRaises(NotImplementedError): + factories.create(NotImplementedModel) + diff --git a/tests/videodinges/test_factories/test_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py index 0ddaca6..0f86df4 100644 --- a/tests/videodinges/test_factories/test_transcoding.py +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -1,12 +1,14 @@ +import os + +from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase -from videodinges.models import Transcoding, Video -from tests.videodinges import factories -from datetime import datetime +from videodinges.models import Transcoding, Video, Upload +from tests.videodinges import factories, UploadMixin class TranscodingTestCase(TestCase): def test_factory_returns_model(self): transcoding = factories.create(Transcoding) - self.assertEqual(transcoding.video.slug, 'slug') + self.assertEqual(transcoding.video.slug, 'slug-1') self.assertEqual(transcoding.quality, '360p') self.assertEqual(transcoding.type, 'video/webm') self.assertEqual(transcoding.url, 'https://some_url') @@ -24,3 +26,30 @@ class TranscodingTestCase(TestCase): self.assertEqual(transcoding.quality, '720p') self.assertEqual(transcoding.type, 'video/mp4') self.assertEqual(transcoding.url, 'http://another_url') + + def test_does_not_create_video_when_providing_one(self): + transcoding = factories.create( + Transcoding, + quality='720p', + type='video/mp4', + url='http://another_url', + video=factories.create(Video, slug='yet-another-video-slug') + ) + + self.assertEquals(Video.objects.all().count(), 1) + + +class TranscodingWithUploadTestCase(UploadMixin, TestCase): + def test_can_assign_upload(self): + transcoding = factories.create( + Transcoding, + quality='720p', + type='video/mp4', + video=factories.create(Video, slug='yet-another-video-slug'), + upload=factories.create(Upload, file=SimpleUploadedFile('my_upload.txt', b'some_contents')) + ) + + self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'my_upload.txt'))) + with open(os.path.join(self.media_root.name, 'my_upload.txt'), 'rb') as f: + self.assertEquals(f.read(), b'some_contents') + diff --git a/tests/videodinges/test_factories/test_upload.py b/tests/videodinges/test_factories/test_upload.py index a53603f..be67a83 100644 --- a/tests/videodinges/test_factories/test_upload.py +++ b/tests/videodinges/test_factories/test_upload.py @@ -1,3 +1,6 @@ +import os + +from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase from tests.videodinges import factories, UploadMixin @@ -7,3 +10,10 @@ class UploadTestCase(UploadMixin, TestCase): def test_model_is_created(self): upload = factories.create(Upload) self.assertEqual(upload.file.name, 'some_file.txt') + self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'some_file.txt'))) + + def test_upload_does_not_create_file_when_providing_upload(self): + upload = factories.create(Upload, file=SimpleUploadedFile('my_file.txt', b'some contents')) + self.assertEqual(upload.file.name, 'my_file.txt') + self.assertFalse(os.path.exists(os.path.join(self.media_root.name, 'some_file.txt'))) + self.assertTrue(os.path.exists(os.path.join(self.media_root.name, 'my_file.txt'))) diff --git a/tests/videodinges/test_factories/test_video.py b/tests/videodinges/test_factories/test_video.py index 3252a5e..bc0c19d 100644 --- a/tests/videodinges/test_factories/test_video.py +++ b/tests/videodinges/test_factories/test_video.py @@ -6,8 +6,36 @@ from datetime import datetime class VideoTestCase(TestCase): def test_factory_returns_model(self): video = factories.create(Video) - self.assertEqual(video.slug, 'slug') - self.assertEqual(video.title, 'Title') - self.assertEqual(video.description, 'Description') + self.assertEqual(video.slug, 'slug-1') + self.assertEqual(video.title, 'Title 1') + self.assertEqual(video.description, 'Description 1') self.assertIsInstance(video.created_at, datetime) self.assertIsInstance(video.updated_at, datetime) + + def test_factory_can_create_multiple_models(self): + video1 = factories.create(Video) + video2 = factories.create(Video) + video3 = factories.create(Video) + + self.assertEqual(video1.slug, 'slug-1') + self.assertEqual(video1.title, 'Title 1') + self.assertEqual(video1.description, 'Description 1') + self.assertIsInstance(video1.created_at, datetime) + self.assertIsInstance(video1.updated_at, datetime) + + self.assertEqual(video2.slug, 'slug-2') + self.assertEqual(video2.title, 'Title 2') + self.assertEqual(video2.description, 'Description 2') + self.assertIsInstance(video2.created_at, datetime) + self.assertIsInstance(video2.updated_at, datetime) + + self.assertEqual(video3.slug, 'slug-3') + self.assertEqual(video3.title, 'Title 3') + self.assertEqual(video3.description, 'Description 3') + self.assertIsInstance(video3.created_at, datetime) + self.assertIsInstance(video3.updated_at, datetime) + + def test_factory_runs_only_2_queries(self): + """ Factory should only use 2 queries: one for selecting primary key, and one for inserting record """ + with self.assertNumQueries(2): + video = factories.create(Video) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index 684b60a..aef91b8 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -70,7 +70,7 @@ class VideoTestCase(UploadMixin, TestCase): self.assertInHTML('
Description
', content) + self.assertInHTML('Description 1
', content) self.assertInHTML('480p versie', content)