From 8f3acfef9b45808a44253f0a73dd483600563307 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 17:42:04 +0200 Subject: [PATCH 01/28] Test stub --- tests/videodinges/models/test_video.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/videodinges/models/test_video.py diff --git a/tests/videodinges/models/test_video.py b/tests/videodinges/models/test_video.py new file mode 100644 index 0000000..bc98a2f --- /dev/null +++ b/tests/videodinges/models/test_video.py @@ -0,0 +1,15 @@ +from django.test import TestCase +from videodinges.models import Video +from datetime import datetime, date + +class VideoTestCase(TestCase): + def setUp(self): + Video.objects.create(title='Title', slug='slug', description='Description') + + def test_model_is_created(self): + video = Video.objects.get(slug='slug') + self.assertEqual(video.slug, 'slug') + self.assertEqual(video.title, 'Title') + self.assertEqual(video.description, 'Description') + self.assertIsInstance(video.created_at, datetime) + self.assertIsInstance(video.updated_at, str) From 5c89842b92297b6cc99059dab51fab61a479034a Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Wed, 6 May 2020 20:02:20 +0200 Subject: [PATCH 02/28] Some basic model tests --- tests/__init__.py | 0 tests/videodinges/__init__.py | 0 tests/videodinges/models/__init__.py | 0 tests/videodinges/models/test_transcoding.py | 14 ++++++++++++++ tests/videodinges/models/test_upload.py | 15 +++++++++++++++ tests/videodinges/models/test_video.py | 4 ++-- 6 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/videodinges/__init__.py create mode 100644 tests/videodinges/models/__init__.py create mode 100644 tests/videodinges/models/test_transcoding.py create mode 100644 tests/videodinges/models/test_upload.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/__init__.py b/tests/videodinges/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/models/__init__.py b/tests/videodinges/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/models/test_transcoding.py b/tests/videodinges/models/test_transcoding.py new file mode 100644 index 0000000..18d63c4 --- /dev/null +++ b/tests/videodinges/models/test_transcoding.py @@ -0,0 +1,14 @@ +from django.test import TestCase +from videodinges.models import Transcoding, Video, qualities, transcoding_types + +class TranscodingTestCase(TestCase): + def setUp(self): + video = Video.objects.create(title='Title', slug='slug', description='Description') + Transcoding.objects.create(video=video, quality=qualities[0].name, type=str(transcoding_types[0]), url='https://some_url') + + def test_model_is_created(self): + transcoding = Transcoding.objects.all()[0] + self.assertEqual(transcoding.video.slug, 'slug') + self.assertEqual(transcoding.quality, '360p') + self.assertEqual(transcoding.type, 'video/webm') + self.assertEqual(transcoding.url, 'https://some_url') diff --git a/tests/videodinges/models/test_upload.py b/tests/videodinges/models/test_upload.py new file mode 100644 index 0000000..314cce0 --- /dev/null +++ b/tests/videodinges/models/test_upload.py @@ -0,0 +1,15 @@ +from django.test import TestCase +from videodinges.models import Upload +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import override_settings + +import tempfile + +@override_settings(MEDIA_ROOT=tempfile.mkdtemp()) +class UploadTestCase(TestCase): + def setUp(self): + Upload.objects.create(file=SimpleUploadedFile('some_file.txt', b'some contents')) + + def test_model_is_created(self): + upload = Upload.objects.all()[0] + self.assertEqual(upload.file.name, 'some_file.txt') diff --git a/tests/videodinges/models/test_video.py b/tests/videodinges/models/test_video.py index bc98a2f..95b464d 100644 --- a/tests/videodinges/models/test_video.py +++ b/tests/videodinges/models/test_video.py @@ -1,6 +1,6 @@ from django.test import TestCase from videodinges.models import Video -from datetime import datetime, date +from datetime import datetime class VideoTestCase(TestCase): def setUp(self): @@ -12,4 +12,4 @@ class VideoTestCase(TestCase): self.assertEqual(video.title, 'Title') self.assertEqual(video.description, 'Description') self.assertIsInstance(video.created_at, datetime) - self.assertIsInstance(video.updated_at, str) + self.assertIsInstance(video.updated_at, datetime) From eb90bca467b9c881feadd2199d8404f6a8f1af2b Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 17:42:24 +0200 Subject: [PATCH 03/28] Atempt to create model factories --- tests/videodinges/factories.py | 40 +++++++++++++++++++ tests/videodinges/test_factories/__init__.py | 0 .../test_factories/test_transcoding.py | 12 ++++++ .../videodinges/test_factories/test_upload.py | 9 +++++ .../videodinges/test_factories/test_video.py | 13 ++++++ 5 files changed, 74 insertions(+) create mode 100644 tests/videodinges/factories.py create mode 100644 tests/videodinges/test_factories/__init__.py create mode 100644 tests/videodinges/test_factories/test_transcoding.py create mode 100644 tests/videodinges/test_factories/test_upload.py create mode 100644 tests/videodinges/test_factories/test_video.py diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py new file mode 100644 index 0000000..d1c6be6 --- /dev/null +++ b/tests/videodinges/factories.py @@ -0,0 +1,40 @@ +""" Module generating useful models in 1 place """ +import tempfile +from typing import Type, TypeVar + +from django.core.files.uploadedfile import SimpleUploadedFile +import django.db.models +from django.test import override_settings + +from videodinges import models + +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}) + + if model is models.Transcoding: + video = create(models.Video, title='Title', slug='slug', description='Description') + return models.Transcoding.objects.create( + **{ + **dict( + video=video, + quality=models.qualities[0].name, + type=str(models.transcoding_types[0]), + url='https://some_url', + ), + **kwargs + } + ) + + if model is models.Upload: + return _upload(**kwargs) + +@override_settings(MEDIA_ROOT=tempfile.mkdtemp()) +def _upload(**kwargs): + return models.Upload.objects.create(**{**dict(file=SimpleUploadedFile('some_file.txt', b'some contents')), **kwargs}) + + +# TODO fix annoying dict notation to something more gentle. diff --git a/tests/videodinges/test_factories/__init__.py b/tests/videodinges/test_factories/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/test_factories/test_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py new file mode 100644 index 0000000..29661ce --- /dev/null +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -0,0 +1,12 @@ +from django.test import TestCase +from videodinges.models import Transcoding +from tests.videodinges import factories +from datetime import datetime + +class TranscodingTestCase(TestCase): + def test_factory_returns_model(self): + transcoding = factories.create(Transcoding) + self.assertEqual(transcoding.video.slug, 'slug') + self.assertEqual(transcoding.quality, '360p') + self.assertEqual(transcoding.type, 'video/webm') + self.assertEqual(transcoding.url, 'https://some_url') diff --git a/tests/videodinges/test_factories/test_upload.py b/tests/videodinges/test_factories/test_upload.py new file mode 100644 index 0000000..0714b90 --- /dev/null +++ b/tests/videodinges/test_factories/test_upload.py @@ -0,0 +1,9 @@ +from django.test import TestCase + +from tests.videodinges import factories +from videodinges.models import Upload + +class UploadTestCase(TestCase): + def test_model_is_created(self): + upload = factories.create(Upload) + self.assertEqual(upload.file.name, 'some_file.txt') diff --git a/tests/videodinges/test_factories/test_video.py b/tests/videodinges/test_factories/test_video.py new file mode 100644 index 0000000..3252a5e --- /dev/null +++ b/tests/videodinges/test_factories/test_video.py @@ -0,0 +1,13 @@ +from django.test import TestCase +from videodinges.models import Video +from tests.videodinges import factories +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.assertIsInstance(video.created_at, datetime) + self.assertIsInstance(video.updated_at, datetime) From 146df2d3b177a130ef2f6f271882c222cb5faa31 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 17:42:37 +0200 Subject: [PATCH 04/28] test_can_overwrite_kwargs --- .../test_factories/test_transcoding.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/videodinges/test_factories/test_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py index 29661ce..0ddaca6 100644 --- a/tests/videodinges/test_factories/test_transcoding.py +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -1,5 +1,5 @@ from django.test import TestCase -from videodinges.models import Transcoding +from videodinges.models import Transcoding, Video from tests.videodinges import factories from datetime import datetime @@ -10,3 +10,17 @@ class TranscodingTestCase(TestCase): self.assertEqual(transcoding.quality, '360p') self.assertEqual(transcoding.type, 'video/webm') self.assertEqual(transcoding.url, 'https://some_url') + + def test_can_overwrite_kwargs(self): + transcoding = factories.create( + Transcoding, + quality='720p', + type='video/mp4', + url='http://another_url', + video=factories.create(Video, slug='yet-another-video-slug') + ) + + self.assertEqual(transcoding.video.slug, 'yet-another-video-slug') + self.assertEqual(transcoding.quality, '720p') + self.assertEqual(transcoding.type, 'video/mp4') + self.assertEqual(transcoding.url, 'http://another_url') From de2d58356b947c775f75d890a6990ef79c8ce89e Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 7 May 2020 22:49:38 +0200 Subject: [PATCH 05/28] Do not generate uneccessary objects --- tests/videodinges/factories.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index d1c6be6..ba64222 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -16,7 +16,8 @@ def create(model: Type[T], **kwargs) -> T: return models.Video.objects.create(**{**dict(title='Title', slug='slug', description='Description'), **kwargs}) if model is models.Transcoding: - video = create(models.Video, title='Title', slug='slug', description='Description') + video = create(models.Video, title='Title', slug='slug', description='Description') \ + if 'video' not in kwargs else None return models.Transcoding.objects.create( **{ **dict( @@ -34,7 +35,9 @@ def create(model: Type[T], **kwargs) -> T: @override_settings(MEDIA_ROOT=tempfile.mkdtemp()) def _upload(**kwargs): - return models.Upload.objects.create(**{**dict(file=SimpleUploadedFile('some_file.txt', b'some contents')), **kwargs}) + file = SimpleUploadedFile('some_file.txt', b'some contents') \ + if 'file' not in kwargs else None + return models.Upload.objects.create(**{**dict(file=file), **kwargs}) # TODO fix annoying dict notation to something more gentle. From aa4025d1384fd54af91978fddd21ad14c0b1425a Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 7 May 2020 22:49:57 +0200 Subject: [PATCH 06/28] Names for url components so they can be used with reverse() --- videodinges/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/videodinges/urls.py b/videodinges/urls.py index 7f43c21..f93a469 100644 --- a/videodinges/urls.py +++ b/videodinges/urls.py @@ -23,8 +23,8 @@ from . import views _urlpatterns = [ url(r'^admin/', admin.site.urls), - url(r'^$', views.index), - url(r'^(?P[\w-]+).html', views.video) + url(r'^$', views.index, name='index'), + url(r'^(?P[\w-]+).html', views.video, name='video') ] _urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) From 9492e8445b39407d9d8d706473e8293405c32a6b Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 7 May 2020 22:50:45 +0200 Subject: [PATCH 07/28] Stub index view test --- tests/videodinges/views/__init__.py | 0 tests/videodinges/views/test_index.py | 28 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/videodinges/views/__init__.py create mode 100644 tests/videodinges/views/test_index.py diff --git a/tests/videodinges/views/__init__.py b/tests/videodinges/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/views/test_index.py b/tests/videodinges/views/test_index.py new file mode 100644 index 0000000..bf7f6cb --- /dev/null +++ b/tests/videodinges/views/test_index.py @@ -0,0 +1,28 @@ +from unittest.mock import patch, Mock + +from django.http import HttpResponse +from django.test import TestCase, Client +from django.urls import reverse + +from tests.videodinges import factories +from videodinges import models + + +class IndexTestCase(TestCase): + def setUp(self): + self.client = Client() + + #@patch('videodinges.views.render') + def test_index(self): + + #render.return_value = HttpResponse(b'data', status=200) + + video1 = factories.create(models.Video, title='Vid 1', slug='vid-1') + video2 = factories.create(models.Video, title='Vid 2', slug='vid-2') + resp = self.client.get(reverse('index')) + self.assertEqual(resp.status_code, 200) + self.assertContains(resp, 'Vid 1') + self.assertContains(resp, 'vid-1.html') + + self.assertContains(resp, 'Vid 2') + self.assertContains(resp, 'vid-2.html') From b54ef76881085b1c0d8dd6b2f7842c718149638c Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 7 May 2020 22:51:06 +0200 Subject: [PATCH 08/28] Stub video page view test --- tests/videodinges/views/test_video.py | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/videodinges/views/test_video.py diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py new file mode 100644 index 0000000..655d20f --- /dev/null +++ b/tests/videodinges/views/test_video.py @@ -0,0 +1,59 @@ +""" Test video page """ +from django.http import HttpResponse +from django.test import TestCase, Client +from django.urls import reverse + +from tests.videodinges import factories +from videodinges import models + + +class VideoTestCase(TestCase): + def setUp(self): + self.client = Client() + + def test_video(self): + + video = factories.create( + models.Video, + title='Vid 1', + slug='vid-1', + ) + transcoding1 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/webm', + url='http://480p.webm', + ) + transcoding2 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/mp4', + url='http://480p.mp4', + ) + transcoding3 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/webm', + url='http://720p.webm', + ) + transcoding4 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/mp4', + url='http://720p.mp4', + ) + + resp:HttpResponse = self.client.get(reverse('video', args=['vid-1'])) + content:str = resp.content.decode(resp.charset) + # TODO: parse HTML, check for essential elements + self.assertEqual(resp.status_code, 200) + self.assertContains(resp, 'Vid 1') + #self.assertContains(resp, '') + #self.assertRegexpMatches(resp.content) + + #self.assertContains(resp, 'Vid 2') + #self.assertContains(resp, 'vid-2.html') From 56c7e24969d0c43db1bf40784ea6d286fa5ccf1e Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:00:20 +0200 Subject: [PATCH 09/28] Usefull basic view test for video --- tests/videodinges/views/test_video.py | 59 +++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index 655d20f..c47f6e4 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -11,12 +11,13 @@ class VideoTestCase(TestCase): def setUp(self): self.client = Client() - def test_video(self): + def test_video_view_renders_properly(self): video = factories.create( models.Video, title='Vid 1', slug='vid-1', + default_quality='480p', ) transcoding1 = factories.create( models.Transcoding, @@ -48,12 +49,52 @@ class VideoTestCase(TestCase): ) resp:HttpResponse = self.client.get(reverse('video', args=['vid-1'])) - content:str = resp.content.decode(resp.charset) - # TODO: parse HTML, check for essential elements - self.assertEqual(resp.status_code, 200) - self.assertContains(resp, 'Vid 1') - #self.assertContains(resp, '') - #self.assertRegexpMatches(resp.content) - #self.assertContains(resp, 'Vid 2') - #self.assertContains(resp, 'vid-2.html') + self.assertEqual(resp.status_code, 200) + + content:str = resp.content.decode(resp.charset) + + srctag = '' + + self.assertInHTML( + srctag.format(url=transcoding1.url, type=transcoding1.type), + content, + ) + self.assertInHTML( + srctag.format(url=transcoding2.url, type=transcoding2.type), + content + ) + self.assertInHTML( + srctag.format(url=transcoding3.url, type=transcoding3.type), + content, + count=0 + ) + self.assertInHTML( + srctag.format(url=transcoding4.url, type=transcoding4.type), + content, + count=0 + ) + + self.assertInHTML('Vid 1', content) + + self.assertInHTML('

