def test_api_update_state_video_processing(self): """Setting a video's `upload_state` to processing should not affect its `uploaded_on`.""" video = VideoFactory(id="9eeef843-bc43-4e01-825d-658aa5bca49f") channel_layer = get_channel_layer() async_to_sync(channel_layer.group_add)( VIDEO_ROOM_NAME.format(video_id=str(video.id)), "test_channel") async_to_sync(channel_layer.group_add)( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), "test_channel_admin") data = { "extraParameters": {}, "key": f"{video.pk}/video/{video.pk}/1533686400", "state": "processing", } signature = generate_hash("shared secret", json.dumps(data).encode("utf-8")) response = self.client.post( "/api/update-state", data, content_type="application/json", HTTP_X_MARSHA_SIGNATURE=signature, ) video.refresh_from_db() self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {"success": True}) self.assertEqual(video.uploaded_on, None) self.assertEqual(video.upload_state, "processing") self.assertEqual(video.resolutions, None) message = async_to_sync(channel_layer.receive)("test_channel") self.assertEqual(message["type"], "video_updated") message = async_to_sync(channel_layer.receive)("test_channel_admin") self.assertEqual(message["type"], "video_updated")
def test_api_update_state_video_multiple_secrets(self): """Confirming the failed upload of a video using the any of the existing secrets.""" video = VideoFactory(id="c804e019-c622-4b76-aa43-33f2317bdc7e") channel_layer = get_channel_layer() async_to_sync(channel_layer.group_add)( VIDEO_ROOM_NAME.format(video_id=str(video.id)), "test_channel") async_to_sync(channel_layer.group_add)( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), "test_channel_admin") data = { "extraParameters": {}, "key": f"{video.pk}/video/{video.pk}/1533686400", "state": "error", } signature = generate_hash( random.choice(["previous secret", "current secret"]), json.dumps(data).encode("utf-8"), ) response = self.client.post( "/api/update-state", data, content_type="application/json", HTTP_X_MARSHA_SIGNATURE=signature, ) video.refresh_from_db() self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {"success": True}) self.assertEqual(video.uploaded_on, None) self.assertEqual(video.upload_state, "error") message = async_to_sync(channel_layer.receive)("test_channel") self.assertEqual(message["type"], "video_updated") message = async_to_sync(channel_layer.receive)("test_channel_admin") self.assertEqual(message["type"], "video_updated")
async def test_timed_text_track_update_channel_layer(self): """Message received on timed_text_track_updated event.""" timed_text_track = await self._get_timed_text_track() jwt_token = AccessToken() jwt_token.payload["resource_id"] = str(timed_text_track.video_id) jwt_token.payload["consumer_site"] = str( timed_text_track.video.consumer_site.id) jwt_token.payload["context_id"] = "Maths" jwt_token.payload["roles"] = ["student"] jwt_token.payload["permissions"] = {"can_update": False} jwt_token.payload["user"] = { "id": "444444", } communicator = WebsocketCommunicator( base_application, f"ws/video/{timed_text_track.video_id}/?jwt={jwt_token}", ) connected, _ = await communicator.connect() self.assertTrue(connected) channel_layer = get_channel_layer() await channel_layer.group_send( VIDEO_ROOM_NAME.format(video_id=str(timed_text_track.video_id)), { "type": "timed_text_track_updated", "timed_text_track": await self._get_timed_text_track_serializer_data(timed_text_track), }, ) response = await communicator.receive_from() self.assertEqual( json.loads(response), { "type": "timedtexttracks", "resource": { "active_stamp": None, "id": str(timed_text_track.id), "is_ready_to_show": False, "language": timed_text_track.language, "mode": timed_text_track.mode, "source_url": None, "upload_state": "pending", "url": None, "video": str(timed_text_track.video_id), }, }, )
def test_send_video_to_simple_user(self): """A message containing serialized video is dispatched to the regular group.""" video = VideoFactory() channel_layer = get_channel_layer() async_to_sync(channel_layer.group_add)( VIDEO_ROOM_NAME.format(video_id=str(video.id)), "test_channel") channel_layers_utils.dispatch_video(video, to_admin=False) message = async_to_sync(channel_layer.receive)("test_channel") self.assertEqual(message["type"], "video_updated") self.assertEqual( message["video"], VideoSerializer(video, context={ "is_admin": False }).data)
async def test_thumbnail_update_channel_layer(self): """Message received on thumbnail_updated event.""" thumbnail = await self._get_thumbnail() jwt_token = AccessToken() jwt_token.payload["resource_id"] = str(thumbnail.video_id) communicator = WebsocketCommunicator( base_application, f"ws/video/{thumbnail.video_id}/?jwt={jwt_token}", ) connected, _ = await communicator.connect() self.assertTrue(connected) channel_layer = get_channel_layer() await channel_layer.group_send( VIDEO_ROOM_NAME.format(video_id=str(thumbnail.video_id)), { "type": "thumbnail_updated", "thumbnail": await self._get_thumbnail_serializer_data(thumbnail), }, ) response = await communicator.receive_from() self.assertEqual( json.loads(response), { "type": "thumbnails", "resource": { "active_stamp": None, "id": str(thumbnail.id), "is_ready_to_show": False, "upload_state": "pending", "urls": None, "video": str(thumbnail.video_id), }, }, )
def test_api_update_state_video(self): """Confirming the successful upload of a video using the sole existing secret.""" video = VideoFactory(id="f87b5f26-da60-49f2-9d71-a816e68a207f") channel_layer = get_channel_layer() async_to_sync(channel_layer.group_add)( VIDEO_ROOM_NAME.format(video_id=str(video.id)), "test_channel") async_to_sync(channel_layer.group_add)( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), "test_channel_admin") data = { "extraParameters": { "resolutions": [144, 240, 480] }, "key": f"{video.pk}/video/{video.pk}/1533686400", "state": "ready", } signature = generate_hash("shared secret", json.dumps(data).encode("utf-8")) response = self.client.post( "/api/update-state", data, content_type="application/json", HTTP_X_MARSHA_SIGNATURE=signature, ) video.refresh_from_db() self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {"success": True}) self.assertEqual(video.uploaded_on, datetime(2018, 8, 8, tzinfo=timezone.utc)) self.assertEqual(video.upload_state, "ready") self.assertEqual(video.resolutions, [144, 240, 480]) message = async_to_sync(channel_layer.receive)("test_channel") self.assertEqual(message["type"], "video_updated") message = async_to_sync(channel_layer.receive)("test_channel_admin") self.assertEqual(message["type"], "video_updated")
async def test_video_update_instructor_channel_layer(self): """Admin user should receive message only from the admin channel.""" video = await self._get_video( live_state=RUNNING, live_type=JITSI, live_info={ "medialive": { "input": { "id": "medialive_input_1", "endpoints": [ "https://live_endpoint1", "https://live_endpoint2", ], }, "channel": { "id": "medialive_channel_1" }, }, "mediapackage": { "id": "mediapackage_channel_1", "endpoints": { "hls": { "id": "endpoint1", "url": "https://channel_endpoint1/live.m3u8", }, }, }, }, ) jwt_token = AccessToken() jwt_token.payload["resource_id"] = str(video.id) jwt_token.payload["roles"] = [ random.choice(["instructor", "administrator"]) ] jwt_token.payload["permissions"] = {"can_update": True} communicator = WebsocketCommunicator( base_application, f"ws/video/{video.id}/?jwt={jwt_token}", ) connected, _ = await communicator.connect() self.assertTrue(connected) channel_layer = get_channel_layer() await channel_layer.group_send( VIDEO_ROOM_NAME.format(video_id=str(video.id)), { "type": "video_updated", "video": await self._get_serializer_data(video, {"is_admin": False}), }, ) self.assertTrue(await communicator.receive_nothing()) await channel_layer.group_send( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), { "type": "video_updated", "video": await self._get_serializer_data(video, {"is_admin": True}), }, ) response = await communicator.receive_from() self.assertEqual( json.loads(response), { "type": "videos", "resource": { "active_shared_live_media": None, "active_shared_live_media_page": None, "active_stamp": None, "allow_recording": True, "description": video.description, "estimated_duration": None, "has_chat": True, "has_live_media": True, "has_transcript": False, "id": str(video.id), "is_public": False, "is_ready_to_show": True, "is_scheduled": False, "timed_text_tracks": [], "thumbnail": None, "title": video.title, "upload_state": "pending", "urls": { "manifests": { "hls": "https://channel_endpoint1/live.m3u8" }, "mp4": {}, "thumbnails": {}, }, "show_download": True, "should_use_subtitle_as_transcript": False, "starting_at": None, "playlist": { "id": str(video.playlist.id), "title": video.playlist.title, "lti_id": video.playlist.lti_id, }, "live_info": { "jitsi": { "config_overwrite": {}, "domain": "meet.jit.si", "external_api_url": "https://meet.jit.si/external_api.js", "interface_config_overwrite": {}, }, "medialive": { "input": { "endpoints": [ "https://live_endpoint1", "https://live_endpoint2", ], }, }, }, "live_state": RUNNING, "live_type": JITSI, "xmpp": None, "shared_live_medias": [], }, }, ) await communicator.disconnect()
def test_api_update_state_video_harvested(self): """Video `upload_state` to `harvested` should reset live state and live info.""" video = VideoFactory( id="1c5a998a-5bb9-41ea-836e-22be0cdeb834", upload_state=PENDING, live_state=STOPPED, live_info={ "medialive": { "input": { "id": "medialive_input_1", "endpoints": [ "https://live_endpoint1", "https://live_endpoint2", ], }, "channel": { "id": "medialive_channel_1" }, }, "mediapackage": { "id": "mediapackage_channel_1", "endpoints": { "hls": { "id": "endpoint1", "url": "https://channel_endpoint1/live.m3u8", }, }, }, }, live_type=RAW, ) channel_layer = get_channel_layer() async_to_sync(channel_layer.group_add)( VIDEO_ROOM_NAME.format(video_id=str(video.id)), "test_channel") async_to_sync(channel_layer.group_add)( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), "test_channel_admin") data = { "extraParameters": { "resolutions": [240, 480, 720] }, "key": f"{video.pk}/video/{video.pk}/1533686400", "state": "harvested", } signature = generate_hash("shared secret", json.dumps(data).encode("utf-8")) response = self.client.post( "/api/update-state", data, content_type="application/json", HTTP_X_MARSHA_SIGNATURE=signature, ) video.refresh_from_db() self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {"success": True}) self.assertEqual(video.uploaded_on, datetime(2018, 8, 8, tzinfo=timezone.utc)) self.assertEqual(video.upload_state, HARVESTED) self.assertEqual(video.resolutions, [240, 480, 720]) self.assertIsNone(video.live_state) self.assertIsNone(video.live_info) message = async_to_sync(channel_layer.receive)("test_channel") self.assertEqual(message["type"], "video_updated") message = async_to_sync(channel_layer.receive)("test_channel_admin") self.assertEqual(message["type"], "video_updated")
async def test_video_update_channel_layer(self): """Messages sent on the admin channel should not be received by a regular user.""" video = await self._get_video( live_state=RUNNING, live_type=JITSI, live_info={ "medialive": { "input": { "id": "medialive_input_1", "endpoints": [ "https://live_endpoint1", "https://live_endpoint2", ], }, "channel": { "id": "medialive_channel_1" }, }, "mediapackage": { "id": "mediapackage_channel_1", "endpoints": { "hls": { "id": "endpoint1", "url": "https://channel_endpoint1/live.m3u8", }, }, }, }, ) jwt_token = AccessToken() jwt_token.payload["resource_id"] = str(video.id) jwt_token.payload["consumer_site"] = str(video.consumer_site.id) jwt_token.payload["context_id"] = "Maths" jwt_token.payload["roles"] = ["student"] jwt_token.payload["permissions"] = {"can_update": False} jwt_token.payload["user"] = { "id": "444444", } communicator = WebsocketCommunicator( base_application, f"ws/video/{video.id}/?jwt={jwt_token}", ) connected, _ = await communicator.connect() self.assertTrue(connected) channel_layer = get_channel_layer() await channel_layer.group_send( VIDEO_ROOM_NAME.format(video_id=str(video.id)), { "type": "video_updated", "video": await self._get_serializer_data(video, {"is_admin": False}), }, ) response = await communicator.receive_from() self.assertEqual( json.loads(response), { "type": "videos", "resource": { "active_shared_live_media": None, "active_shared_live_media_page": None, "active_stamp": None, "allow_recording": True, "description": video.description, "estimated_duration": None, "has_chat": True, "has_live_media": True, "has_transcript": False, "id": str(video.id), "is_public": False, "is_ready_to_show": True, "is_recording": False, "is_scheduled": False, "join_mode": "approval", "timed_text_tracks": [], "thumbnail": None, "title": video.title, "upload_state": "pending", "urls": { "manifests": { "hls": "https://channel_endpoint1/live.m3u8" }, "mp4": {}, "thumbnails": {}, }, "show_download": True, "should_use_subtitle_as_transcript": False, "starting_at": None, "participants_asking_to_join": [], "participants_in_discussion": [], "playlist": { "id": str(video.playlist.id), "title": video.playlist.title, "lti_id": video.playlist.lti_id, }, "recording_time": 0, "live_info": {}, "live_state": RUNNING, "live_type": JITSI, "xmpp": None, "shared_live_medias": [], }, }, ) await channel_layer.group_send( VIDEO_ADMIN_ROOM_NAME.format(video_id=str(video.id)), { "type": "video_updated", "video": await self._get_serializer_data(video, {"is_admin": True}), }, ) self.assertTrue(await communicator.receive_nothing()) await communicator.disconnect()