Commit 595d4f28 by christopher lee

implemented correct identities for PUT

Assigned profile_name to be the identifier for EncodedVideos.
Tests for PUT have been added to check for expected behavior.
Attempting to PUT multiple EncodedVideos with the same profile
will return an error.

Other notes:
-url now accepts dashes
-reduced number of unique constants
-updated api.py tests with HTTP requests
-removed unused repr
parent e793792a
...@@ -13,26 +13,18 @@ class Profile(models.Model): ...@@ -13,26 +13,18 @@ class Profile(models.Model):
profile_name = models.CharField( profile_name = models.CharField(
max_length=50, max_length=50,
unique=True, unique=True,
) )
extension = models.CharField(max_length=10) extension = models.CharField(max_length=10)
width = models.PositiveIntegerField() width = models.PositiveIntegerField()
height = models.PositiveIntegerField() height = models.PositiveIntegerField()
def __repr__(self):
return (
u"Profile(profile_name={0.profile_name})"
).format(self)
def __unicode__(self):
return repr(self)
class Video(models.Model): class Video(models.Model):
""" """
Model for a Video group with the same content. Model for a Video group with the same content.
A video can have multiple formats. This model is the collection of those A video can have multiple formats. This model are the fields that represent
videos with fields that do not change across formats. the collection of those videos that do not change across formats.
""" """
edx_video_id = models.CharField( edx_video_id = models.CharField(
max_length=50, max_length=50,
...@@ -48,14 +40,6 @@ class Video(models.Model): ...@@ -48,14 +40,6 @@ class Video(models.Model):
client_video_id = models.CharField(max_length=255, db_index=True) client_video_id = models.CharField(max_length=255, db_index=True)
duration = models.FloatField(validators=[MinValueValidator(0)]) duration = models.FloatField(validators=[MinValueValidator(0)])
def __repr__(self):
return (
u"Video(client_video_id={0.client_video_id}, duration={0.duration})"
).format(self)
def __unicode__(self):
return repr(self)
class CourseVideos(models.Model): class CourseVideos(models.Model):
""" """
...@@ -83,12 +67,3 @@ class EncodedVideo(models.Model): ...@@ -83,12 +67,3 @@ class EncodedVideo(models.Model):
profile = models.ForeignKey(Profile, related_name="+") profile = models.ForeignKey(Profile, related_name="+")
video = models.ForeignKey(Video, related_name="encoded_videos") video = models.ForeignKey(Video, related_name="encoded_videos")
def __repr__(self):
return (
u"EncodedVideo(video={0.video.client_video_id}, "
u"profile={0.profile.profile_name})"
).format(self)
def __unicode__(self):
return repr(self)
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Serializers for Video Abstraction Layer Serializers for Video Abstraction Layer
""" """
from rest_framework import serializers from rest_framework import serializers
from django.core.exceptions import ValidationError
from edxval.models import Profile, Video, EncodedVideo from edxval.models import Profile, Video, EncodedVideo
...@@ -31,10 +32,44 @@ class EncodedVideoSerializer(serializers.ModelSerializer): ...@@ -31,10 +32,44 @@ class EncodedVideoSerializer(serializers.ModelSerializer):
"profile", "profile",
) )
def get_identity(self, data):
"""
This hook is required for bulk update.
We need to override the default, to use the slug as the identity.
"""
return data.get('profile', None)
class VideoSerializer(serializers.HyperlinkedModelSerializer): class VideoSerializer(serializers.HyperlinkedModelSerializer):
encoded_videos = EncodedVideoSerializer(many=True, allow_add_remove=True) encoded_videos = EncodedVideoSerializer(
many=True,
allow_add_remove=True
)
class Meta: class Meta:
model = Video model = Video
lookup_field = "edx_video_id" lookup_field = "edx_video_id"
def restore_fields(self, data, files):
"""
Converts a dictionary of data into a dictionary of deserialized fields. Also
checks if there are duplicate profile_name(s). If there is, the deserialization
is rejected.
"""
reverted_data = {}
if data is not None and not isinstance(data, dict):
self._errors['non_field_errors'] = ['Invalid data']
return None
profiles = [ev["profile"] for ev in data.get("encoded_videos", [])]
if len(profiles) != len(set(profiles)):
self._errors['non_field_errors'] = ['Invalid data: duplicate profiles']
for field_name, field in self.fields.items():
field.initialize(parent=self, field_name=field_name)
try:
field.field_from_native(data, files, field_name, reverted_data)
except ValidationError as err:
self._errors[field_name] = list(err.messages)
return reverted_data
...@@ -23,7 +23,6 @@ ENCODED_VIDEO_DICT_MOBILE = dict( ...@@ -23,7 +23,6 @@ ENCODED_VIDEO_DICT_MOBILE = dict(
file_size=4545, file_size=4545,
bitrate=6767, bitrate=6767,
) )
ENCODED_VIDEO_DICT_DESKTOP = dict( ENCODED_VIDEO_DICT_DESKTOP = dict(
url="http://www.meowmagic.com", url="http://www.meowmagic.com",
file_size=1212, file_size=1212,
...@@ -38,28 +37,27 @@ VIDEO_DICT_NEGATIVE_DURATION = dict( ...@@ -38,28 +37,27 @@ VIDEO_DICT_NEGATIVE_DURATION = dict(
edx_video_id="thisis12char-thisis7", edx_video_id="thisis12char-thisis7",
encoded_videos=[] encoded_videos=[]
) )
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE = dict(
url="http://www.meowmix.com",
file_size=-25556,
bitrate=9600,
)
ENCODED_VIDEO_DICT_NEGATIVE_BITRATE = dict(
url="http://www.meowmix.com",
file_size=25556,
bitrate=-9600,
)
VIDEO_DICT_BEE_INVALID = dict( VIDEO_DICT_BEE_INVALID = dict(
client_video_id="Barking Bee", client_video_id="Barking Bee",
duration=111.00, duration=111.00,
edx_video_id="wa/sps", edx_video_id="wa/sps",
) )
VIDEO_DICT_INVALID_ID = dict( VIDEO_DICT_INVALID_ID = dict(
client_video_id="SuperSloth", client_video_id="SuperSloth",
duration=42, duration=42,
edx_video_id="sloppy/sloth!!", edx_video_id="sloppy/sloth!!",
encoded_videos=[] encoded_videos=[]
) )
ENCODED_VIDEO_DICT_NEGATIVE_FILESIZE = dict(
url="http://www.meowmix.com",
file_size=-25556,
bitrate=9600,
)
ENCODED_VIDEO_DICT_NEGATIVE_BITRATE = dict(
url="http://www.meowmix.com",
file_size=25556,
bitrate=-9600,
)
""" """
Non-latin/invalid Non-latin/invalid
""" """
...@@ -87,30 +85,43 @@ Fish ...@@ -87,30 +85,43 @@ Fish
VIDEO_DICT_FISH = dict( VIDEO_DICT_FISH = dict(
client_video_id="Shallow Swordfish", client_video_id="Shallow Swordfish",
duration=122.00, duration=122.00,
edx_video_id="supersoaker" edx_video_id="super-soaker"
)
VIDEO_DICT_DIFFERENT_ID_FISH = dict(
client_video_id="Shallow Swordfish",
duration=122.00,
edx_video_id="medium-soaker"
) )
ENCODED_VIDEO_DICT_FISH_MOBILE = dict( ENCODED_VIDEO_DICT_FISH_MOBILE = dict(
url="https://www.swordsingers.com", url="https://www.swordsingers.com",
file_size=9000, file_size=9000,
bitrate=42, bitrate=42,
profile="mobile", profile="mobile",
) )
ENCODED_VIDEO_DICT_FISH_DESKTOP = dict( ENCODED_VIDEO_DICT_FISH_DESKTOP = dict(
url="https://www.swordsplints.com", url="https://www.swordsplints.com",
file_size=1234, file_size=1234,
bitrate=4222, bitrate=4222,
profile="desktop", profile="desktop",
) )
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE = dict(
url="https://www.fishfellow.com",
file_size=1,
bitrate=1,
profile="mobile",
)
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP = dict(
url="https://www.furryfish.com",
file_size=2,
bitrate=2,
profile="desktop",
)
ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE = dict( ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE = dict(
url="https://www.swordsplints.com", url="https://www.swordsplints.com",
file_size=1234, file_size=1234,
bitrate=4222, bitrate=4222,
profile=11, profile="bird"
) )
COMPLETE_SET_FISH = dict( COMPLETE_SET_FISH = dict(
encoded_videos=[ encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE, ENCODED_VIDEO_DICT_FISH_MOBILE,
...@@ -118,7 +129,40 @@ COMPLETE_SET_FISH = dict( ...@@ -118,7 +129,40 @@ COMPLETE_SET_FISH = dict(
], ],
**VIDEO_DICT_FISH **VIDEO_DICT_FISH
) )
COMPLETE_SET_TWO_MOBILE_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE,
ENCODED_VIDEO_DICT_FISH_MOBILE
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_UPDATE_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE,
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE,
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
**VIDEO_DICT_DIFFERENT_ID_FISH
)
COMPLETE_SET_FIRST_HALF_UPDATE_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE,
ENCODED_VIDEO_DICT_FISH_DESKTOP
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_UPDATE_ONLY_DESKTOP_FISH = dict(
encoded_videos=[
ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP
],
**VIDEO_DICT_FISH
)
COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict( COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict(
encoded_videos=[ encoded_videos=[
ENCODED_VIDEO_DICT_FISH_MOBILE, ENCODED_VIDEO_DICT_FISH_MOBILE,
...@@ -126,7 +170,6 @@ COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict( ...@@ -126,7 +170,6 @@ COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH = dict(
], ],
**VIDEO_DICT_FISH **VIDEO_DICT_FISH
) )
COMPLETE_SET_INVALID_VIDEO_FISH = dict( COMPLETE_SET_INVALID_VIDEO_FISH = dict(
client_video_id="Shallow Swordfish", client_video_id="Shallow Swordfish",
duration=122.00, duration=122.00,
...@@ -141,8 +184,6 @@ COMPLETE_SETS_ALL_INVALID = [ ...@@ -141,8 +184,6 @@ COMPLETE_SETS_ALL_INVALID = [
COMPLETE_SET_INVALID_VIDEO_FISH, COMPLETE_SET_INVALID_VIDEO_FISH,
COMPLETE_SET_INVALID_VIDEO_FISH COMPLETE_SET_INVALID_VIDEO_FISH
] ]
""" """
Star Star
""" """
...@@ -157,14 +198,24 @@ ENCODED_VIDEO_DICT_STAR = dict( ...@@ -157,14 +198,24 @@ ENCODED_VIDEO_DICT_STAR = dict(
bitrate=42, bitrate=42,
profile="mobile" profile="mobile"
) )
ENCODED_VIDEO_UPDATE_DICT_STAR = dict(
url="https://www.whatyouare.com",
file_size=9000,
bitrate=42,
profile="mobile"
)
COMPLETE_SET_STAR = dict( COMPLETE_SET_STAR = dict(
encoded_videos=[ encoded_videos=[
ENCODED_VIDEO_DICT_STAR ENCODED_VIDEO_DICT_STAR
], ],
**VIDEO_DICT_STAR **VIDEO_DICT_STAR
) )
COMPLETE_SET_UPDATE_STAR = dict(
encoded_videos=[
ENCODED_VIDEO_UPDATE_DICT_STAR
],
**VIDEO_DICT_STAR
)
COMPLETE_SET_NOT_A_LIST = dict( COMPLETE_SET_NOT_A_LIST = dict(
encoded_videos=dict( encoded_videos=dict(
url="https://www.howIwonder.com", url="https://www.howIwonder.com",
...@@ -174,7 +225,6 @@ COMPLETE_SET_NOT_A_LIST = dict( ...@@ -174,7 +225,6 @@ COMPLETE_SET_NOT_A_LIST = dict(
), ),
**VIDEO_DICT_STAR **VIDEO_DICT_STAR
) )
COMPLETE_SET_EXTRA_VIDEO_FIELD = dict( COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
encoded_videos=[ encoded_videos=[
dict( dict(
...@@ -188,12 +238,13 @@ COMPLETE_SET_EXTRA_VIDEO_FIELD = dict( ...@@ -188,12 +238,13 @@ COMPLETE_SET_EXTRA_VIDEO_FIELD = dict(
**VIDEO_DICT_STAR **VIDEO_DICT_STAR
) )
""" """
Unsorted Other
""" """
VIDEO_DICT_COAT = dict( VIDEO_DICT_ZEBRA = dict(
client_video_id="Callous Coat", client_video_id="Zesty Zebra",
duration=111.00, duration=111.00,
edx_video_id="itchyjacket" edx_video_id="zestttt",
encoded_videos=[]
) )
VIDEO_DICT_ANIMAL = dict( VIDEO_DICT_ANIMAL = dict(
client_video_id="Average Animal", client_video_id="Average Animal",
...@@ -201,48 +252,9 @@ VIDEO_DICT_ANIMAL = dict( ...@@ -201,48 +252,9 @@ VIDEO_DICT_ANIMAL = dict(
edx_video_id="mediocrity", edx_video_id="mediocrity",
encoded_videos=[] encoded_videos=[]
) )
VIDEO_DICT_ZEBRA = dict(
client_video_id="Zesty Zebra",
duration=111.00,
edx_video_id="zestttt",
encoded_videos=[]
)
VIDEO_DICT_UPDATE_ANIMAL = dict( VIDEO_DICT_UPDATE_ANIMAL = dict(
client_video_id="Lolcat", client_video_id="Above Average Animal",
duration=122.00, duration=999.00,
edx_video_id="mediocrity", edx_video_id="mediocrity",
encoded_videos=[]
) )
VIDEO_DICT_CRAYFISH = dict(
client_video_id="Crazy Crayfish",
duration=111.00,
edx_video_id="craycray",
)
VIDEO_DICT_DUPLICATES = [
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH,
VIDEO_DICT_CRAYFISH
]
COMPLETE_SETS = [
COMPLETE_SET_STAR,
COMPLETE_SET_FISH
]
COMPLETE_SETS_ONE_INVALID = [
COMPLETE_SET_STAR,
COMPLETE_SET_INVALID_VIDEO_FISH
]
VIDEO_DICT_SET_OF_THREE = [
VIDEO_DICT_COAT,
VIDEO_DICT_ANIMAL,
VIDEO_DICT_CRAYFISH
]
VIDEO_DICT_INVALID_SET = [
VIDEO_DICT_COAT,
VIDEO_DICT_INVALID_ID,
VIDEO_DICT_BEE_INVALID
]
...@@ -7,6 +7,9 @@ import mock ...@@ -7,6 +7,9 @@ import mock
from django.test import TestCase from django.test import TestCase
from django.db import DatabaseError from django.db import DatabaseError
from django.core.urlresolvers import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from edxval.models import Profile, Video, EncodedVideo from edxval.models import Profile, Video, EncodedVideo
from edxval import api as api from edxval import api as api
...@@ -16,26 +19,23 @@ from edxval.tests import constants ...@@ -16,26 +19,23 @@ from edxval.tests import constants
class GetVideoInfoTest(TestCase): class GetVideoInfoTest(TestCase):
#TODO When upload portion is finished, do not forget to create tests for validating
#TODO regex for models. Currently, objects are created manually and validators
#TODO are not triggered.
def setUp(self): def setUp(self):
""" """
Creates EncodedVideo objects in database Creates EncodedVideo objects in database
""" """
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
Video.objects.create(**constants.VIDEO_DICT_COAT) Video.objects.create(**constants.VIDEO_DICT_FISH)
EncodedVideo.objects.create( EncodedVideo.objects.create(
video=Video.objects.get( video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_COAT.get("edx_video_id") edx_video_id=constants.VIDEO_DICT_FISH.get("edx_video_id")
), ),
profile=Profile.objects.get(profile_name="mobile"), profile=Profile.objects.get(profile_name="mobile"),
**constants.ENCODED_VIDEO_DICT_MOBILE **constants.ENCODED_VIDEO_DICT_MOBILE
) )
EncodedVideo.objects.create( EncodedVideo.objects.create(
video=Video.objects.get( video=Video.objects.get(
edx_video_id=constants.VIDEO_DICT_COAT.get("edx_video_id") edx_video_id=constants.VIDEO_DICT_FISH.get("edx_video_id")
), ),
profile=Profile.objects.get(profile_name="desktop"), profile=Profile.objects.get(profile_name="desktop"),
**constants.ENCODED_VIDEO_DICT_DESKTOP **constants.ENCODED_VIDEO_DICT_DESKTOP
...@@ -45,7 +45,9 @@ class GetVideoInfoTest(TestCase): ...@@ -45,7 +45,9 @@ class GetVideoInfoTest(TestCase):
""" """
Tests for successful video request Tests for successful video request
""" """
self.assertIsNotNone(api.get_video_info(constants.EDX_VIDEO_ID)) self.assertIsNotNone(api.get_video_info(
constants.VIDEO_DICT_FISH.get("edx_video_id"))
)
def test_no_such_video(self): def test_no_such_video(self):
""" """
...@@ -71,7 +73,9 @@ class GetVideoInfoTest(TestCase): ...@@ -71,7 +73,9 @@ class GetVideoInfoTest(TestCase):
""" """
mock_init.side_effect = Exception("Mock error") mock_init.side_effect = Exception("Mock error")
with self.assertRaises(api.ValInternalError): with self.assertRaises(api.ValInternalError):
api.get_video_info(constants.EDX_VIDEO_ID) api.get_video_info(
constants.VIDEO_DICT_FISH.get("edx_video_id")
)
@mock.patch.object(Video.objects, 'get') @mock.patch.object(Video.objects, 'get')
def test_force_database_error(self, mock_get): def test_force_database_error(self, mock_get):
...@@ -80,4 +84,66 @@ class GetVideoInfoTest(TestCase): ...@@ -80,4 +84,66 @@ class GetVideoInfoTest(TestCase):
""" """
mock_get.side_effect = DatabaseError("DatabaseError") mock_get.side_effect = DatabaseError("DatabaseError")
with self.assertRaises(api.ValInternalError): with self.assertRaises(api.ValInternalError):
api.get_video_info(constants.EDX_VIDEO_ID) api.get_video_info(
constants.VIDEO_DICT_FISH.get("edx_video_id")
)
class GetVideoInfoTestWithHttpCalls(APITestCase):
def setUp(self):
"""
Creates EncodedVideo objects in database with HTTP requests.
The tests are similar to the GetVideoInfoTest class. This class
is to tests that we have the same results, using a populated
database via HTTP uploads.
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
url = reverse('video-list')
response = self.client.post(
url, constants.COMPLETE_SET_FISH, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_get_video_found(self):
"""
Tests for successful video request
"""
self.assertIsNotNone(
api.get_video_info(
constants.COMPLETE_SET_FISH.get("edx_video_id")
)
)
def test_get_info_queries_for_two_encoded_video(self):
"""
Tests number of queries for a Video/EncodedVideo(1) pair
"""
with self.assertNumQueries(4):
api.get_video_info(constants.COMPLETE_SET_FISH.get("edx_video_id"))
def test_get_info_queries_for_one_encoded_video(self):
"""
Tests number of queries for a Video/EncodedVideo(1) pair
"""
url = reverse('video-list')
response = self.client.post(
url, constants.COMPLETE_SET_STAR, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(3):
api.get_video_info(constants.COMPLETE_SET_STAR.get("edx_video_id"))
def test_get_info_queries_for_only_video(self):
"""
Tests number of queries for a Video with no Encoded Videopair
"""
url = reverse('video-list')
response = self.client.post(
url, constants.VIDEO_DICT_ZEBRA, format='json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(2):
api.get_video_info(constants.VIDEO_DICT_ZEBRA.get("edx_video_id"))
...@@ -63,13 +63,6 @@ class SerializerTests(TestCase): ...@@ -63,13 +63,6 @@ class SerializerTests(TestCase):
ProfileSerializer ProfileSerializer
) )
def test_non_latin_deserialization(self):
"""
Tests deserialization of non-latin data
"""
#TODO write a test for this when we understand what we want
pass
def test_invalid_edx_video_id(self): def test_invalid_edx_video_id(self):
""" """
Test the Video model regex validation for edx_video_id field Test the Video model regex validation for edx_video_id field
...@@ -84,7 +77,7 @@ class SerializerTests(TestCase): ...@@ -84,7 +77,7 @@ class SerializerTests(TestCase):
""" """
Tests for basic structure of EncodedVideoSetSerializer Tests for basic structure of EncodedVideoSetSerializer
""" """
video = Video.objects.create(**constants.VIDEO_DICT_COAT) video = Video.objects.create(**constants.VIDEO_DICT_FISH)
EncodedVideo.objects.create( EncodedVideo.objects.create(
video=video, video=video,
profile=Profile.objects.get(profile_name="desktop"), profile=Profile.objects.get(profile_name="desktop"),
...@@ -99,4 +92,4 @@ class SerializerTests(TestCase): ...@@ -99,4 +92,4 @@ class SerializerTests(TestCase):
# Check for 2 EncodedVideo entries # Check for 2 EncodedVideo entries
self.assertEqual(len(result.get("encoded_videos")), 2) self.assertEqual(len(result.get("encoded_videos")), 2)
# Check for original Video data # Check for original Video data
self.assertDictContainsSubset(constants.VIDEO_DICT_COAT, result) self.assertDictContainsSubset(constants.VIDEO_DICT_FISH, result)
...@@ -3,7 +3,293 @@ from rest_framework import status ...@@ -3,7 +3,293 @@ from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from edxval.tests import constants from edxval.tests import constants
from edxval.models import Profile from edxval.models import Profile, Video
class VideoDetail(APITestCase):
"""
Tests Retrieve, Update and Destroy requests
"""
def setUp(self):
"""
Used for manually creating profile objects which EncodedVideos require.
"""
Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
"""
Tests for successful PUT requests.
These tests should be returning HTTP_200_OK responses.
"""
def test_update_video(self):
"""
Tests PUTting a single video with no encoded videos.
"""
url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.VIDEO_DICT_ANIMAL.get("edx_video_id")}
)
response = self.client.put(
url,
constants.VIDEO_DICT_UPDATE_ANIMAL,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertNotEqual(
videos[0].duration,
constants.VIDEO_DICT_ANIMAL.get("duration"))
self.assertEqual(
videos[0].duration,
constants.VIDEO_DICT_UPDATE_ANIMAL.get("duration")
)
def test_update_one_encoded_video(self):
"""
Tests PUTting one encoded video.
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_STAR, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_STAR.get("edx_video_id")}
)
response = self.client.put(
url,
constants.COMPLETE_SET_UPDATE_STAR,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 1)
new_url = videos[0].encoded_videos.all()[0].url
self.assertNotEqual(
constants.ENCODED_VIDEO_UPDATE_DICT_STAR,
constants.ENCODED_VIDEO_DICT_STAR.get("url"))
self.assertEqual(
new_url,
constants.ENCODED_VIDEO_UPDATE_DICT_STAR.get("url")
)
def test_update_two_encoded_videos(self):
"""
Tests PUTting two encoded videos and then PUT back.
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")}
)
response = self.client.patch(
path=url,
data=constants.COMPLETE_SET_UPDATE_FISH,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 2)
first_url = videos[0].encoded_videos.all()[0].url
self.assertNotEqual(
constants.ENCODED_VIDEO_DICT_FISH_MOBILE,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url"))
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertNotEqual(
constants.ENCODED_VIDEO_DICT_FISH_DESKTOP,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url"))
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url")
)
response = self.client.put(
path=url,
data=constants.COMPLETE_SET_FISH,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
first_url = videos[0].encoded_videos.all()[0].url
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_FISH_DESKTOP.get("url")
)
def test_update_one_of_two_encoded_videos(self):
"""
Tests PUTting one of two EncodedVideo(s) and then a single EncodedVideo PUT back.
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")}
)
response = self.client.put(
path=url,
data=constants.COMPLETE_SET_FIRST_HALF_UPDATE_FISH,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 2)
first_url = videos[0].encoded_videos.all()[0].url
self.assertNotEqual(
constants.ENCODED_VIDEO_DICT_FISH_MOBILE,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url"))
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_FISH_DESKTOP.get("url")
)
response = self.client.put(
path=url,
data=constants.COMPLETE_SET_UPDATE_ONLY_DESKTOP_FISH,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
first_url = videos[0].encoded_videos.all()[0].url
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url")
)
self.assertEqual(len(videos[0].encoded_videos.all()), 1)
def test_update_invalid_video(self):
"""
Tests PUTting a video with different edx_video_id.
The new edx_video_id is ignored in the VideoDetail view.
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")}
)
response = self.client.put(
url,
constants.COMPLETE_SET_DIFFERENT_ID_UPDATE_FISH,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 2)
first_url = videos[0].encoded_videos.all()[0].url
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url")
)
"""
Tests for bad PUT requests.
These tests should be returning HTTP_400_BAD_REQUEST responses.
"""
def test_update_an_invalid_encoded_videos(self):
"""
Tests PUTting one of two invalid EncodedVideo(s)
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")}
)
response = self.client.put(
path=url,
data=constants.COMPLETE_SET_INVALID_ENCODED_VIDEO_FISH,
format='json'
)
self.assertEqual(
response.data.get("encoded_videos")[1].get("profile")[0],
"Object with profile_name=bird does not exist."
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 2)
first_url = videos[0].encoded_videos.all()[0].url
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_FISH_DESKTOP.get("url")
)
def test_update_duplicate_encoded_video_profiles(self):
"""
Tests PUTting duplicate EncodedVideos for a Video
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(
'video-detail',
kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")}
)
response = self.client.put(
path=url,
data=constants.COMPLETE_SET_TWO_MOBILE_FISH,
format='json'
)
self.assertEqual(
response.data.get("non_field_errors")[0],
"Invalid data: duplicate profiles"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
videos = Video.objects.all()
self.assertEqual(len(videos), 1)
self.assertEqual(len(videos[0].encoded_videos.all()), 2)
first_url = videos[0].encoded_videos.all()[0].url
self.assertEqual(
first_url,
constants.ENCODED_VIDEO_DICT_FISH_MOBILE.get("url")
)
second_url = videos[0].encoded_videos.all()[1].url
self.assertEqual(
second_url,
constants.ENCODED_VIDEO_DICT_FISH_DESKTOP.get("url")
)
class VideoListTest(APITestCase): class VideoListTest(APITestCase):
...@@ -71,6 +357,20 @@ class VideoListTest(APITestCase): ...@@ -71,6 +357,20 @@ class VideoListTest(APITestCase):
response = self.client.post(url, constants.VIDEO_DICT_NON_LATIN_TITLE, format='json') response = self.client.post(url, constants.VIDEO_DICT_NON_LATIN_TITLE, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_post_video_with_duplicate_encoded_videos(self):
"""
Tests POSTing a single video with duplicate EncodedVideos
"""
url = reverse('video-list')
response = self.client.post(url, constants.COMPLETE_SET_TWO_MOBILE_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
video = self.client.get("/edxval/video/").data
self.assertEqual(len(video), 0)
self.assertEqual(
response.data.get("non_field_errors")[0],
"Invalid data: duplicate profiles"
)
""" """
Tests for POSTing invalid data Tests for POSTing invalid data
...@@ -177,25 +477,11 @@ class VideoListTest(APITestCase): ...@@ -177,25 +477,11 @@ class VideoListTest(APITestCase):
url = reverse('video-list') url = reverse('video-list')
with self.assertNumQueries(7): with self.assertNumQueries(7):
self.client.post(url, constants.COMPLETE_SET_STAR, format='json') self.client.post(url, constants.COMPLETE_SET_STAR, format='json')
"""
Tests for GET
"""
def test_get_all_videos(self):
"""
Tests getting all Video objects
"""
url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 2)
def test_queries_for_get(self): def test_queries_for_get(self):
''' """
Tests number of queries when GETting all videos Tests number of queries when GETting all videos
''' """
url = reverse('video-list') url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json') response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
...@@ -211,4 +497,20 @@ class VideoListTest(APITestCase): ...@@ -211,4 +497,20 @@ class VideoListTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(5): with self.assertNumQueries(5):
self.client.get("/edxval/video/").data self.client.get("/edxval/video/").data
"""
Tests for GET
"""
def test_get_all_videos(self):
"""
Tests getting all Video objects
"""
url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
videos = self.client.get("/edxval/video/").data
self.assertEqual(len(videos), 2)
...@@ -9,7 +9,7 @@ admin.autodiscover() ...@@ -9,7 +9,7 @@ admin.autodiscover()
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^edxval/video/$', views.VideoList.as_view(), url(r'^edxval/video/$', views.VideoList.as_view(),
name="video-list"), name="video-list"),
url(r'^edxval/video/(?P<edx_video_id>\w+)', url(r'^edxval/video/(?P<edx_video_id>[-\w]+)',
views.VideoDetail.as_view(), views.VideoDetail.as_view(),
name="video-detail"), name="video-detail"),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
......
from rest_framework import generics from rest_framework import generics
from edxval.models import Video from edxval.models import Video, Profile
from edxval.serializers import ( from edxval.serializers import (
VideoSerializer VideoSerializer,
ProfileSerializer
) )
...@@ -14,6 +15,14 @@ class VideoList(generics.ListCreateAPIView): ...@@ -14,6 +15,14 @@ class VideoList(generics.ListCreateAPIView):
lookup_field = "edx_video_id" lookup_field = "edx_video_id"
serializer_class = VideoSerializer serializer_class = VideoSerializer
class ProfileList(generics.ListCreateAPIView):
"""
GETs or POST video objects
"""
queryset = Profile.objects.all()
lookup_field = "profile_name"
serializer_class = ProfileSerializer
class VideoDetail(generics.RetrieveUpdateDestroyAPIView): class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
......
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url
from rest_framework.urlpatterns import format_suffix_patterns
from edxval import views from edxval import views
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment