Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
fun-apps / tests / test_youtube.py
Size: Mime:
# -*- coding: utf-8 -*-
import json
import mock
import tempfile

from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.utils import override_settings

from courseware.tests.factories import InstructorFactory
from opaque_keys.edx.keys import CourseKey

from fun.tests.utils import skipUnlessCms
from universities.tests.factories import UniversityFactory
from videoproviders.api.youtube import MissingCredentials, Client
from videoproviders.models import YoutubeAuth, YoutubeCourseSettings

from . import fixtures


def mock_list_service(execute_return_value):
    return mock.Mock(return_value=MockResource('list', execute_return_value))
def mock_download_service(execute_return_value):
    return mock.Mock(return_value=MockResource('download', execute_return_value))
def mock_insert_service(execute_return_value):
    return mock.Mock(return_value=MockResource('insert', execute_return_value))

class MockResource(object):
    """
    Mock youtube service class that exposes a 'method_name' method.
    """
    def __init__(self, method_name, execute_return_value):
        setattr(self, method_name, mock.Mock(**{
            'return_value': mock.Mock(**{
                'execute.return_value': execute_return_value
            })
        }))


@mock.patch('apiclient.discovery.build', new=mock.Mock)
class BaseYoutubeTestCase(TestCase):
    """
    Youtube test classes should inherit from this class, or use the same
    decorator to patch the youtube service, so that no real call to the Youtube
    API is ever made.
    """

    def setUp(self):
        self.course_key_string = "org/coursename/run"
        self.youtube_client = Client(self.course_key_string)

    @property
    def course_id(self):
        return CourseKey.from_string(self.course_key_string)

    def create_credentials(self):
        university = UniversityFactory(code="org")
        YoutubeAuth.objects.create(
            university=university,
            client_id="clientid",
            client_secret="clientsecret",
            access_token="accesstoken",
            refresh_token="refreshtoken"
        )

    def create_course_settings(self):
        return YoutubeCourseSettings.objects.create(
            playlist_id="playlistid1",
            course_id=CourseKey.from_string(self.course_key_string)
        )


class YoutubeWithoutCredentialsTests(BaseYoutubeTestCase):

    def test_missing_credentials_in_database(self):
        self.assertRaises(MissingCredentials, getattr, self.youtube_client, "credentials")


class YoutubeTests(BaseYoutubeTestCase):

    def setUp(self):
        super(YoutubeTests, self).setUp()
        self.create_credentials()

    def test_playlist_id_is_found(self):
        playlist_id = "correctplaylistid"
        self.youtube_client.auth.playlists = mock_list_service({
            "items": [
                {
                    "id": "wrongplaylistid",
                    "snippet": {
                        "title": "some title",
                    }
                },
                {
                    "id": playlist_id,
                    "snippet": {
                        "title": self.youtube_client.course_key_string
                    }
                }
            ]
        })

        self.assertEqual(0, YoutubeCourseSettings.objects.count())
        self.assertEqual(playlist_id, self.youtube_client.playlist_id)
        self.assertEqual(playlist_id, YoutubeCourseSettings.objects.get().playlist_id)

    def test_iter_videos_for_empty_course(self):
        self.create_course_settings()
        self.youtube_client.auth.playlistItems = mock_list_service({"items": []})
        videos = list(self.youtube_client.iter_videos())
        self.assertEqual([], videos)

    def test_iter_videos(self):
        settings = self.create_course_settings()
        self.youtube_client.auth.playlistItems = mock_list_service(fixtures.get_json_content(
            "youtube/playlist_items1.json"
        ))
        self.youtube_client.auth.videos = mock_list_service(fixtures.get_json_content(
            "youtube/video1.json"
        ))
        videos = list(self.youtube_client.iter_videos())
        self.youtube_client.auth.playlistItems.return_value.list.assert_called_with(
            playlistId=settings.playlist_id,
            part="id,snippet",
            pageToken=None,
            maxResults=50
        )
        self.assertEqual(1, len(videos))
        self.assertEqual("videoid1", videos[0]["id"])
        self.assertEqual("Video title 1", videos[0]["title"])
        self.assertEqual("4 juillet 2016 13:12", videos[0]["created_at"])

    def test_iter_video_encoding_in_progress(self):
        self.create_course_settings()
        self.youtube_client.auth.playlistItems = mock_list_service(fixtures.get_json_content(
            "youtube/playlist_items1.json"
        ))
        self.youtube_client.auth.videos = mock_list_service(fixtures.get_json_content(
            "youtube/video1_in_progress.json"
        ))
        videos = self.youtube_client.get_videos()
        self.assertEqual(66.6, videos[0]["encoding_progress"])

    def test_generate_subtitle_name(self):
        self.youtube_client.auth.captions = mock_list_service({
            "items": [
               {
                    "snippet": {
                        "language": "pl",
                        "name": "",
                    }
                },
                {
                    "snippet": {
                        "language": "fr",
                        "name": "",
                    }
                },
                {
                    "snippet": {
                        "language": "fr",
                        "name": "#2",
                    }
                },
                {
                    "snippet": {
                        "language": "en",
                        "name": "#1",
                    }
                },
            ]
        })
        self.assertEqual("", self.youtube_client.find_next_subtitle_name("videoid1", "dk"))
        self.assertEqual("#1", self.youtube_client.find_next_subtitle_name("videoid1", "pl"))
        self.assertEqual("#3", self.youtube_client.find_next_subtitle_name("videoid1", "fr"))
        self.assertEqual("#2", self.youtube_client.find_next_subtitle_name("videoid1", "en"))


