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..8a0591d
--- /dev/null
+++ 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..31c9d44
--- /dev/null
+++ b/tests/videodinges/base.py
@@ -0,0 +1,32 @@
+import tempfile
+from unittest import TestCase
+
+from django.test import override_settings
+
+
+class UploadMixin(TestCase):
+ clean_uploads_after_run = True
+ base_upload_dir: tempfile.TemporaryDirectory
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ super().setUpClass()
+ cls.base_upload_dir = tempfile.TemporaryDirectory(suffix='-videodinges-tests')
+
+ def setUp(self) -> None:
+ super().setUp()
+ 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()
diff --git a/tests/videodinges/factories.py b/tests/videodinges/factories.py
new file mode 100644
index 0000000..0867ab2
--- /dev/null
+++ b/tests/videodinges/factories.py
@@ -0,0 +1,77 @@
+""" Module generating useful models in 1 place """
+from inspect import signature
+from typing import Type, TypeVar
+
+from django.core.files.uploadedfile import SimpleUploadedFile
+import django.db.models
+
+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 _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():
+ # 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 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:
+ """
+ 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/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..9e0fcbc
--- /dev/null
+++ b/tests/videodinges/models/test_transcoding.py
@@ -0,0 +1,61 @@
+from django.db.utils import IntegrityError
+
+from django.test import TestCase
+
+from tests.videodinges import factories, UploadMixin
+from videodinges.models import Transcoding, Video, qualities, transcoding_types, Upload
+
+
+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')
+
+
+class CreateTranscodingTestCase(UploadMixin, 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',
+ )
diff --git a/tests/videodinges/models/test_upload.py b/tests/videodinges/models/test_upload.py
new file mode 100644
index 0000000..400a384
--- /dev/null
+++ b/tests/videodinges/models/test_upload.py
@@ -0,0 +1,15 @@
+from django.test import TestCase
+
+from tests.videodinges import UploadMixin
+from videodinges.models import Upload
+from django.core.files.uploadedfile import SimpleUploadedFile
+
+
+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):
+ 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
new file mode 100644
index 0000000..95b464d
--- /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
+
+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, datetime)
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_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
new file mode 100644
index 0000000..0f86df4
--- /dev/null
+++ b/tests/videodinges/test_factories/test_transcoding.py
@@ -0,0 +1,55 @@
+import os
+
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.test import TestCase
+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-1')
+ 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')
+
+ 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
new file mode 100644
index 0000000..be67a83
--- /dev/null
+++ b/tests/videodinges/test_factories/test_upload.py
@@ -0,0 +1,19 @@
+import os
+
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.test import TestCase
+
+from tests.videodinges import factories, UploadMixin
+from videodinges.models import Upload
+
+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
new file mode 100644
index 0000000..bc0c19d
--- /dev/null
+++ b/tests/videodinges/test_factories/test_video.py
@@ -0,0 +1,41 @@
+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-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/unit/__init__.py b/tests/videodinges/unit/__init__.py
new file mode 100644
index 0000000..63d748f
--- /dev/null
+++ b/tests/videodinges/unit/__init__.py
@@ -0,0 +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/__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..02061fc
--- /dev/null
+++ b/tests/videodinges/unit/models/test_get_quality_by_name.py
@@ -0,0 +1,15 @@
+from django.test import SimpleTestCase
+from videodinges.models import get_quality_by_name
+
+class GetQualityByNameTestCase(SimpleTestCase):
+
+ 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)
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..fea697d
--- /dev/null
+++ b/tests/videodinges/unit/models/test_get_short_name_of_transcoding_type.py
@@ -0,0 +1,12 @@
+from django.test import SimpleTestCase
+from videodinges.models import TranscodingType, get_short_name_of_transcoding_type
+
+class GetShortNameOfTranscodingTypeTestCase(SimpleTestCase):
+
+ 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
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')
diff --git a/tests/videodinges/views/test_video.py b/tests/videodinges/views/test_video.py
new file mode 100644
index 0000000..aef91b8
--- /dev/null
+++ b/tests/videodinges/views/test_video.py
@@ -0,0 +1,248 @@
+""" Test video page """
+from django.http import HttpResponse
+from django.test import TestCase, Client
+from django.urls import reverse
+
+from tests.videodinges import factories, UploadMixin
+from videodinges import models
+
+
+class VideoTestCase(UploadMixin, TestCase):
+ def setUp(self):
+ super().setUp()
+ self.client = Client()
+
+ 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,
+ 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 = '
Description 1
', content) + + self.assertInHTML('480p versie', content) + + self.assertInHTML( + '720p versie', + content + ) + + self.assertInHTML( + '', + 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) + + self.assertInHTML( + """""", + content, + ) + + + self.assertInHTML( + '480p versie', + content + ) + + self.assertInHTML('720p versie', 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) + + self.assertInHTML( + """""", + content, + ) + + + self.assertInHTML( + '480p versie', + content + ) + + 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, + ) + + 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