Files
edx-platform/cms/djangoapps/contentstore/tests/test_video_utils.py
M. Zulqarnain fa271dafe9 BOM-2369 (C): pyupgrade on contentstore/tests (#26759)
* pyupgrade on contentstore/tests
2021-03-04 14:37:39 +05:00

368 lines
13 KiB
Python

"""
Unit tests for video utils.
"""
from datetime import datetime
from unittest import TestCase
from unittest.mock import patch
import ddt
import pytz
import requests
from django.conf import settings
from django.core.files.uploadedfile import UploadedFile
from django.test.utils import override_settings
from edxval.api import create_profile, create_video, get_course_video_image_url, update_video_image
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.video_utils import (
YOUTUBE_THUMBNAIL_SIZES,
download_youtube_video_thumbnail,
scrape_youtube_thumbnail,
validate_video_image
)
from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file
class ValidateVideoImageTestCase(TestCase):
"""
Tests for `validate_video_image` method.
"""
def test_invalid_image_file_info(self):
"""
Test that when no file information is provided to validate_video_image, it gives proper error message.
"""
error = validate_video_image({})
self.assertEqual(error, 'The image must have name, content type, and size information.')
def test_corrupt_image_file(self):
"""
Test that when corrupt file is provided to validate_video_image, it gives proper error message.
"""
with open(settings.MEDIA_ROOT + '/test-corrupt-image.png', 'w+') as image_file:
uploaded_image_file = UploadedFile(
image_file,
content_type='image/png',
size=settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES']
)
error = validate_video_image(uploaded_image_file)
self.assertEqual(error, 'There is a problem with this image file. Try to upload a different file.')
@ddt.ddt
class ScrapeVideoThumbnailsTestCase(CourseTestCase):
"""
Test cases for scraping video thumbnails from youtube.
"""
def setUp(self):
super().setUp()
course_ids = [str(self.course.id)]
profiles = ['youtube']
created = datetime.now(pytz.utc)
previous_uploads = [
{
'edx_video_id': 'test1',
'client_video_id': 'test1.mp4',
'duration': 42.0,
'status': 'upload',
'courses': course_ids,
'encoded_videos': [],
'created': created
},
{
'edx_video_id': 'test-youtube-video-1',
'client_video_id': 'test-youtube-id.mp4',
'duration': 128.0,
'status': 'file_complete',
'courses': course_ids,
'created': created,
'encoded_videos': [
{
'profile': 'youtube',
'url': '3_yD_cEKoCk',
'file_size': 1600,
'bitrate': 100,
}
],
},
{
'edx_video_id': 'test-youtube-video-2',
'client_video_id': 'test-youtube-id.mp4',
'image': 'image2.jpg',
'duration': 128.0,
'status': 'file_complete',
'courses': course_ids,
'created': created,
'encoded_videos': [
{
'profile': 'youtube',
'url': '3_yD_cEKoCk',
'file_size': 1600,
'bitrate': 100,
}
],
},
]
for profile in profiles:
create_profile(profile)
for video in previous_uploads:
create_video(video)
# Create video images.
with make_image_file() as image_file:
update_video_image(
'test-youtube-video-2', str(self.course.id), image_file, 'image.jpg'
)
def mocked_youtube_thumbnail_response(
self,
mocked_content=None,
error_response=False,
image_width=settings.VIDEO_IMAGE_MIN_WIDTH,
image_height=settings.VIDEO_IMAGE_MIN_HEIGHT
):
"""
Returns a mocked youtube thumbnail response.
"""
image_content = ''
with make_image_file(dimensions=(image_width, image_height), ) as image_file:
image_content = image_file.read()
if mocked_content or error_response:
image_content = mocked_content
mocked_response = requests.Response()
mocked_response.status_code = requests.codes.ok if image_content else requests.codes.not_found # pylint: disable=no-member
mocked_response._content = image_content # pylint: disable=protected-access
mocked_response.headers = {'content-type': 'image/jpeg'}
return mocked_response
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
@patch('requests.get')
@ddt.data(
(
{
'maxresdefault': 'maxresdefault-result-image-content',
'sddefault': 'sddefault-result-image-content',
'hqdefault': 'hqdefault-result-image-content',
'mqdefault': 'mqdefault-result-image-content',
'default': 'default-result-image-content'
},
'maxresdefault-result-image-content'
),
(
{
'maxresdefault': '',
'sddefault': 'sddefault-result-image-content',
'hqdefault': 'hqdefault-result-image-content',
'mqdefault': 'mqdefault-result-image-content',
'default': 'default-result-image-content'
},
'sddefault-result-image-content'
),
(
{
'maxresdefault': '',
'sddefault': '',
'hqdefault': 'hqdefault-result-image-content',
'mqdefault': 'mqdefault-result-image-content',
'default': 'default-result-image-content'
},
'hqdefault-result-image-content'
),
(
{
'maxresdefault': '',
'sddefault': '',
'hqdefault': '',
'mqdefault': 'mqdefault-result-image-content',
'default': 'default-result-image-content'
},
'mqdefault-result-image-content'
),
(
{
'maxresdefault': '',
'sddefault': '',
'hqdefault': '',
'mqdefault': '',
'default': 'default-result-image-content'
},
'default-result-image-content'
),
)
@ddt.unpack
def test_youtube_video_thumbnail_download(
self,
thumbnail_content_data,
expected_thumbnail_content,
mocked_request
):
"""
Test that we get highest resolution video thumbnail available from youtube.
"""
# Mock get youtube thumbnail responses.
def mocked_youtube_thumbnail_responses(resolutions):
"""
Returns a list of mocked responses containing youtube thumbnails.
"""
mocked_responses = []
for resolution in YOUTUBE_THUMBNAIL_SIZES:
mocked_content = resolutions.get(resolution, '')
error_response = False if mocked_content else True # lint-amnesty, pylint: disable=simplifiable-if-expression
mocked_responses.append(self.mocked_youtube_thumbnail_response(mocked_content, error_response))
return mocked_responses
mocked_request.side_effect = mocked_youtube_thumbnail_responses(thumbnail_content_data)
thumbnail_content, thumbnail_content_type = download_youtube_video_thumbnail('test-yt-id')
# Verify that we get the expected thumbnail content.
self.assertEqual(thumbnail_content, expected_thumbnail_content)
self.assertEqual(thumbnail_content_type, 'image/jpeg')
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
@patch('requests.get')
def test_scrape_youtube_thumbnail(self, mocked_request):
"""
Test that youtube thumbnails are correctly scrapped.
"""
course_id = str(self.course.id)
video1_edx_video_id = 'test-youtube-video-1'
video2_edx_video_id = 'test-youtube-video-2'
# Mock get youtube thumbnail responses.
mocked_request.side_effect = [self.mocked_youtube_thumbnail_response()]
# Verify that video1 has no image attached.
video1_image_url = get_course_video_image_url(course_id=course_id, edx_video_id=video1_edx_video_id)
self.assertIsNone(video1_image_url)
# Verify that video2 has already image attached.
video2_image_url = get_course_video_image_url(course_id=course_id, edx_video_id=video2_edx_video_id)
self.assertIsNotNone(video2_image_url)
# Scrape video thumbnails.
scrape_youtube_thumbnail(course_id, video1_edx_video_id, 'test-yt-id')
scrape_youtube_thumbnail(course_id, video2_edx_video_id, 'test-yt-id2')
# Verify that now video1 image is attached.
video1_image_url = get_course_video_image_url(course_id=course_id, edx_video_id=video1_edx_video_id)
self.assertIsNotNone(video1_image_url)
# Also verify that video2's image is not updated.
video2_image_url_latest = get_course_video_image_url(course_id=course_id, edx_video_id=video2_edx_video_id)
self.assertEqual(video2_image_url, video2_image_url_latest)
@ddt.data(
(
100,
100,
False
),
(
640,
360,
True
)
)
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
@patch('cms.djangoapps.contentstore.video_utils.LOGGER')
@patch('requests.get')
@ddt.unpack
def test_scrape_youtube_thumbnail_logging(
self,
image_width,
image_height,
is_success,
mocked_request,
mock_logger
):
"""
Test that we get correct logs in case of failure as well as success.
"""
course_id = str(self.course.id)
video1_edx_video_id = 'test-youtube-video-1'
mocked_request.side_effect = [
self.mocked_youtube_thumbnail_response(
image_width=image_width,
image_height=image_height
)
]
scrape_youtube_thumbnail(course_id, video1_edx_video_id, 'test-yt-id')
if is_success:
mock_logger.info.assert_called_with(
'VIDEOS: Scraping youtube video thumbnail for edx_video_id [%s] in course [%s]',
video1_edx_video_id,
course_id
)
else:
mock_logger.info.assert_called_with(
'VIDEOS: Scraping youtube video thumbnail failed for edx_video_id [%s] in course [%s] with error: %s',
video1_edx_video_id,
course_id,
'This image file must be larger than 2 KB.'
)
@ddt.data(
(
None,
'image/jpeg',
'This image file must be larger than {image_min_size}.'.format(
image_min_size=settings.VIDEO_IMAGE_MIN_FILE_SIZE_KB
)
),
(
b'dummy-content',
None,
'This image file type is not supported. Supported file types are {supported_file_formats}.'.format(
supported_file_formats=list(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.keys())
)
),
(
None,
None,
'This image file type is not supported. Supported file types are {supported_file_formats}.'.format(
supported_file_formats=list(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.keys())
)
),
)
@patch('cms.djangoapps.contentstore.video_utils.LOGGER')
@patch('cms.djangoapps.contentstore.video_utils.download_youtube_video_thumbnail')
@ddt.unpack
def test_no_video_thumbnail_downloaded(
self,
image_content,
image_content_type,
error_message,
mock_download_youtube_thumbnail,
mock_logger
):
"""
Test that when no thumbnail is downloaded, video image is not updated.
"""
mock_download_youtube_thumbnail.return_value = image_content, image_content_type
course_id = str(self.course.id)
video1_edx_video_id = 'test-youtube-video-1'
# Verify that video1 has no image attached.
video1_image_url = get_course_video_image_url(course_id=course_id, edx_video_id=video1_edx_video_id)
self.assertIsNone(video1_image_url)
# Scrape video thumbnail.
scrape_youtube_thumbnail(course_id, video1_edx_video_id, 'test-yt-id')
mock_logger.info.assert_called_with(
'VIDEOS: Scraping youtube video thumbnail failed for edx_video_id [%s] in course [%s] with error: %s',
video1_edx_video_id,
course_id,
error_message
)
# Verify that no image is attached to video1.
video1_image_url = get_course_video_image_url(course_id=course_id, edx_video_id=video1_edx_video_id)
self.assertIsNone(video1_image_url)