Vid 1

', content) + + self.assertInHTML('

Description

', content) + + self.assertInHTML('480p versie', content) + + self.assertInHTML( + '720p versie', + content + ) + + self.assertIn( + 'You need a browser that understands HTML5 video and supports h.264 or vp8 codecs.', + content + ) + + self.assertInHTML( + '', + content + ) + From 046dc94c7784bd5a2dd56c5c41da3ea88f69e5ed Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:02:45 +0200 Subject: [PATCH 10/28] test_video_show_correct_default_quality --- tests/videodinges/views/test_video.py | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index c47f6e4..4812e87 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -98,3 +98,66 @@ class VideoTestCase(TestCase): content ) + def test_video_show_correct_default_quality(self): + + video = factories.create( + models.Video, + title='Vid 1', + slug='vid-1', + default_quality='720p', + ) + transcoding1 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/webm', + url='http://480p.webm', + ) + transcoding2 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/mp4', + url='http://480p.mp4', + ) + transcoding3 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/webm', + url='http://720p.webm', + ) + transcoding4 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/mp4', + url='http://720p.mp4', + ) + + resp:HttpResponse = self.client.get(reverse('video', args=['vid-1'])) + + self.assertEqual(resp.status_code, 200) + + content:str = resp.content.decode(resp.charset) + + srctag = '' + + self.assertInHTML( + srctag.format(url=transcoding1.url, type=transcoding1.type), + content, + count=0 + ) + self.assertInHTML( + srctag.format(url=transcoding2.url, type=transcoding2.type), + content, + count=0 + ) + self.assertInHTML( + srctag.format(url=transcoding3.url, type=transcoding3.type), + content, + ) + self.assertInHTML( + srctag.format(url=transcoding4.url, type=transcoding4.type), + content, + ) From a58618c9200e0275e707e18ca043ce90547493b7 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:08:00 +0200 Subject: [PATCH 11/28] test_video_shows_correct_quality_for_parameter --- tests/videodinges/views/test_video.py | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index 4812e87..a8db4c8 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -161,3 +161,67 @@ class VideoTestCase(TestCase): srctag.format(url=transcoding4.url, type=transcoding4.type), content, ) + + def test_video_shows_correct_quality_for_parameter(self): + + video = factories.create( + models.Video, + title='Vid 1', + slug='vid-1', + ) + transcoding1 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/webm', + url='http://480p.webm', + ) + transcoding2 = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/mp4', + url='http://480p.mp4', + ) + transcoding3 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/webm', + url='http://720p.webm', + ) + transcoding4 = factories.create( + models.Transcoding, + video=video, + quality='720p', + type='video/mp4', + url='http://720p.mp4', + ) + + resp:HttpResponse = self.client.get( + reverse('video', args=['vid-1']) + '?quality=720p') + + self.assertEqual(resp.status_code, 200) + + content:str = resp.content.decode(resp.charset) + + srctag = '' + + self.assertInHTML( + srctag.format(url=transcoding1.url, type=transcoding1.type), + content, + count=0 + ) + self.assertInHTML( + srctag.format(url=transcoding2.url, type=transcoding2.type), + content, + count=0 + ) + self.assertInHTML( + srctag.format(url=transcoding3.url, type=transcoding3.type), + content, + ) + self.assertInHTML( + srctag.format(url=transcoding4.url, type=transcoding4.type), + content, + ) From 9b732c55d71fbce3372102297056185d23ad88f4 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:09:18 +0200 Subject: [PATCH 12/28] test_video_show_correct_default_quality quality selectors --- tests/videodinges/views/test_video.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index a8db4c8..3bad763 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -162,6 +162,14 @@ class VideoTestCase(TestCase): content, ) + self.assertInHTML( + '480p versie', + content + ) + + self.assertInHTML('720p versie', content) + + def test_video_shows_correct_quality_for_parameter(self): video = factories.create( From f8a3151672d81d7cddf008257b2be0e46b6a1419 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:17:56 +0200 Subject: [PATCH 13/28] Better video tag test --- tests/videodinges/views/test_video.py | 82 +++++++++------------------ 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index 3bad763..defa2cf 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -57,23 +57,13 @@ class VideoTestCase(TestCase): srctag = '' self.assertInHTML( - srctag.format(url=transcoding1.url, type=transcoding1.type), + """""", content, ) - self.assertInHTML( - srctag.format(url=transcoding2.url, type=transcoding2.type), - content - ) - self.assertInHTML( - srctag.format(url=transcoding3.url, type=transcoding3.type), - content, - count=0 - ) - self.assertInHTML( - srctag.format(url=transcoding4.url, type=transcoding4.type), - content, - count=0 - ) self.assertInHTML('Vid 1', content) @@ -88,11 +78,6 @@ class VideoTestCase(TestCase): content ) - self.assertIn( - 'You need a browser that understands HTML5 video and supports h.264 or vp8 codecs.', - content - ) - self.assertInHTML( '', content @@ -141,26 +126,15 @@ class VideoTestCase(TestCase): content:str = resp.content.decode(resp.charset) - srctag = '' + self.assertInHTML( + """""", + content, + ) - self.assertInHTML( - srctag.format(url=transcoding1.url, type=transcoding1.type), - content, - count=0 - ) - self.assertInHTML( - srctag.format(url=transcoding2.url, type=transcoding2.type), - content, - count=0 - ) - self.assertInHTML( - srctag.format(url=transcoding3.url, type=transcoding3.type), - content, - ) - self.assertInHTML( - srctag.format(url=transcoding4.url, type=transcoding4.type), - content, - ) self.assertInHTML( '480p versie', @@ -213,23 +187,19 @@ class VideoTestCase(TestCase): content:str = resp.content.decode(resp.charset) - srctag = '' + self.assertInHTML( + """""", + content, + ) + self.assertInHTML( - srctag.format(url=transcoding1.url, type=transcoding1.type), - content, - count=0 - ) - self.assertInHTML( - srctag.format(url=transcoding2.url, type=transcoding2.type), - content, - count=0 - ) - self.assertInHTML( - srctag.format(url=transcoding3.url, type=transcoding3.type), - content, - ) - self.assertInHTML( - srctag.format(url=transcoding4.url, type=transcoding4.type), - content, + '480p versie', + content ) + + self.assertInHTML('720p versie', content) From 8ace8d3312e4e42283f66e09ca8c110afcc99bc1 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Fri, 8 May 2020 22:32:08 +0200 Subject: [PATCH 14/28] test_video_uploads_shows_correctly --- tests/videodinges/factories.py | 19 ++++++------ tests/videodinges/views/test_video.py | 42 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index ba64222..32c441b 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -18,17 +18,16 @@ def create(model: Type[T], **kwargs) -> T: if model is models.Transcoding: video = create(models.Video, title='Title', slug='slug', description='Description') \ if 'video' not in kwargs else None - return models.Transcoding.objects.create( - **{ - **dict( - video=video, - quality=models.qualities[0].name, - type=str(models.transcoding_types[0]), - url='https://some_url', - ), - **kwargs - } + defaults = dict( + video=video, + quality=models.qualities[0].name, + type=str(models.transcoding_types[0]), ) + 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: return _upload(**kwargs) diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index defa2cf..173ff5d 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -203,3 +203,45 @@ class VideoTestCase(TestCase): ) self.assertInHTML('720p versie', content) + + def test_video_uploads_shows_correctly(self): + + image = factories.create(models.Upload) + movie = factories.create(models.Upload) + + video = factories.create( + models.Video, + title='Vid 1', + slug='vid-1', + poster=image, + og_image=image + ) + transcoding = factories.create( + models.Transcoding, + video=video, + quality='480p', + type='video/webm', + upload=movie, + ) + + resp:HttpResponse = self.client.get( + reverse('video', args=['vid-1']) + '?quality=720p') + + self.assertEqual(resp.status_code, 200) + + content:str = resp.content.decode(resp.charset) + + self.assertInHTML( + """""".format(url=movie.file.url, image=image.file.url), + content, + ) + + self.assertInHTML( + ''.format(image=image.file.url), + content, + ) + + From 1141f6d4a6bff973fd6b5dc14c26fbc40cbc4749 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 00:00:18 +0200 Subject: [PATCH 15/28] CreateTranscodingTestCase test constraints on model --- tests/videodinges/models/test_transcoding.py | 49 +++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/videodinges/models/test_transcoding.py b/tests/videodinges/models/test_transcoding.py index 18d63c4..fed63fe 100644 --- a/tests/videodinges/models/test_transcoding.py +++ b/tests/videodinges/models/test_transcoding.py @@ -1,5 +1,10 @@ +from django.db.utils import IntegrityError + from django.test import TestCase -from videodinges.models import Transcoding, Video, qualities, transcoding_types + +from tests.videodinges import factories +from videodinges.models import Transcoding, Video, qualities, transcoding_types, Upload + class TranscodingTestCase(TestCase): def setUp(self): @@ -12,3 +17,45 @@ class TranscodingTestCase(TestCase): self.assertEqual(transcoding.quality, '360p') self.assertEqual(transcoding.type, 'video/webm') self.assertEqual(transcoding.url, 'https://some_url') + + +class CreateTranscodingTestCase(TestCase): + + def test_upload_and_url_cannot_both_be_filled(self): + video = factories.create(Video) + with self.assertRaisesMessage(IntegrityError, 'CHECK constraint failed: upload_and_url_cannot_both_be_filled'): + Transcoding.objects.create( + video=video, + quality=qualities[0].name, + type=str(transcoding_types[0]), + url='https://some_url', + upload=factories.create(Upload) + ) + + def test_either_upload_or_url_must_be_filled(self): + video = factories.create(Video) + + with self.assertRaisesMessage(IntegrityError, 'CHECK constraint failed: upload_or_url_is_filled'): + Transcoding.objects.create( + video=video, + quality=qualities[0].name, + type=str(transcoding_types[0]), + ) + + def test_no_duplicate_qualities_for_same_video_and_type_can_be_created(self): + video = factories.create(Video) + + Transcoding.objects.create( + video=video, + quality=qualities[0].name, + type=str(transcoding_types[0]), + url='https://some_url', + ) + + with self.assertRaisesMessage(IntegrityError, 'UNIQUE constraint failed: transcodings.video_id, transcodings.quality, transcodings.type'): + Transcoding.objects.create( + video=video, + quality=qualities[0].name, + type=str(transcoding_types[0]), + url='https://some_url', + ) From 2cbd8bf49d8b1e5c426a3a73e7f7b2bcfd82ca5d Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 10:41:19 +0200 Subject: [PATCH 16/28] Added unit module --- tests/videodinges/unit/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/videodinges/unit/__init__.py diff --git a/tests/videodinges/unit/__init__.py b/tests/videodinges/unit/__init__.py new file mode 100644 index 0000000..9f57084 --- /dev/null +++ b/tests/videodinges/unit/__init__.py @@ -0,0 +1,4 @@ +""" + Module for storing 'real' _unit_ tests. + Meaning, they should test single small units e.g. methods or functions in an isolated way. +""" From 63ce1078e4961578bc8adf18963fc056fc2c4925 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 10:41:44 +0200 Subject: [PATCH 17/28] test models.get_quality_by_name() --- tests/videodinges/unit/models/__init__.py | 0 .../unit/models/test_get_quality_by_name.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/videodinges/unit/models/__init__.py create mode 100644 tests/videodinges/unit/models/test_get_quality_by_name.py diff --git a/tests/videodinges/unit/models/__init__.py b/tests/videodinges/unit/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/videodinges/unit/models/test_get_quality_by_name.py b/tests/videodinges/unit/models/test_get_quality_by_name.py new file mode 100644 index 0000000..6184035 --- /dev/null +++ b/tests/videodinges/unit/models/test_get_quality_by_name.py @@ -0,0 +1,15 @@ +from django.test import TestCase +from videodinges.models import get_quality_by_name + +class GetQualityByNameTestCase(TestCase): + + def test_returns_quality_if_listed(self): + result = get_quality_by_name('480p') + self.assertEqual(result.name, '480p') + self.assertEqual(result.width, 853) + self.assertEqual(result.height, 480) + self.assertEqual(result.priority, 2) + + def test_returns_none_if_not_listed(self): + result = get_quality_by_name('non-existend') + self.assertIsNone(result) From e1aff0c7a1a4d53ac4b00d167e5257932858a002 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 10:42:06 +0200 Subject: [PATCH 18/28] Test get_short_name_of_transcoding_type() --- .../test_get_short_name_of_transcoding_type.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py diff --git a/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py b/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py new file mode 100644 index 0000000..0b409e5 --- /dev/null +++ b/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py @@ -0,0 +1,12 @@ +from django.test import TestCase +from videodinges.models import TranscodingType, get_short_name_of_transcoding_type + +class GetShortNameOfTranscodingTypeTestCase(TestCase): + + def test_gets_transcoding_by_name(self): + result = get_short_name_of_transcoding_type('video/webm; codecs="vp8, vorbis"') + self.assertEqual(result, 'vp8') + + def test_gets_transcoding_by_transcoding_object(self): + result = get_short_name_of_transcoding_type(TranscodingType(name='Looooong naaaaame', short_name='shrt nm')) + self.assertEqual(result, 'shrt nm') \ No newline at end of file From 40490d6086dc4543d197c30f1b6acc622147d5d3 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 11:21:47 +0200 Subject: [PATCH 19/28] Better upload handling in temporary dirs --- tests/videodinges/__init__.py | 1 + tests/videodinges/base.py | 22 +++++++++++++++++++ tests/videodinges/factories.py | 12 +++------- tests/videodinges/models/test_transcoding.py | 4 ++-- tests/videodinges/models/test_upload.py | 8 +++---- .../videodinges/test_factories/test_upload.py | 4 ++-- tests/videodinges/views/test_video.py | 5 +++-- 7 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 tests/videodinges/base.py diff --git a/tests/videodinges/__init__.py b/tests/videodinges/__init__.py index e69de29..8a0591d 100644 --- a/tests/videodinges/__init__.py +++ b/tests/videodinges/__init__.py @@ -0,0 +1 @@ +from .base import UploadMixin diff --git a/tests/videodinges/base.py b/tests/videodinges/base.py new file mode 100644 index 0000000..9962367 --- /dev/null +++ b/tests/videodinges/base.py @@ -0,0 +1,22 @@ +import tempfile +from unittest import TestCase + +from django.test import override_settings + + +class UploadMixin(TestCase): + base_upload_dir: str + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + cls.base_upload_dir = tempfile.mkdtemp(suffix='-videodinges-tests') + + def setUp(self) -> None: + super().setUp() + media_root = tempfile.mkdtemp(suffix='-' + self.__class__.__name__, dir=self.base_upload_dir) + self.media_root_override_settings = override_settings(MEDIA_ROOT=media_root) + self.media_root_override_settings.enable() + + def tearDown(self) -> None: + self.media_root_override_settings.disable() + super().tearDown() diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 32c441b..a87cafb 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -1,10 +1,8 @@ """ Module generating useful models in 1 place """ -import tempfile from typing import Type, TypeVar from django.core.files.uploadedfile import SimpleUploadedFile import django.db.models -from django.test import override_settings from videodinges import models @@ -30,13 +28,9 @@ def create(model: Type[T], **kwargs) -> T: return models.Transcoding.objects.create(**{**defaults, **kwargs}) if model is models.Upload: - return _upload(**kwargs) - -@override_settings(MEDIA_ROOT=tempfile.mkdtemp()) -def _upload(**kwargs): - file = SimpleUploadedFile('some_file.txt', b'some contents') \ - if 'file' not in kwargs else None - return models.Upload.objects.create(**{**dict(file=file), **kwargs}) + file = SimpleUploadedFile('some_file.txt', b'some contents') \ + if 'file' not in kwargs else None + return models.Upload.objects.create(**{**dict(file=file), **kwargs}) # TODO fix annoying dict notation to something more gentle. diff --git a/tests/videodinges/models/test_transcoding.py b/tests/videodinges/models/test_transcoding.py index fed63fe..9e0fcbc 100644 --- a/tests/videodinges/models/test_transcoding.py +++ b/tests/videodinges/models/test_transcoding.py @@ -2,7 +2,7 @@ from django.db.utils import IntegrityError from django.test import TestCase -from tests.videodinges import factories +from tests.videodinges import factories, UploadMixin from videodinges.models import Transcoding, Video, qualities, transcoding_types, Upload @@ -19,7 +19,7 @@ class TranscodingTestCase(TestCase): self.assertEqual(transcoding.url, 'https://some_url') -class CreateTranscodingTestCase(TestCase): +class CreateTranscodingTestCase(UploadMixin, TestCase): def test_upload_and_url_cannot_both_be_filled(self): video = factories.create(Video) diff --git a/tests/videodinges/models/test_upload.py b/tests/videodinges/models/test_upload.py index 314cce0..400a384 100644 --- a/tests/videodinges/models/test_upload.py +++ b/tests/videodinges/models/test_upload.py @@ -1,13 +1,13 @@ from django.test import TestCase + +from tests.videodinges import UploadMixin from videodinges.models import Upload from django.core.files.uploadedfile import SimpleUploadedFile -from django.test import override_settings -import tempfile -@override_settings(MEDIA_ROOT=tempfile.mkdtemp()) -class UploadTestCase(TestCase): +class UploadTestCase(UploadMixin, TestCase): def setUp(self): + super().setUp() Upload.objects.create(file=SimpleUploadedFile('some_file.txt', b'some contents')) def test_model_is_created(self): diff --git a/tests/videodinges/test_factories/test_upload.py b/tests/videodinges/test_factories/test_upload.py index 0714b90..a53603f 100644 --- a/tests/videodinges/test_factories/test_upload.py +++ b/tests/videodinges/test_factories/test_upload.py @@ -1,9 +1,9 @@ from django.test import TestCase -from tests.videodinges import factories +from tests.videodinges import factories, UploadMixin from videodinges.models import Upload -class UploadTestCase(TestCase): +class UploadTestCase(UploadMixin, TestCase): def test_model_is_created(self): upload = factories.create(Upload) self.assertEqual(upload.file.name, 'some_file.txt') diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py index 173ff5d..684b60a 100644 --- a/tests/videodinges/views/test_video.py +++ b/tests/videodinges/views/test_video.py @@ -3,12 +3,13 @@ from django.http import HttpResponse from django.test import TestCase, Client from django.urls import reverse -from tests.videodinges import factories +from tests.videodinges import factories, UploadMixin from videodinges import models -class VideoTestCase(TestCase): +class VideoTestCase(UploadMixin, TestCase): def setUp(self): + super().setUp() self.client = Client() def test_video_view_renders_properly(self): From 61c178d22258ef9338c6630f461077ce3974e16f Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 11:36:18 +0200 Subject: [PATCH 20/28] Clean temporary files by default --- tests/videodinges/base.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/videodinges/base.py b/tests/videodinges/base.py index 9962367..31c9d44 100644 --- a/tests/videodinges/base.py +++ b/tests/videodinges/base.py @@ -5,18 +5,28 @@ from django.test import override_settings class UploadMixin(TestCase): - base_upload_dir: str + clean_uploads_after_run = True + base_upload_dir: tempfile.TemporaryDirectory + @classmethod def setUpClass(cls) -> None: super().setUpClass() - cls.base_upload_dir = tempfile.mkdtemp(suffix='-videodinges-tests') + cls.base_upload_dir = tempfile.TemporaryDirectory(suffix='-videodinges-tests') def setUp(self) -> None: super().setUp() - media_root = tempfile.mkdtemp(suffix='-' + self.__class__.__name__, dir=self.base_upload_dir) - self.media_root_override_settings = override_settings(MEDIA_ROOT=media_root) + self.media_root = tempfile.TemporaryDirectory(suffix='-' + self.__class__.__name__, dir=self.base_upload_dir.name) + self.media_root_override_settings = override_settings(MEDIA_ROOT=self.media_root.name) self.media_root_override_settings.enable() def tearDown(self) -> None: self.media_root_override_settings.disable() + if self.clean_uploads_after_run: + self.media_root.cleanup() super().tearDown() + + @classmethod + def tearDownClass(cls) -> None: + if cls.clean_uploads_after_run: + cls.base_upload_dir.cleanup() + super().tearDownClass() From 8e6a36212d042d0edf43d6ca17595e061b2b7f39 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Sat, 9 May 2020 14:43:22 +0200 Subject: [PATCH 21/28] Use SimpleTestCase for unit tests --- tests/videodinges/unit/__init__.py | 2 ++ tests/videodinges/unit/models/test_get_quality_by_name.py | 4 ++-- .../unit/models/test_get_short_name_of_transcoding_type.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/videodinges/unit/__init__.py b/tests/videodinges/unit/__init__.py index 9f57084..63d748f 100644 --- a/tests/videodinges/unit/__init__.py +++ b/tests/videodinges/unit/__init__.py @@ -1,4 +1,6 @@ """ Module for storing 'real' _unit_ tests. Meaning, they should test single small units e.g. methods or functions in an isolated way. + + Try to use django.test.SimpleTestCase to prevent unnecessary database setup and improve speed. """ diff --git a/tests/videodinges/unit/models/test_get_quality_by_name.py b/tests/videodinges/unit/models/test_get_quality_by_name.py index 6184035..02061fc 100644 --- a/tests/videodinges/unit/models/test_get_quality_by_name.py +++ b/tests/videodinges/unit/models/test_get_quality_by_name.py @@ -1,7 +1,7 @@ -from django.test import TestCase +from django.test import SimpleTestCase from videodinges.models import get_quality_by_name -class GetQualityByNameTestCase(TestCase): +class GetQualityByNameTestCase(SimpleTestCase): def test_returns_quality_if_listed(self): result = get_quality_by_name('480p') diff --git a/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py b/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py index 0b409e5..fea697d 100644 --- a/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py +++ b/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py @@ -1,7 +1,7 @@ -from django.test import TestCase +from django.test import SimpleTestCase from videodinges.models import TranscodingType, get_short_name_of_transcoding_type -class GetShortNameOfTranscodingTypeTestCase(TestCase): +class GetShortNameOfTranscodingTypeTestCase(SimpleTestCase): def test_gets_transcoding_by_name(self): result = get_short_name_of_transcoding_type('video/webm; codecs="vp8, vorbis"') From 29fb109b6f5ab013d6c3a3239627e63717b5ede2 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 18:05:08 +0200 Subject: [PATCH 22/28] Helper function to improve code --- tests/videodinges/factories.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index a87cafb..93059d2 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -11,7 +11,7 @@ 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='Title', slug='slug', description='Description') if model is models.Transcoding: video = create(models.Video, title='Title', slug='slug', description='Description') \ @@ -25,12 +25,19 @@ def create(model: Type[T], **kwargs) -> T: # only URL if no upload for they are multually exclusive defaults['url'] = 'https://some_url' - return models.Transcoding.objects.create(**{**defaults, **kwargs}) + return _create_with_defaults(models.Transcoding, kwargs, **defaults) 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=file) -# 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. + :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 + """ + return model.objects.create(**{**defaults, **kwargs}) From 8c4ec83993005c775c60b280698d1a8c37a1114b Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 18:36:21 +0200 Subject: [PATCH 23/28] Use lambda for dependencies so they won't be created when dependency is already provided --- tests/videodinges/factories.py | 12 +++++++++--- tests/videodinges/test_factories/test_transcoding.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 93059d2..458bb89 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -14,10 +14,8 @@ def create(model: Type[T], **kwargs) -> T: return _create_with_defaults(models.Video, kwargs, title='Title', slug='slug', description='Description') 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, + video=lambda: create(models.Video), quality=models.qualities[0].name, type=str(models.transcoding_types[0]), ) @@ -36,8 +34,16 @@ def create(model: Type[T], **kwargs) -> T: 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. + :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 """ + + for k, v in defaults.items(): + if callable(v) and not k in kwargs: + defaults[k] = v() + return model.objects.create(**{**defaults, **kwargs}) diff --git a/tests/videodinges/test_factories/test_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py index 0ddaca6..b1f691f 100644 --- a/tests/videodinges/test_factories/test_transcoding.py +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -24,3 +24,14 @@ 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) From 019adcba56e4fe42a3415776bfddf095278d511f Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 18:37:41 +0200 Subject: [PATCH 24/28] Uploads are already 'lazily' executed --- tests/videodinges/factories.py | 4 +--- tests/videodinges/test_factories/test_upload.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 458bb89..4a09346 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -26,9 +26,7 @@ def create(model: Type[T], **kwargs) -> T: return _create_with_defaults(models.Transcoding, kwargs, **defaults) if model is models.Upload: - file = SimpleUploadedFile('some_file.txt', b'some contents') \ - if 'file' not in kwargs else None - return _create_with_defaults(models.Upload, kwargs, file=file) + return _create_with_defaults(models.Upload, kwargs, file=SimpleUploadedFile('some_file.txt', b'some contents')) def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: 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'))) From 2e1c7e7165113819ca8990b740ce86f54f048651 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 18:43:05 +0200 Subject: [PATCH 25/28] Raise NotImplementedError when unknown model is passed --- tests/videodinges/factories.py | 2 ++ tests/videodinges/test_factories/test_create.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/videodinges/test_factories/test_create.py diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 4a09346..d9a7ead 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -28,6 +28,8 @@ def create(model: Type[T], **kwargs) -> T: if model is models.Upload: return _create_with_defaults(models.Upload, kwargs, file=SimpleUploadedFile('some_file.txt', b'some contents')) + raise NotImplementedError('Factory for %s not implemented' % model) + def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: """ 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) + From c6f2bacf0fe83d23b8524e33fd562273122a3867 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 19:08:59 +0200 Subject: [PATCH 26/28] Useful conditions can be put in lambdas and an extra test to asure transcoding with upload is possible --- tests/videodinges/factories.py | 13 +++++----- .../test_factories/test_transcoding.py | 24 ++++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index d9a7ead..3f577b2 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -14,16 +14,17 @@ def create(model: Type[T], **kwargs) -> T: return _create_with_defaults(models.Video, kwargs, title='Title', slug='slug', description='Description') if model is models.Transcoding: - defaults = dict( + 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 _create_with_defaults(models.Transcoding, kwargs, **defaults) if model is models.Upload: return _create_with_defaults(models.Upload, kwargs, file=SimpleUploadedFile('some_file.txt', b'some contents')) diff --git a/tests/videodinges/test_factories/test_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py index b1f691f..4e43b78 100644 --- a/tests/videodinges/test_factories/test_transcoding.py +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -1,7 +1,9 @@ +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): @@ -35,3 +37,19 @@ class TranscodingTestCase(TestCase): ) 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') + From c17b558d142a124e4d3ac18e4136bb92c66c4323 Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 19:34:02 +0200 Subject: [PATCH 27/28] Ability to generate unique fields in factories --- tests/videodinges/factories.py | 23 +++++++++++++-- .../test_factories/test_transcoding.py | 2 +- .../videodinges/test_factories/test_video.py | 29 +++++++++++++++++-- tests/videodinges/views/test_video.py | 2 +- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 3f577b2..3196d43 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,7 +12,11 @@ T = TypeVar('T', bound=django.db.models.Model) def create(model: Type[T], **kwargs) -> T: if model is models.Video: - return _create_with_defaults(models.Video, kwargs, title='Title', slug='slug', description='Description') + 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: def url(): @@ -38,6 +43,9 @@ def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: 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 @@ -45,6 +53,17 @@ def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: for k, v in defaults.items(): if callable(v) and not k in kwargs: - defaults[k] = v() + if len(signature(v).parameters) == 1: + result = v(_next_pk(model)) + else: + result = v() + defaults[k] = result return model.objects.create(**{**defaults, **kwargs}) + + +def _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_transcoding.py b/tests/videodinges/test_factories/test_transcoding.py index 4e43b78..0f86df4 100644 --- a/tests/videodinges/test_factories/test_transcoding.py +++ b/tests/videodinges/test_factories/test_transcoding.py @@ -8,7 +8,7 @@ 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') diff --git a/tests/videodinges/test_factories/test_video.py b/tests/videodinges/test_factories/test_video.py index 3252a5e..ec4b957 100644 --- a/tests/videodinges/test_factories/test_video.py +++ b/tests/videodinges/test_factories/test_video.py @@ -6,8 +6,31 @@ 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) 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('

