Merge branch 'tests'

Added basic test suite for this application
This commit is contained in:
2020-05-21 20:17:02 +02:00
21 changed files with 643 additions and 2 deletions

0
tests/__init__.py Normal file
View File

View File

@@ -0,0 +1 @@
from .base import UploadMixin

32
tests/videodinges/base.py Normal file
View File

@@ -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()

View File

@@ -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

View File

View File

@@ -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',
)

View File

@@ -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')

View File

@@ -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)

View File

@@ -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)

View File

@@ -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')

View File

@@ -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')))

View File

@@ -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)

View File

@@ -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.
"""

View File

@@ -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)

View File

@@ -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')

View File

View File

@@ -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')

View File

@@ -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 = '<source src="{url}" type="{type}" />'
self.assertInHTML(
"""<video width="853" height="480" controls="controls">
<source src="http://480p.mp4" type='video/mp4' />
<source src="http://480p.webm" type='video/webm' />
You need a browser that understands HTML5 video and supports h.264 or vp8 codecs.
</video>""",
content,
)
self.assertInHTML('<title>Vid 1</title>', content)
self.assertInHTML('<h1>Vid 1</h1>', content)
self.assertInHTML('<p>Description 1</p>', content)
self.assertInHTML('<strong>480p versie</strong>', content)
self.assertInHTML(
'<a href="vid-1.html?quality=720p" onclick="vidTimeInUrl(this);">720p versie</a>',
content
)
self.assertInHTML(
'<script src="static/js/video.js" type="text/javascript"></script>',
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(
"""<video width="1280" height="720" controls="controls">
<source src="http://720p.mp4" type='video/mp4' />
<source src="http://720p.webm" type='video/webm' />
You need a browser that understands HTML5 video and supports h.264 or vp8 codecs.
</video>""",
content,
)
self.assertInHTML(
'<a href="vid-1.html?quality=480p" onclick="vidTimeInUrl(this);">480p versie</a>',
content
)
self.assertInHTML('<strong>720p versie</strong>', 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(
"""<video width="1280" height="720" controls="controls">
<source src="http://720p.mp4" type='video/mp4' />
<source src="http://720p.webm" type='video/webm' />
You need a browser that understands HTML5 video and supports h.264 or vp8 codecs.
</video>""",
content,
)
self.assertInHTML(
'<a href="vid-1.html?quality=480p" onclick="vidTimeInUrl(this);">480p versie</a>',
content
)
self.assertInHTML('<strong>720p versie</strong>', 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(
"""<video width="853" height="480" poster="{image}" controls="controls">
<source src="{url}" type='video/webm' />
You need a browser that understands HTML5 video and supports vp8 codecs.
</video>""".format(url=movie.file.url, image=image.file.url),
content,
)
self.assertInHTML(
'<meta property="og:image" content="{image}" />'.format(image=image.file.url),
content,
)

View File

@@ -23,8 +23,8 @@ from . import views
_urlpatterns = [ _urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^$', views.index), url(r'^$', views.index, name='index'),
url(r'^(?P<slug>[\w-]+).html', views.video) url(r'^(?P<slug>[\w-]+).html', views.video, name='video')
] ]
_urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) _urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)