def test_observe_group(self): cmd_id = IdentityService.random() handler = Mock() event = DummyEvent(cmd_id, IdentityService.random()) self.dispatcher.observe_group( { self.cmd_id: { type(self.evt): self.handler }, cmd_id: { type(event): handler }, }, times=1, ) self.dispatcher.dispatch(self.evt) handler.assert_not_called() self.handler.assert_called_once_with(self.evt) handler.reset_mock() self.handler.reset_mock() self.dispatcher.dispatch(event) handler.assert_not_called() self.handler.assert_not_called()
def test_queue_front_playlist_while_playing(self): collection_1_id = IdentityService.random() collection_2_id = IdentityService.random() self.data_producer.player().video( "source1", collection_id=collection_1_id ).video("source2", collection_id=collection_1_id).play( "source1" ).parent_producer().video( "source3", collection_id=collection_2_id ).video( "source4", collection_id=collection_2_id ).populate( self.data_facade ) queue = self.playlist_repo.get(self.queue_id) videos = self.video_repo.list() playlist2 = [ video for video in videos if video.collection_id == collection_2_id ] for video in playlist2: queue.ids = self.service.queue(queue, video.id, front=True) expected = [videos[0].id, videos[2].id, videos[3].id, videos[1].id] self.assertListEqual(expected, queue.ids)
def test_construction(self): collection_id = IdentityService.random() artist_id = (IdentityService.id_artist("artist"), ) album_id = (IdentityService.id_album("album_name"), ) video = Video( IdentityService.random(), "source", collection_id, artist_id, album_id, "title", timedelta(seconds=300), timedelta(), None, "protocol", "thumbnail_url", "/tmp/file", [], "subtitle", ) self.assertEqual("source", video.source) self.assertEqual(collection_id, video.collection_id) self.assertEqual(artist_id, video.artist_id) self.assertEqual(album_id, video.album_id) self.assertEqual("title", video.title) self.assertEqual(300, video.duration) self.assertEqual("/tmp/file", video.location) self.assertEqual([], video.streams) self.assertEqual("subtitle", video.subtitle) self.expect_events(video, Evt.VideoCreated)
def test_add(self): first = IdentityService.random() self.album.add(first) self.assertListEqual([first], self.album.ids) second = IdentityService.random() self.album.add(second) self.assertListEqual([first, second], self.album.ids) self.expect_events(self.album, Evt.AlbumVideosUpdated, Evt.AlbumVideosUpdated)
def test_construction(self): name = "name" ids = [IdentityService.random()] thumbnail = "thumbnail" id = IdentityService.id_artist(name) artist = Artist(id, name, ids, thumbnail) self.expect_events(artist, Evt.ArtistCreated)
def test_create_video(self): source = "source" video_id = IdentityService.id_video(source) collection_id = IdentityService.random() metadata = { "title": "title", "duration": 300, "source_protocol": "http", "artist": "artist", "album": "album", "thumbnail": "thumbnail_url", } self.downloader.download_metadata.return_value = metadata self.deezer.search.return_value = [] attrs = deepcopy(metadata) attrs["artist_id"] = IdentityService.id_artist(attrs.pop("artist")) attrs["album_id"] = IdentityService.id_album(attrs.pop("album")) self.evt_expecter.expect( VideoEvt.VideoCreated, video_id, source, collection_id, **attrs, state=VideoState.CREATED, ).from_(Cmd.CreateVideo, video_id, source, collection_id)
def test_construction(self): name = "name" ids = [IdentityService.random()] thumbnail = "thumbnail" id = IdentityService.id_album(name) album = Album(id, name, ids, thumbnail) self.expect_events(album, Evt.AlbumCreated)
async def test_update(self): req_body = { "name": "test_playlist", "ids": [str(IdentityService.random())] } playlist = self.playlist_repo.get(self.playlist_id) self.expect_and_raise_l([ { "cmd": make_cmd(PlaylistCmd.RenamePlaylist, playlist.id, req_body["name"]), "evt": PlaylistEvt.PlaylistRenamed, "args": { "name": req_body["name"] }, }, { "cmd": make_cmd(PlaylistCmd.UpdatePlaylistContent, playlist.id, req_body["ids"]), "evt": PlaylistEvt.PlaylistContentUpdated, "args": { "ids": req_body["ids"] }, }, ]) resp = await self.client.patch(f"/api/playlists/{playlist.id}", json=req_body) body = await resp.json() self.assertEqual(200, resp.status) self.assertEqual(playlist.to_dict(), body)
def test_retrieving_to_finalizing(self): event = Evt.VideoCreated( IdentityService.random(), *self.video.to_tuple(), IdentityService.id_artist("artist"), IdentityService.id_album("album"), "title", 300, "m3u8", "thumbnail", VideoState.CREATED, ) self.workflow.to_RETRIEVING(event) cmd = self.expect_dispatch( Cmd.RetrieveVideo, self.video.id, settings["downloader.output_directory"] ) def return_video(id): self.assertEqual(self.video.id, id) return VideoModel(self.video.id, self.video.source, source_protocol="m3u8") self.video_repo.get.side_effect = return_video self.raise_event( Evt.VideoRetrieved, cmd.id, self.video.id, "https://url.m3u8", ) self.assertTrue(self.workflow.is_FINALIZING())
async def queue(self, req): source = req.query["url"] video_id = IdentityService.id_video(source) playlist_id = self._player_repo.get_player().queue if self._source_service.is_playlist(source): collection_id = IdentityService.random() self._evt_dispatcher.dispatch( Notification( collection_id, NotifLevel.INFO, "unfolding playlist", {"source": source}, )) loop = asyncio.get_running_loop() with ThreadPoolExecutor() as pool: sources = await loop.run_in_executor( pool, partial(self._source_service.unfold, source)) if not sources: return self._internal_error( "Could not unfold the playlist URL") self._evt_dispatcher.dispatch( Notification( collection_id, NotifLevel.INFO, "downloading playlist", { "source": source, "count": f"{len(sources)} media" }, )) videos = [ Video(IdentityService.id_video(source), source, collection_id) for source in sources ] workflow_id = IdentityService.id_workflow( QueuePlaylistWorkflow, video_id) self._start_workflow( QueuePlaylistWorkflow, workflow_id, self._data_facade, videos, playlist_id, ) return self._no_content() video = Video(video_id, source, collection_id=None) self._start_workflow( QueueVideoWorkflow, video_id, self._data_facade, video, playlist_id, queue_front=False, ) return self._no_content()
def test_empty(self): self.assertTrue(self.artist.empty()) self.artist = Artist( IdentityService.id_artist("name"), "name", [IdentityService.random()], "thumbnail", ) self.assertFalse(self.artist.empty())
def test_observe_event_by_id(self): self.dispatcher.observe_result(self.cmd_id, {type(self.evt): self.handler}) # Unrelated event should not call the handler self.dispatcher.observe_result(IdentityService.random(), {type(self.evt): self.handler}) self.dispatcher.dispatch(self.evt) self.handler.assert_called_once_with(self.evt)
def test_empty(self): self.assertTrue(self.album.empty()) self.album = Album( IdentityService.id_album("name"), "name", [IdentityService.random()], "thumbnail", ) self.assertFalse(self.album.empty())
async def test_update_not_found(self): playlist_id = IdentityService.id_playlist() req_body = { "name": "test_playlist", "ids": [str(IdentityService.random())] } resp = await self.client.patch(f"/api/playlists/{playlist_id}", json=req_body) self.assertEqual(404, resp.status)
def make_test_workflow(self, video_count=2): collection_id = IdentityService.random() sources = [f"src{i}" for i in range(video_count)] videos = [ Video(IdentityService.id_video(source), source, collection_id) for source in sources ] return self.make_workflow(StreamPlaylistWorkflow, videos, self.player_playlist_id)
def test_download_video_error(self): self.ydl.download.side_effect = RuntimeError("error") op_id = IdentityService.random() video_id = IdentityService.id_video("url") self.downloader.download_video(op_id, video_id, "url", "/tmp/media.mp4") self.dispatcher.dispatch.assert_called_with( DownloadError(op_id, "error"))
def test_parsing_to_deleting(self): event = Evt.VideoRetrieved( IdentityService.random(), self.video.id, f"{settings['downloader.output_directory']}/video.mp4", ) self.workflow.to_PARSING(event) cmd = self.expect_dispatch(Cmd.ParseVideo, self.video.id) self.raise_error(cmd) self.assertTrue(self.workflow.is_DELETING())
def test_download_video(self, path_cls): path_mock = path_cls.return_value path_mock.exists.return_value = True op_id = IdentityService.random() video_id = IdentityService.id_video("url") self.downloader.download_video(op_id, video_id, "url", "/tmp/media.mp4") self.dispatcher.dispatch.assert_called_with(DownloadSuccess(op_id))
def test_download_video_missing(self, path_cls): path_mock = path_cls.return_value path_mock.exists.return_value = False op_id = IdentityService.random() video_id = IdentityService.id_video("url") self.downloader.download_video(op_id, video_id, "url", "/tmp/media.mp4") self.dispatcher.dispatch.assert_called_with( DownloadError(op_id, "video path points to non existent file"))
async def test_update_validation_error(self): req_body = {"name": 2, "ids": [str(IdentityService.random())]} playlist = self.playlist_repo.get(self.playlist_id) resp = await self.client.patch(f"/api/playlists/{playlist.id}", json=req_body) body = await resp.json() self.assertEqual(422, resp.status) self.assertEqual( {"name": ["Not a valid string."]}, body, )
def test_deleting_to_aborted(self): cmd = Cmd.CreateVideo( IdentityService.random(), self.video.id, "source", collection_id=None ) error = OperationError(cmd, "") self.workflow.to_DELETING(error) cmd = self.expect_dispatch(Cmd.DeleteVideo, self.video.id) self.raise_event( Evt.VideoDeleted, cmd.id, self.video.id, ) self.assertTrue(self.workflow.is_ABORTED())
def test_queue_front_playlist_empty_queue(self): collection_id = IdentityService.random() self.data_producer.video("source1", collection_id=collection_id).video( "source2", collection_id=collection_id ).video("source3", collection_id=collection_id).populate(self.data_facade) queue = self.playlist_repo.get(self.queue_id) videos = self.video_repo.list() for video in videos: queue.ids = self.service.queue(queue, video.id, front=True) expected = [video.id for video in videos] self.assertListEqual(expected, queue.ids)
def from_event(self, evt_cls, model_id, *args, **kwargs): cmd_id = IdentityService.random() for cls, handler_data in self.evt_to_handler.items(): self.evt_dispatcher.observe_result(cmd_id, {cls: handler_data.functor}) evt = evt_cls(cmd_id, model_id, *args, **kwargs) self.evt_dispatcher.dispatch(evt) for cls, handler_data in self.evt_to_handler.items(): handler_data.functor.assert_called_once_with( cls(cmd_id, *handler_data.attrs, **handler_data.dict_attrs))
def test_queue_front_merge_playlist(self): collection_id = IdentityService.random() self.data_producer.video("source1", collection_id=collection_id).video( "source2" ).video("source3", collection_id=collection_id).populate(self.data_facade) queue = self.playlist_repo.get(self.queue_id) videos = self.video_repo.list() queue.ids = self.service.queue(queue, videos[0].id, front=True) queue.ids = self.service.queue(queue, videos[1].id, front=False) queue.ids = self.service.queue(queue, videos[2].id, front=True) expected = [videos[0].id, videos[2].id, videos[1].id] self.assertListEqual(expected, queue.ids)
def test_list_containing(self): videos = [IdentityService.id_video(f"source{i}") for i in range(4)] artists = [ Artist(IdentityService.random(), "artist_1", videos[:2]), Artist(IdentityService.random(), "artist_2", videos[2:]), Artist( IdentityService.random(), "artist_3", [videos[0], videos[2]], ), ] for artist in artists: self.repo.create(artist) result = self.repo.list_containing(videos[0]) self.assertEqual([artists[0], artists[2]], result) result = self.repo.list_containing(videos[3]) self.assertEqual([artists[1]], result) result = self.repo.list_containing(IdentityService.random()) self.assertEqual([], result)
def test_list_containing(self): videos = [IdentityService.id_video(f"source{i}") for i in range(4)] albums = [ Album(IdentityService.random(), "album_1", videos[:2]), Album(IdentityService.random(), "album_2", videos[2:]), Album( IdentityService.random(), "album_3", [videos[0], videos[2]], ), ] for album in albums: self.repo.create(album) result = self.repo.list_containing(videos[0]) self.assertEqual([albums[0], albums[2]], result) result = self.repo.list_containing(videos[3]) self.assertEqual([albums[1]], result) result = self.repo.list_containing(IdentityService.random()) self.assertEqual([], result)
def test_finalizing_to_completed(self): event = Evt.VideoSubtitleFetched( IdentityService.random(), self.video.id, Path() ) self.workflow.to_FINALIZING(event) cmd = self.expect_dispatch(Cmd.SetVideoReady, self.video.id) self.raise_event( Evt.VideoStateUpdated, cmd.id, self.video.id, VideoState.CREATED, VideoState.READY, timedelta(), None, )
def test_parsing_to_sub_fetching(self): event = Evt.VideoRetrieved( IdentityService.random(), self.video.id, f"{settings['downloader.output_directory']}/video.mp4", ) self.workflow.to_PARSING(event) cmd = self.expect_dispatch(Cmd.ParseVideo, self.video.id) self.raise_event( Evt.VideoParsed, cmd.id, self.video.id, {}, ) self.assertTrue(self.workflow.is_SUB_RETRIEVING())
def test_parsing_to_finalizing(self): settings["subtitle.enabled"] = False event = Evt.VideoRetrieved( IdentityService.random(), self.video.id, f"{settings['downloader.output_directory']}/video.mp4", ) self.workflow.to_PARSING(event) cmd = self.expect_dispatch(Cmd.ParseVideo, self.video.id) self.raise_event( Evt.VideoParsed, cmd.id, self.video.id, {}, ) self.assertTrue(self.workflow.is_FINALIZING())
def test_next_loop_last_album_no_album(self): collection_id = IdentityService.random() self.data_producer.player().video( "source1", collection_id=collection_id, state=VideoState.READY ).video("source2", state=VideoState.READY).video( "source3", state=VideoState.READY ).populate( self.data_facade ) videos = self.video_repo.list() expected = videos[2].id self.assertEqual( expected, self.service.next_video(self.queue_id, videos[2].id, loop_last="album"), )