Vid 1

', content) - self.assertInHTML('

Description

', content) + self.assertInHTML('

Description 1

', content) self.assertInHTML('480p versie', content) From 9f0b6881dd1d2a69234a7e1cf71c2de31cbf0fcb Mon Sep 17 00:00:00 2001 From: Bastiaan Welmers Date: Thu, 21 May 2020 20:00:01 +0200 Subject: [PATCH 28/28] Asure not too many queries are run --- tests/videodinges/factories.py | 12 ++++++++++-- tests/videodinges/test_factories/test_video.py | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py index 3196d43..0867ab2 100644 --- a/tests/videodinges/factories.py +++ b/tests/videodinges/factories.py @@ -51,10 +51,18 @@ def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: :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(model)) + result = v(next_pk()) else: result = v() defaults[k] = result @@ -62,7 +70,7 @@ def _create_with_defaults(model: Type[T], kwargs: dict, **defaults) -> T: return model.objects.create(**{**defaults, **kwargs}) -def _next_pk(model: Type[T]) -> int: +def _query_next_pk(model: Type[T]) -> int: try: return model.objects.order_by('-pk').first().pk + 1 except AttributeError: diff --git a/tests/videodinges/test_factories/test_video.py b/tests/videodinges/test_factories/test_video.py index ec4b957..bc0c19d 100644 --- a/tests/videodinges/test_factories/test_video.py +++ b/tests/videodinges/test_factories/test_video.py @@ -34,3 +34,8 @@ class VideoTestCase(TestCase): 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)