@skipUnlessCms
class YoutubeCmsTests(BaseYoutubeTestCase):
    """
    This test case covers the CMS views. Technically, these tests could be
    located in fun/cms, but then it would have to import lots of code from this
    module.
    """
    def setUp(self):
        super(YoutubeCmsTests, self).setUp()
        self.create_credentials()
        self.instructor = InstructorFactory.create(course_key=self.course_id)
        self.client.login(username=self.instructor.username, password="test")

        self.subtitle_id = "subtitleid"
        self.youtube_client.auth.captions = mock_list_service({
            "items": [{
                "id": self.subtitle_id,
                "snippet": {
                    "language": "fr"
                }
            }]
        })
        self.subtitle_download_url = reverse(
            "youtube:download_subtitle",
            kwargs={'subtitle_id': self.subtitle_id, 'course_key_string': self.course_key_string}
        )

    @override_settings(CMS_BASE="yipikaye.com")
    def test_iter_subtitles(self):
        subtitles = list(self.youtube_client.iter_subtitles("somevideoid"))

        self.assertEqual(1, len(subtitles))
        subtitle = subtitles[0]
        expected_url = "//yipikaye.com" + self.subtitle_download_url

        self.assertEqual("subtitleid", subtitle["id"])
        self.assertEqual("fr", subtitle["language"])
        self.assertEqual(expected_url, subtitle["url"])

    def test_download_subtitle(self):
        self.youtube_client.auth.captions = mock_download_service("vtt content here")
        self.assertEqual("vtt content here", self.youtube_client.download_subtitle(self.subtitle_id))

    @mock.patch('videoproviders.api.youtube.Client.download_subtitle')
    def test_download_subtitle_view(self, mock_download_subtitle):
        mock_download_subtitle.return_value = "vtt content here"
        response = self.client.get(self.subtitle_download_url)
        self.assertEqual(200, response.status_code)
        self.assertEqual("vtt content here", response.content)

    def test_reverse_subtitle_url_with_complex_id(self):
        subtitle_id = "VOlQLBC_Vxz1R2fVli6aj0FbSjwYVrSpp212rQRPxkw="
        self.assertIsNotNone(reverse(
            'youtube:download_subtitle',
            kwargs={
                "course_key_string": "org/num/run",
                "subtitle_id": subtitle_id
            }
        ))

    def test_get_upload_url(self):
        upload_url = self.youtube_client.get_upload_url()
        expected_url = reverse("youtube:upload_video", kwargs={'course_key_string': self.course_key_string})
        self.assertEqual(expected_url, upload_url["url"])
        self.assertEqual("path", upload_url["file_parameter_name"])

    def test_upload_video(self):
        self.create_course_settings()
        video_file = tempfile.NamedTemporaryFile(prefix="test_youtube_video", suffix=".mp4")

        # Upload video
        self.youtube_client.auth.videos = mock_insert_service({
            "id": "videoid1"
        })
        self.youtube_client.auth.playlistItems = mock_insert_service({})
        video = self.youtube_client.upload_video(video_file)

        # Create video
        self.youtube_client.auth.videos = mock_list_service(fixtures.get_json_content(
            "youtube/video1.json"
        ))
        created_video = self.youtube_client.create_video(video)

        self.assertEqual({"id": "videoid1"}, video)
        self.assertEqual("videoid1", created_video["id"])

    @mock.patch('videoproviders.api.youtube.Client.upload_video')
    def test_upload_video_view(self, mock_upload_video):
        mock_upload_video.return_value = {"id": "videoid1"}
        video_file = tempfile.NamedTemporaryFile(prefix="test_youtube_video", suffix=".mp4")
        url = reverse("youtube:upload_video", kwargs={"course_key_string": self.course_key_string})

        # This is how a file is uploaded in django tests
        upload_response = self.client.post(url, {
            "name": video_file.name,
            Client.FILE_PARAMETER_NAME: video_file
        })

        self.assertEqual(200, upload_response.status_code)
        self.assertEqual({"id": "videoid1"}, json.loads(upload_response.content))