"""Test for Episode logic tier.""" import copy from datetime import datetime from unittest.mock import ANY from unittest.mock import call from unittest.mock import MagicMock from unittest.mock import patch from oto import status import pytest from podcast import config from podcast.connectors import mysql from podcast.constants import error from podcast.logic import email from podcast.logic import episode from podcast.logic import megaphone from podcast.models import episode as episode_model from podcast.models import episode_replication_status as episode_replication_status_model from podcast.models import insertion_point as insertion_point_model from podcast.models import ows_asset_transcoder as oat from podcast.models import podcast as podcast_model from podcast.models import s3 from podcast.models import transcription as transcription_model from podcast.models.api_base import ApiBase from podcast.utils import api_utils from podcast.utils import signed_urls from podcast.utils.exc import MegaphoneError, OwsError @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, get_assets_mock, mock_megaphone ): """Test create episode.""" podcast_id = 1 result = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) assert result['id'] == 5 assert result['created_by'] == 2 assert result['updated_by'] == 2 assert result['status'] == 'draft' assert result['title'] == 'cow' @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_no_podcast(mock_g, mock_get_feature_flag, mock_current_user): """Test create episode fails if podcast_id doesn't exists.""" podcast_id = 20000 with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, {'draft': True}) assert err.value.status == status.NOT_FOUND assert err.value.message == error.ERROR_PODCAST_NOT_FOUND def test_create_episode_fails_if_read_level_producer(mock_current_read_only_user): """Test create episode fails if user is read only.""" podcast_id = 20000 with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, {'draft': True}) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['log', 'request_context', config]) def test_create_episode_success_show_episodic( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone, podcast_fixture): """Create published episode succes. If show type is episodic and episode type is bonus/full then season number should default to 1. """ podcast_id = 1 data = { 'title': 'new title', 'description': 'new description', 'season_number': 2, 'episode_number': 2, 'episode_type': 'bonus', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } result = episode.create_episode(podcast_id, data) assert result['id'] == 5 assert result['season_number'] == 2 assert result['episode_number'] == 2 @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_success_trailer_show_episodic( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone, podcast_fixture): """Create published episode succes. If show type is episodic and episode type is trailer then season number and episode number should be blank. """ podcast_id = 1 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': 1, 'episode_type': 'trailer', 'trailer_type': 'regular', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } result = episode.create_episode(podcast_id, data) assert result['id'] == 5 assert result['season_number'] is None assert result['episode_number'] is None @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_trailer_fails_if_not_trailer_type( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone, podcast_fixture): """Create published episode failure. If episode type is trailer and trailer_type is null while draft is false, creation fails. """ podcast_id = 2 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': 1, 'episode_type': 'trailer', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, data) assert err.value.status == 400 assert err.value.message == error.ERROR_MISSING_TRAILER_TYPE @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_trailer_success_if_not_trailer_type( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone, podcast_fixture): """Create draft episode success. If episode type is trailer and trailer_type is null while draft is true, creation succeds. """ podcast_id = 2 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': 1, 'episode_type': 'trailer', 'content': 'clean', 'draft': True, 'published_date': '2050-11-26T16:52:01.000Z' } result = episode.create_episode(podcast_id, data) assert result['id'] == 5 assert result['trailer_type'] is None @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_season_number_missing_show_episodic( mock_g, mock_get_feature_flag, mock_current_admin_user): """Create published episode fails. If show type is episodic and episode type is bonus/full then episode number shouldn't be None. """ podcast_id = 1 data = { 'title': 'new title', 'description': 'new description', 'season_number': None, 'episode_number': 2, 'episode_type': 'full', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, data) assert err.value.status == 400 assert err.value.message == error.ERROR_MISSING_SEASON_NUMBER @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_episode_number_missing_show_episodic( mock_g, mock_get_feature_flag, mock_current_admin_user): """Create published episode fails. If show type is episodic and episode type is bonus/full then episode number shouldn't be None. """ podcast_id = 1 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': None, 'episode_type': 'full', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, data) assert err.value.status == 400 assert err.value.message == error.ERROR_MISSING_EPISODE_NUMBER @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_season_number_missing( mock_g, mock_get_feature_flag, mock_current_admin_user): """Create published episode fails. If show type is serial and episode type is full then season number shouldn't be None. """ podcast_id = 2 data = { 'title': 'new title', 'description': 'new description', 'season_number': None, 'episode_number': 2, 'episode_type': 'full', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, data) assert err.value.status == 400 assert err.value.message == error.ERROR_MISSING_SEASON_NUMBER @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_episode_number_missing( mock_g, mock_get_feature_flag, mock_current_admin_user): """Create published episode fails. If show type is serial and episode type is bonus then episode number shouldn't be None. """ podcast_id = 2 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': None, 'episode_type': 'bonus', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, data) assert err.value.status == 400 assert err.value.message == error.ERROR_MISSING_EPISODE_NUMBER @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_success_trailer( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone, podcast_fixture): """Create published episode succes. If show type is serial and episode type is trailer then season number and episode number should be blank. """ podcast_id = 2 data = { 'title': 'new title', 'description': 'new description', 'season_number': 1, 'episode_number': 1, 'episode_type': 'trailer', 'trailer_type': 'regular', 'content': 'clean', 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } result = episode.create_episode(podcast_id, data) assert result['id'] == 5 assert result['season_number'] is None assert result['episode_number'] is None @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_fails_if_podcast_deleted( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, get_assets_mock, mock_megaphone ): """Test update episode failes if podcast is marked as deleted.""" podcast_id = 1 new_episode = episode.create_episode(podcast_id, {'draft': True}) with mysql.pod_db_session() as session: podcast_model.delete_podcast(podcast_id, session) with pytest.raises(OwsError) as err: episode.update_episode( podcast_id, new_episode['id'], {'title': 'new title', 'draft': True}, ) assert err.value.status == status.NOT_FOUND assert err.value.message == error.ERROR_PODCAST_NOT_FOUND def test_update_episode_fails_if_read_only( monkeypatch, mock_current_read_only_user, get_assets_mock, mock_megaphone ): """Test update episode fails if user is read only.""" podcast_id = 2 with pytest.raises(OwsError) as err: episode.update_episode( podcast_id, 3, {'title': 'new title', 'draft': True}, ) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_fails_if_no_access(mock_g, mock_get_feature_flag, mock_current_user): """Test create episode failes if no access.""" podcast_id = 2 with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, {'draft': True}) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_fails_if_no_access( mock_g, mock_get_feature_flag, mock_current_user, get_assets_mock, mock_megaphone): """Test update episode fails if no access.""" podcast_id = 2 with pytest.raises(OwsError) as err: episode.update_episode( podcast_id, 3, {'title': 'new title', 'draft': True}, ) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_success( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, mock_current_admin_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode success.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 2 monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) new_episode = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) episode_data = episode.update_episode( podcast_id, new_episode['id'], {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'}, ) megaphone.call_episode_api.assert_called_with( { 'externalId': episode_data['uuid'], 'guid': episode_data['uuid'], 'title': 'new title', 'explicit': 'yes', 'summary': None, 'episodeNumber': None, 'seasonNumber': None, 'pubdate': None, 'preCount': 0, 'preOffset': 0, 'postCount': 0, 'postOffset': 0, 'expectedAdhash': '', 'insertionPoints': [], 'backgroundImageFileUrl': 'http://url/c.jpeg', 'backgroundAudioFileUrl': 'http://url/b.mp3', 'draft': True, 'retainAdLocations': True, 'episodeType': 'bonus' }, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4720', '1', 2, new_episode['id'] ) @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_success_megaphone_api_not_called( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, mock_current_admin_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode success but episode not sent to megaphone for youtube feed type.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 4 monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) monkeypatch.setattr(megaphone, 'create_or_update_episode', MagicMock()) new_episode = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) episode.update_episode( podcast_id, new_episode['id'], {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'}, ) assert not megaphone.create_or_update_episode.called @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_trailer_type( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, mock_current_admin_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode trailer type when episode type is trailer.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 2 monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) new_episode = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) episode_data = episode.update_episode( podcast_id, new_episode['id'], { 'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'trailer', 'trailer_type': 'regular' }, ) assert episode_data['trailer_type'] == 'regular' @patch('podcast.logic.megaphone.g', spec=['log']) @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('requests.put') @patch('podcast.models.api_episode.ApiEpisode.get') @patch('podcast.models.api_episode.ApiEpisode.create') @patch('owsrequest.request.get') def test_update_episode_megaphone_audio_processing_error( oat_request_mock, mp_post_request_mock, mp_get_request_mock, mp_put_request_mock, mock_g, mock_get_feature_flag, mock_g_log, oat_response_fixture, mock_current_admin_user, monkeypatch, output_cdn_fixture, get_assets_mock, ): """Test update episode success.""" mock_g_log.log.info = MagicMock() oat_request_mock.return_value.json.return_value = oat_response_fixture mp_post_request_mock.return_value = { 'id': 1, 'uuid': 'b5f819ab-41b8-4104-90e9-1ceb751f55f8', 'uid': 'cowhouse' } mp_get_request_mock.return_value = { 'id': 1, 'audioFileProcessing': True } mp_put_request_mock.return_value.status_code = 400 mp_put_request_mock.return_value.json.return_value = {'error': 'error'} podcast_id = 2 new_episode = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) with pytest.raises(MegaphoneError) as err: episode.update_episode(podcast_id, new_episode['id'], {'title': 'new title', 'draft': True}) assert err.value.status == status.BAD_REQUEST assert err.value.message == {'error': 'error', 'is_audio_processing': True} @patch('owsrequest.request.get') def test_reupload_mp3_to_megaphone_success( request_mock, oat_response_fixture, mock_current_admin_user, output_cdn_fixture, get_assets_mock, mock_megaphone, ): """Test reupload mp3 to megaphone success.""" episode_id = 3 request_mock.return_value.json.return_value = oat_response_fixture episode.reupload_mp3_to_megaphone(episode_id) megaphone.call_episode_api.assert_called_with( { 'backgroundAudioFileUrl': 'http://url/b.mp3', 'retainAdLocations': True }, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4720', '6bf5bea4-a1d4-11e6-8dea-b334e2aa4811', 2, 3 ) @patch('owsrequest.request.get') def test_reupload_mp3_to_megaphone_fails_no_access( request_mock, oat_response_fixture, mock_current_network_admin_user, output_cdn_fixture, get_assets_mock, mock_megaphone, ): """Test reupload mp3 to megaphone fails if no access.""" episode_id = 3 request_mock.return_value.json.return_value = oat_response_fixture with pytest.raises(OwsError) as err: episode.reupload_mp3_to_megaphone(episode_id) assert err.value.status == 403 megaphone.call_episode_api.assert_not_called() def test_reupload_mp3_to_megaphone_no_mp3_asset(output_cdn_fixture, monkeypatch, mock_current_admin_user): """Test that reupload mp3 to megaphone fails with missing MP3 asset.""" episode_id = 3 monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={'items': [ { 'asset_type': 'JPG', 'asset_subtype': 'xlarge_cover', 'filename': 'images/xlarge_cover/filename.jpg' }, ]})) ) with pytest.raises(OwsError) as err: episode.reupload_mp3_to_megaphone(episode_id) assert err.value.status == status.BAD_REQUEST assert err.value.message == error.ERROR_ASSET_FINAL_NOT_FOUND def test_reupload_mp3_to_megaphone_no_megaphone_id(mock_current_admin_user): """Test that reupload mp3 to megaphone fails with no episode in megaphone.""" episode_id = 2 with pytest.raises(OwsError) as err: episode.reupload_mp3_to_megaphone(episode_id) assert err.value.status == status.BAD_REQUEST assert err.value.message == error.ERROR_MESSAGE_MEGAPHONE_EPISODE_NOT_FOUND @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episodes(mock_get_feature_flag, monkeypatch, mock_current_admin_user): """Test get episodes for a podcast.""" podcast_id = 1 episode_data = dict( id=3, podcast_id=1, title='Episode Title', summary='Episode Summary', season_number=1, episode_number=1, episode_type='full', content='clean', created_by='test_user', updated_by='test_user', created_date=datetime.now(), updated_date=datetime.now() ) monkeypatch.setattr( episode_model, 'get_episodes', MagicMock( return_value={'items': [episode_data]})) monkeypatch.setattr( episode_model, 'get_drafts_count', MagicMock(return_value=4)) monkeypatch.setattr( episode_model, 'get_done_count', MagicMock(return_value=2)) result = episode.get_episodes(podcast_id) assert result['items'][0] == episode_data assert result['pagination'] == { 'total_records': 6, 'total_done': 2, 'total_drafts': 4, } @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episodes_not_found(mock_get_feature_flag, monkeypatch, mock_current_admin_user): """Test get episodes for a podcast when podcast is not found.""" podcast_id = 1 items = [] total_records = 0 monkeypatch.setattr( episode_model, 'get_episodes', MagicMock( return_value={ 'items': items, 'pagination': { 'total_records': total_records } })) result = episode.get_episodes(podcast_id) data_response = result['items'] assert len(data_response) == 0 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episodes_no_access(mock_get_feature_flag, monkeypatch, mock_current_user): """Test get episodes for a podcast when you have no access.""" podcast_id = 2 with pytest.raises(OwsError) as err: episode.get_episodes(podcast_id) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_by_id(mock_get_feature_flag, mock_current_user): """Test to get episode by podcast_id and episode_id.""" podcast_id = 1 episode_id = 1 result = episode.get_episode_by_id(podcast_id, episode_id) assert result['id'] == episode_id assert result['podcast_id'] == podcast_id @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_by_id_no_access(mock_get_feature_flag, mock_current_user): """Test to get episode by id with no access.""" podcast_id = 2 episode_id = 1 with pytest.raises(OwsError) as err: episode.get_episode_by_id(podcast_id, episode_id) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_by_id_not_found(mock_get_feature_flag, mock_current_admin_user): """Test to get error 404 with wrong podcast_id or episode_id.""" podcast_id = 11 episode_id = 1 with pytest.raises(OwsError) as err: episode.get_episode_by_id(podcast_id, episode_id) assert err.value.status == status.NOT_FOUND podcast_id = 1 episode_id = 11 with pytest.raises(OwsError) as err: episode.get_episode_by_id(podcast_id, episode_id) assert err.value.status == status.NOT_FOUND def test_delete_episode(monkeypatch, mock_current_admin_user): """Test to delete episode.""" podcast_id = 1 episode_id = 1 monkeypatch.setattr(megaphone, 'delete_episode', MagicMock(return_value=None)) result = episode.delete_episode(podcast_id, episode_id) assert result['id'] == episode_id with pytest.raises(OwsError) as err: episode.delete_episode(podcast_id, episode_id) assert err.value.status == status.NOT_FOUND def test_delete_episode_no_access(monkeypatch, mock_current_user): """Test to delete episode with no access.""" podcast_id = 1 episode_id = 1 with pytest.raises(OwsError) as err: episode.delete_episode(podcast_id, episode_id) assert err.value.status == 403 def test_delete_episode_success_called(monkeypatch, mock_current_admin_user): """Test that delete_episode is called with right params.""" podcast_id = 1 episode_id = 1 monkeypatch.setattr(megaphone, 'delete_episode', MagicMock(return_value=None)) podcast_data = podcast_model.get_podcast_by_id(podcast_id) result = episode.delete_episode(podcast_id, episode_id) podcast_megaphone_id = podcast_data['megaphone_id'] episode_megaphone_id = result['megaphone_id'] megaphone.delete_episode.assert_called_once_with( podcast_megaphone_id, episode_megaphone_id, 1) def test_delete_episode_is_not_called(monkeypatch, mock_current_admin_user, output_cdn_fixture): """Test that delete_episode is not called for unpublished episode.""" podcast_id = 1 episode_id = 2 monkeypatch.setattr(megaphone, 'delete_episode', MagicMock(return_value=None)) episode.delete_episode(podcast_id, episode_id) megaphone.delete_episode.assert_not_called() @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_no_assets_error( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user): """Test that update episode fails with assets don't exist in db.""" podcast_id = 1 episode_id = 2 monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={'items': []}) )) episode_info = { 'description': 'new_description', 'draft': False } with pytest.raises(OwsError) as err: episode.update_episode(podcast_id, episode_id, episode_info) assert err.value.status == status.BAD_REQUEST @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_no_access(mock_g, mock_get_feature_flag, monkeypatch, mock_current_user): """Test that update episode fails with no access.""" podcast_id = 2 episode_id = 2 with pytest.raises(OwsError) as err: episode.update_episode(podcast_id, episode_id, {'draft': False}) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_no_mp3_asset( mock_g, mock_get_feature_flag, output_cdn_fixture, monkeypatch, mock_current_admin_user): """Test that update episode fails with missing MP3 asset.""" podcast_id = 1 episode_id = 2 monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={'items': [ { 'asset_type': 'JPG', 'asset_subtype': 'xlarge_cover', 'filename': 'images/xlarge_cover/filename.jpg' }, ]})) ) episode_info = { 'description': 'new_description', 'draft': False } with pytest.raises(OwsError) as err: episode.update_episode(podcast_id, episode_id, episode_info) assert err.value.status == status.BAD_REQUEST @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_with_mp3_and_no_image( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, mock_megaphone): """Test that update episode succeeds with missing MP3 asset.""" podcast_id = 1 episode_id = 2 episode_data = episode.get_episode_by_id(podcast_id, episode_id) monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={ 'audio_path': 'filename.mp3', 'audio_url': 'http://url/filename.mp3', 'audio_duration': 10000 })) ) monkeypatch.setattr(episode, 's3', MagicMock(check_s3_file_exists=MagicMock(return_value=None))) episode_info = { 'description': 'new_description', 'draft': False, 'content': 'clean' } episode.update_episode(podcast_id, episode_id, episode_info) megaphone.call_episode_api.assert_called_once_with( { 'externalId': episode_data['uuid'], 'guid': episode_data['uuid'], 'title': 'title2', 'explicit': 'clean', 'summary': 'new_description', 'episodeNumber': 2, 'seasonNumber': 1, 'pubdate': megaphone._get_utc_pubdate( datetime.strptime('2018-01-01T08:20:20.000Z', '%Y-%m-%dT%H:%M:%S.%fZ')), 'preCount': 0, 'preOffset': 0, 'postCount': 0, 'postOffset': 0, 'expectedAdhash': '122', 'insertionPoints': [], 'backgroundImageFileUrl': None, 'backgroundAudioFileUrl': 'http://url/filename.mp3', 'draft': False, 'retainAdLocations': True, 'episodeType': 'full' }, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', None, 1, 2) @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True, False]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_scheduled_email_sent( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, mock_megaphone): """Test sends an email when published date changes.""" podcast_id = 1 episode_id = 2 episode.get_episode_by_id(podcast_id, episode_id) monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={ 'audio_path': 'filename.mp3', 'audio_url': 'http://url/filename.mp3', 'audio_duration': 10000 })) ) monkeypatch.setattr(episode, 's3', MagicMock(check_s3_file_exists=MagicMock(return_value=None))) episode_info = { 'draft': False, 'published_date': '2050-11-26T16:52:01.000Z' } episode.update_episode(podcast_id, episode_id, episode_info) email._send_sqs.assert_called() @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_no_assets_on_s3( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, monkeypatch, mock_current_admin_user, get_assets_mock): """Test that update episode fails when some assets don't exist in s3.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 1 episode_id = 2 monkeypatch.setattr(episode, 's3', MagicMock( check_s3_file_exists=MagicMock( side_effect=OwsError.bad_request( message=error.ERROR_ASSET_FINAL_NOT_FOUND ) ) )) episode_info = { 'description': 'new_description', 'draft': False } with pytest.raises(OwsError) as err: episode.update_episode(podcast_id, episode_id, episode_info) assert err.value.status == status.BAD_REQUEST @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, False, True, False]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_megaphone_create_with_mp3_and_image( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, monkeypatch, mock_current_admin_user, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test that episode's status changes on success submission.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 1 episode_id = 2 episode_data = episode.get_episode_by_id(podcast_id, episode_id) assert episode_data['status'] == 'draft' monkeypatch.setattr(episode, 's3', MagicMock(check_s3_file_exists=MagicMock(return_value=None))) episode_info = { 'description': 'new_description', 'draft': False, 'content': None } episode.update_episode(podcast_id, episode_id, episode_info) megaphone.call_episode_api.assert_called_once_with( { 'externalId': episode_data['uuid'], 'guid': episode_data['uuid'], 'title': 'title2', 'explicit': 'no', 'summary': 'new_description', 'episodeNumber': 2, 'seasonNumber': 1, 'pubdate': megaphone._get_utc_pubdate( datetime.strptime('2018-01-01T08:20:20.000Z', '%Y-%m-%dT%H:%M:%S.%fZ')), 'preCount': 0, 'preOffset': 0, 'postCount': 0, 'postOffset': 0, 'expectedAdhash': '122', 'insertionPoints': [], 'backgroundImageFileUrl': 'http://url/c.jpeg', 'backgroundAudioFileUrl': 'http://url/b.mp3', 'draft': False, 'retainAdLocations': True, 'episodeType': 'full' }, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', None, 1, 2) episode_data = episode.get_episode_by_id(podcast_id, episode_id) assert episode_data['status'] == 'published' @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[False, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_podcast_is_deleted_error( mock_g, mock_get_feature_flag, monkeypatch, mock_current_admin_user, output_cdn_fixture): """Test that update episode fails with podcast it relates to is deleted.""" podcast_id = 1 episode_id = 2 episode_data = episode.get_episode_by_id(podcast_id, episode_id) assert episode_data['status'] == 'draft' monkeypatch.setattr(episode, 's3', MagicMock(check_s3_file_exists=MagicMock(return_value=None))) monkeypatch.setattr(podcast_model, 'get_podcast_by_id', MagicMock( side_effect=OwsError.not_found('Podcast not found') )) episode_info = { 'description': 'new_description', 'draft': False } with pytest.raises(OwsError) as err: episode.update_episode(podcast_id, episode_id, episode_info) assert err.value.status == status.NOT_FOUND @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_commit_audio( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, get_assets_mock, mock_megaphone ): """Test update podcast and commits artwork and deletes insertion points.""" episode_id = 1 data = {'audio_filename': 'audiopow.wav', 'draft': True} monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) result = episode.get_episode_by_id(1, episode_id) assert len(result['insertion_points']) == 3 monkeypatch.setattr(oat, 'commit', MagicMock()) assert transcription_model.get(episode_id) is not None episode.update_episode(1, 1, data) oat.commit.assert_called_with('audiopow.wav', episode_id) result = episode.get_episode_by_id(1, episode_id) assert len(result['insertion_points']) == 0 assert transcription_model.get(episode_id) is None @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_when_audio_processing( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, mock_megaphone): """Test doesn't submit audio when processing.""" episode_id = 2 episode_info = { 'description': 'new_description', 'draft': False, 'content': None } monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=True )) monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={ 'audio_path': 'filename.mp3', 'audio_url': 'http://url/filename.mp3', 'audio_duration': 10000 })) ) episode.update_episode(1, episode_id, episode_info) megaphone_info = { 'externalId': 'abc', 'guid': 'abc', 'title': 'title2', 'explicit': 'no', 'summary': 'new_description', 'episodeNumber': 2, 'seasonNumber': 1, 'pubdate': '2018-01-01T08:20:20.000Z', 'preCount': 0, 'preOffset': 0, 'postCount': 0, 'postOffset': 0, 'expectedAdhash': '122', 'insertionPoints': [], 'backgroundImageFileUrl': None, 'draft': False, 'retainAdLocations': True, 'episodeType': 'full' } megaphone.call_episode_api.assert_called_once_with( megaphone_info, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', None, 1, 2) @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_when_audio_not_processing( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, mock_megaphone): """Test submits audio when not processing.""" episode_id = 2 episode_info = { 'description': 'new_description', 'draft': False, 'content': None } monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) monkeypatch.setattr(episode, 'oat', MagicMock( get_episode_assets=MagicMock( return_value={ 'audio_path': 'filename.mp3', 'audio_url': 'http://url/filename.mp3', 'audio_duration': 10000 })) ) episode.update_episode(1, episode_id, episode_info) megaphone_info = { 'externalId': 'abc', 'guid': 'abc', 'title': 'title2', 'explicit': 'no', 'summary': 'new_description', 'episodeNumber': 2, 'seasonNumber': 1, 'pubdate': '2018-01-01T08:20:20.000Z', 'preCount': 0, 'preOffset': 0, 'postCount': 0, 'postOffset': 0, 'expectedAdhash': '122', 'insertionPoints': [], 'backgroundImageFileUrl': None, 'backgroundAudioFileUrl': 'http://url/filename.mp3', 'draft': False, 'retainAdLocations': True, 'episodeType': 'full' } megaphone.call_episode_api.assert_called_once_with( megaphone_info, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', None, 1, 2) @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_commit_artwork( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, get_assets_mock, mock_megaphone): """Test update podcast and commits artwork.""" episode_id = 2 data = {'artwork_filename': 'artworkmeow', 'draft': True} monkeypatch.setattr(oat, 'commit', MagicMock()) episode.update_episode(1, episode_id, data) oat.commit.assert_called_with('artworkmeow', episode_id) @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_delete_artwork( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_admin_user, get_assets_mock, mock_megaphone): """Test update podcast and deletes artwork.""" episode_id = 2 data = {'artwork_filename': None, 'draft': True} monkeypatch.setattr(oat, 'delete_asset', MagicMock()) episode.update_episode(1, episode_id, data) oat.delete_asset.assert_called_with('artwork', episode_id, 'episode') @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_create_planned_inventory( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_admin_user, mock_megaphone ): """Test create planned inventory.""" podcast_id = 1 data = {'cow': 'man', 'draft': True} episode_fixture['published_date'] = datetime(2019, 11, 26, 16, 52, 1) monkeypatch.setattr( episode_model, 'create_planned_inventory', MagicMock( return_value={'items': [episode_fixture]})) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value={})) result = episode.create_planned_inventory(podcast_id, data) episode_model.create_planned_inventory.assert_called_once_with(1, data) megaphone.call_episode_api.assert_called_once episode_model.update_episode.assert_called_once_with(4, {'megaphone_id': 1, 'megaphone_uid': 'cowhouse'}) assert result['items'] == [episode_fixture] @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_create_planned_inventory_and_not_reviewed_not_sent_to_megaphone( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_admin_user, mock_megaphone ): """Test create planned inventory but not reviewed so not sent megaphone..""" podcast_id = 1 data = {'cow': 'man', 'draft': True} episode_fixture['published_date'] = datetime(2019, 11, 26, 16, 52, 1) episode_fixture['is_reviewed'] = False monkeypatch.setattr( episode_model, 'create_planned_inventory', MagicMock( return_value={'items': [episode_fixture]})) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value={})) result = episode.create_planned_inventory(podcast_id, data) episode_model.create_planned_inventory.assert_called_once_with(1, data) assert not megaphone.call_episode_api.called assert not episode_model.update_episode.called assert result['items'] == [episode_fixture] @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_create_planned_inventory_and_not_sent_megaphone( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_admin_user, mock_megaphone ): """Test create planned inventory but apple-subscription feed do not sent megaphone.""" podcast_id = 4 data = {'cow': 'man', 'draft': True} episode_fixture['published_date'] = datetime(2019, 11, 26, 16, 52, 1) monkeypatch.setattr( episode_model, 'create_planned_inventory', MagicMock( return_value={'items': [episode_fixture]})) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value={})) result = episode.create_planned_inventory(podcast_id, data) episode_model.create_planned_inventory.assert_called_once_with(4, data) assert not megaphone.call_episode_api.called assert not episode_model.update_episode.called assert result['items'] == [episode_fixture] @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_create_planned_inventory_no_acess( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_user ): """Test create planned inventory with no access.""" podcast_id = 2 data = {'cow': 'man', 'draft': True} with pytest.raises(OwsError) as err: episode.create_planned_inventory(podcast_id, data) assert err.value.status == 403 def test_create_planned_inventory_read_only( monkeypatch, episode_fixture, mock_current_read_only_user): """Test create planned inventory fails if user is read only.""" podcast_id = 1 data = {'cow': 'man', 'draft': True} with pytest.raises(OwsError) as err: episode.create_planned_inventory(podcast_id, data) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_episodes_by_ids(mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_admin_user): """Test get episodes by ids.""" return_value = {'items': [episode_fixture]} monkeypatch.setattr(episode_model, 'get_episodes_by_ids', MagicMock(return_value=return_value)) result = episode.get_episodes_by_ids([1, 2]) episode_model.get_episodes_by_ids.assert_called_once_with([1, 2]) assert result == return_value @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_episodes_by_ids_no_access(mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_user): """Test get many episodes by ids no access.""" return_value = {'items': [episode_fixture, {'podcast_id': 2}]} monkeypatch.setattr(episode_model, 'get_episodes_by_ids', MagicMock(return_value=return_value)) with pytest.raises(OwsError) as err: episode.get_episodes_by_ids([1, 2]) assert err.value.status == 403 @patch('requests.get') def test_megaphone_server_error(mock_get): """Test megaphone server error.""" mock_get.return_value.status_code = status.INTERNAL_ERROR mock_get.return_value.text = '500 error' with pytest.raises(MegaphoneError) as err: ApiBase(is_org=True)._get('test') assert err.value.status == status.INTERNAL_ERROR assert err.value.message == {'error': '500 error', 'is_megaphone_server_error': True} @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_is_processing(mock_get_feature_flag, monkeypatch, mock_current_admin_user): """Test episode is processing.""" monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) response = episode.get_episode_is_processing(1, 1) megaphone.is_megaphone_audio_processing.assert_called_once_with( '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', '6bf5bea4-a1d4-11e6-8dea-b334e2aa4880', 1) response == {'is_processing': False} def test_get_object_asset_by_id_and_type_exists_in_output_bucket( monkeypatch, mock_get_object_asset_by_id_and_type_wav, mock_current_admin_user): """Test get object asset by id and type for episode wav asset that already exists in output bucket.""" assert_url = 'https://{}/461f5444_a522_4ff1_b51f_17b699df621b.wav'.format(config.OUTPUT_CDN_BASE_URL) signed_url = '{}?Policy=policy&Signature=signature&Key-Pair-Id=key-pair'.format(assert_url) monkeypatch.setattr(s3, 'check_s3_file_exists', MagicMock(return_value='')) monkeypatch.setattr(api_utils, 'asset_url', MagicMock(return_value=assert_url)) monkeypatch.setattr(signed_urls, 'sign_url', MagicMock(return_value=signed_url)) result = episode.get_episode_wav_asset(1) oat.get_object_asset_by_id_and_type.assert_called_once_with(1, 'episode', 'WAV') s3.check_s3_file_exists.assert_called_once_with( config.OUTPUT_ASSETS_BUCKET_NAME, '461f5444_a522_4ff1_b51f_17b699df621b.wav') api_utils.asset_url.assert_called_once_with('461f5444_a522_4ff1_b51f_17b699df621b.wav') signed_urls.sign_url.assert_called_once_with(assert_url) assert result['audio_wav_url'] == signed_url def test_get_object_asset_by_id_and_type_copy_in_output_bucket( monkeypatch, mock_get_object_asset_by_id_and_type_wav, mock_current_admin_user): """Test get object asset by id and type for episode wav asset that's copied to output bucket from input bucket.""" assert_url = 'https://{}/461f5444_a522_4ff1_b51f_17b699df621b.wav'.format(config.OUTPUT_CDN_BASE_URL) signed_url = '{}?Policy=policy&Signature=signature&Key-Pair-Id=key-pair'.format(assert_url) monkeypatch.setattr(s3, 'check_s3_file_exists', MagicMock( return_value=OwsError.internal_server_error())) monkeypatch.setattr(s3, 'copy_s3_file', MagicMock(return_value={})) monkeypatch.setattr(api_utils, 'asset_url', MagicMock(return_value=assert_url)) monkeypatch.setattr(signed_urls, 'sign_url', MagicMock(return_value=signed_url)) result = episode.get_episode_wav_asset(1) oat.get_object_asset_by_id_and_type.assert_called_once_with(1, 'episode', 'WAV') s3.check_s3_file_exists.assert_called_once_with( config.OUTPUT_ASSETS_BUCKET_NAME, '461f5444_a522_4ff1_b51f_17b699df621b.wav') api_utils.asset_url.assert_called_once_with('461f5444_a522_4ff1_b51f_17b699df621b.wav') signed_urls.sign_url.assert_called_once_with(assert_url) assert result['audio_wav_url'] == signed_url def test_get_object_asset_by_id_and_type_copy_no_access(monkeypatch, mock_current_user): """Test get episode wav asset no access.""" with pytest.raises(OwsError) as err: episode.get_episode_wav_asset(1) assert err.value.status == 403 @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_and_create_assets(mock_g, mock_replicate_audio_assets, monkeypatch, mock_current_admin_user, episode_fixture, episode_fixture_2, mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2, podcast_2_seasons_replica_fixture): """Test replicate episodes and create assets.""" mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode, 'get_episodes_by_ids', MagicMock(return_value={'items': [episode_fixture, episode_fixture_2]}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_bulk', MagicMock(side_effect=[mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2]) ) monkeypatch.setattr( episode, '_replicate_ad_locations_for_type_bulk', MagicMock(side_effect=[mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-completed'}, {'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'} ] } monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_episodes_and_create_assets( 1, 4, [4, 6], podcast_2_seasons_replica_fixture, 'bulk', True, True) episode.get_episodes_by_ids.assert_called_with([4, 6]) episode._replicate_episode_for_type_bulk.assert_has_calls([ call(1, 4, podcast_2_seasons_replica_fixture, 'bulk', ANY, True, episode_fixture), call(1, 4, podcast_2_seasons_replica_fixture, 'bulk', ANY, True, episode_fixture_2)]) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 4, 'episode_id': 5}, {'original_episode_id': 6, 'episode_id': 7} ]) episode_replication_status_model.update_status.assert_has_calls( [call({'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-completed'}), call({'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'})] ) episode._replicate_ad_locations_for_type_bulk.assert_has_calls([ call( [ {'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-completed'}, {'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'} ], ANY, {'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 4, 'episode_id': 5} ), call( [ {'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-completed'}, {'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'} ], ANY, {'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 6, 'episode_id': 7} ) ]) assert result == [mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2] @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_and_assets_with_no_seasons(mock_g, mock_replicate_audio_assets, monkeypatch, mock_current_admin_user, episode_fixture, episode_fixture_2, mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2): """Test replicate episodes and create assets.""" monkeypatch.setattr( episode, 'get_episodes_by_ids', MagicMock(return_value={'items': [episode_fixture, episode_fixture_2]}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_bulk', MagicMock(side_effect=[mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-completed'}, {'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'} ] } mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_episodes_and_create_assets(1, 4, [4, 6], None, 'bulk', True, False) episode._replicate_episode_for_type_bulk.assert_has_calls([ call(1, 4, None, 'bulk', ANY, True, episode_fixture), call(1, 4, None, 'bulk', ANY, True, episode_fixture_2)]) assert result == [mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2] @patch('podcast.logic.episode.send_to_sentry') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_and_create_assets_fails(mock_g, mock_sentry, monkeypatch, mock_current_read_only_user): """Test replicate_episode_and_create_assets_fails.""" mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode, 'get_episodes_by_ids', MagicMock(side_effect=OwsError.forbidden()) ) episode.replicate_episodes_and_create_assets(1, 4, [4], None, 'bulk', True, False) mock_sentry.assert_called_once() @patch('podcast.logic.episode.send_to_sentry') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_and_create_assets_for_failed_oat_call(mock_g, mock_replicate_audio_assets, mock_sentry, monkeypatch, mock_current_admin_user, episode_fixture, mock_replicate_episodes_fixture): """Test exception raised for failed OAT calls.""" monkeypatch.setattr( episode, 'get_episodes_by_ids', MagicMock(return_value={'items': [episode_fixture]}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_bulk', MagicMock(side_effect=[mock_replicate_episodes_fixture]) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) mock_replicate_audio_assets.return_value.status_code = 500 mock_g.return_value.id.return_value = 1 episode.replicate_episodes_and_create_assets(1, 4, [4], None, 'bulk', True, False) episode_replication_status_model.update_status.assert_has_calls( [call({'original_episode_id': 4, 'episode_id': 5, 'status': 'asset-replication-failed'})] ) mock_sentry.assert_called_once() @patch('podcast.logic.episode.send_to_sentry') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_audio_assets_not_called(mock_g, mock_replicate_audio_assets, mock_sentry, monkeypatch, mock_current_admin_user, episode_fixture, episode_fixture_2, mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2): """Test replicate audio assets not called with empty episode id.""" mock_replicate_episodes_fixture['episode_id'] = None mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [{'original_episode_id': 6, 'episode_id': 7, 'status': 'asset-replication-completed'}] } mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode, 'get_episodes_by_ids', MagicMock(return_value={'items': [episode_fixture, episode_fixture_2]}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_bulk', MagicMock(side_effect=[mock_replicate_episodes_fixture, mock_replicate_episodes_fixture_2]) ) episode.replicate_episodes_and_create_assets(1, 4, [4, 6], None, 'bulk', True, False) mock_replicate_audio_assets.assert_called_once_with([{'original_episode_id': 6, 'episode_id': 7}]) def test_replicate_episodes(monkeypatch, episode_fixture, db_session_fixture, episode_replication_status_fixture, mock_replicate_episodes_fixture, podcast_2_seasons_replica_fixture): """Test _replicate_episode_for_type_bulk method.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=duplicated_episode_fixture) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=episode_replication_status_fixture) ) result = episode._replicate_episode_for_type_bulk( 1, 4, podcast_2_seasons_replica_fixture, 'bulk', 1, True, episode_fixture) episode_replication_status_model.create_status.assert_called_once_with({ 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 4, 'replication_type': 'bulk' }, db_session_fixture) episode_model.create_episode.assert_called_once_with({ 'podcast_id': 4, 'title': episode_fixture.get('title'), 'description': episode_fixture.get('description'), 'episode_type': episode_fixture.get('episode_type'), 'trailer_type': episode_fixture.get('trailer_type'), 'content': episode_fixture.get('content'), 'season_number': episode_fixture.get('season_number'), 'episode_number': episode_fixture.get('episode_number'), 'external_id': episode_fixture.get('external_id'), 'published_date': episode_fixture.get('published_date'), 'is_reviewed': False, 'season_id': 5, 'apple_id': episode_fixture.get('apple_id') }, db_session_fixture, 1) episode_replication_status_model.update_status.assert_called_once_with({ 'id': episode_replication_status_fixture['id'], 'episode_id': duplicated_episode_fixture['id'], 'status': 'episode-created' }, db_session_fixture) assert result == mock_replicate_episodes_fixture @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episodes_with_no_season(mock_g, monkeypatch, mock_current_admin_user, episode_fixture, db_session_fixture, episode_replication_status_fixture, mock_replicate_episodes_fixture): """Test _replicate_episode_for_type_bulk method.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=duplicated_episode_fixture) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=episode_replication_status_fixture) ) result = episode._replicate_episode_for_type_bulk(1, 4, None, 'bulk', 1, True, episode_fixture) episode_model.create_episode.assert_called_once_with({ 'podcast_id': 4, 'title': episode_fixture.get('title'), 'description': episode_fixture.get('description'), 'episode_type': episode_fixture.get('episode_type'), 'trailer_type': episode_fixture.get('trailer_type'), 'content': episode_fixture.get('content'), 'season_number': episode_fixture.get('season_number'), 'episode_number': episode_fixture.get('episode_number'), 'external_id': episode_fixture.get('external_id'), 'published_date': episode_fixture.get('published_date'), 'is_reviewed': False, 'season_id': None, 'apple_id': episode_fixture.get('apple_id') }, db_session_fixture, 1) assert result == mock_replicate_episodes_fixture def test_replicate_episodes_fails(monkeypatch, mock_current_admin_user, episode_fixture, mock_replicate_episodes_fixture): """Test _replicate_episode_for_type_bulk fails wile creating episode replication status.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 mock_replicate_episodes_fixture['error_message'] = 'Cannot record status:400-Bad Request' monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=None) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_episode_for_type_bulk(1, 4, None, 'bulk', 1, True, episode_fixture) assert not episode_model.create_episode.called assert not episode_replication_status_model.update_status.called assert result == { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 4, 'episode_id': None, 'error_message': 'Cannot record status:400-Bad Request'} def test_replicate_episodes_fails_creating_episodes(monkeypatch, mock_current_admin_user, episode_fixture, episode_replication_status_fixture): """Test _replicate_episode_for_type_bulk fails wile creating episode.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_episode_for_type_bulk(1, 4, None, 'bulk', 1, True, episode_fixture) assert episode_replication_status_model.create_status.called assert episode_model.create_episode.called episode_replication_status_model.update_status.assert_called_once_with( {'id': 5, 'status': 'episode-creation-failed'}) assert result == { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 4, 'episode_id': None, 'error_message': 'Cannot create episode:400-Bad Request'} def test_replicate_bulk_ad_locations_success(monkeypatch, mock_current_admin_user): """Test _replicate_ad_locations_for_type_bulk success.""" assets = [ { 'original_episode_id': 1, 'episode_id': 2, 'status': 'asset-replication-completed' } ] processed_episode = { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 1, 'episode_id': 2 } monkeypatch.setattr( insertion_point_model, 'get_insertion_points', MagicMock(return_value={ 'items': [ {'id': 123, 'episode_id': 1, 'point_type': 'pre', 'timecode': '1.0', 'count': '1', 'created_by': 1}, ] }) ) monkeypatch.setattr( insertion_point_model, 'create_insertion_points', MagicMock() ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_ad_locations_for_type_bulk(assets, 1, processed_episode) insertion_point_model.get_insertion_points.assert_called_once_with(1) insertion_point_model.create_insertion_points.assert_called_once_with( 2, [{'episode_id': 2, 'point_type': 'pre', 'timecode': '1.0', 'count': '1'}], user_id=1) episode_replication_status_model.update_status.assert_called_once_with( {'original_episode_id': 1, 'episode_id': 2, 'status': 'ad-locations-replication-completed'}) assert result == { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 1, 'episode_id': 2, 'status': 'ad-locations-replication-completed'} @patch('podcast.logic.episode.send_to_sentry') def test_replicate_bulk_ad_locations_failure(mock_sentry, monkeypatch, mock_current_admin_user): """Test _replicate_ad_locations_for_type_bulk failure.""" assets = [ { 'original_episode_id': 1, 'episode_id': 2, 'status': 'asset-replication-completed' } ] processed_episode = { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 1, 'episode_id': 2 } monkeypatch.setattr( insertion_point_model, 'get_insertion_points', MagicMock(return_value={ 'items': [ {'id': 123, 'episode_id': 1, 'point_type': 'pre', 'timecode': '1.0', 'count': '1', 'created_by': 1}, ] }) ) monkeypatch.setattr( insertion_point_model, 'create_insertion_points', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_ad_locations_for_type_bulk(assets, 1, processed_episode) insertion_point_model.get_insertion_points.assert_called_once_with(1) assert insertion_point_model.create_insertion_points.called episode_replication_status_model.update_status.assert_called_once_with( {'original_episode_id': 1, 'episode_id': 2, 'status': 'ad-locations-replication-failed'}) mock_sentry.assert_called_once() assert result == { 'original_podcast_id': 1, 'podcast_id': 4, 'original_episode_id': 1, 'episode_id': 2, 'status': 'ad-locations-replication-failed'} @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[True, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_show_family_access_success( mock_g, mock_get_feature_flag, monkeypatch, podcast_fixture, mock_current_network_admin_user, get_assets_mock, mock_megaphone ): """Test create episode success with show family access.""" podcast_id = 1 result = episode.create_episode(podcast_id, {'draft': True, 'title': 'cow'}) assert result['id'] == 5 assert result['created_by'] == 6 assert result['updated_by'] == 6 assert result['status'] == 'draft' assert result['title'] == 'cow' @patch('podcast.utils.feature_flag_utils.get_feature_flag', side_effect=[True, True]) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_create_episode_show_family_access_failure( mock_g, mock_get_feature_flag, mock_current_network_admin_user_second): """Test create episode fails if no show family access.""" podcast_id = 1 with pytest.raises(OwsError) as err: episode.create_episode(podcast_id, {'draft': True}) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_show_family_access_success( request_mock, mock_g, mock_get_feature_flag, oat_response_fixture, mock_current_podcast_level_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode success with show family access.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 1 episode_id = 1 monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) episode_data = episode.update_episode( podcast_id, episode_id, {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'}, ) megaphone.call_episode_api.assert_called_with( { 'externalId': episode_data['uuid'], 'guid': episode_data['uuid'], 'title': 'new title', 'explicit': 'yes', 'summary': '
description.
', 'episodeNumber': 1, 'seasonNumber': 1, 'pubdate': '2019-06-01T08:20:20.000Z', 'preCount': 1, 'preOffset': '0E-15', 'postCount': 1, 'postOffset': 24.384, 'expectedAdhash': '012', 'insertionPoints': ['3.140000000000000'], 'backgroundImageFileUrl': 'http://url/c.jpeg', 'backgroundAudioFileUrl': 'http://url/b.mp3', 'draft': True, 'retainAdLocations': True, 'episodeType': 'bonus' }, '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', '6bf5bea4-a1d4-11e6-8dea-b334e2aa4880', 1, 1 ) @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_show_family_access_success_and_not_sent_to_megaphone( request_mock, mock_g, mock_get_feature_flag, episode_fixture, oat_response_fixture, mock_current_podcast_level_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode success with show family access but youtube feed so not sent megaphone.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 3 episode_id = 1 monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock(return_value=False)) monkeypatch.setattr(episode_model, 'get_episode_by_id', MagicMock()) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value=episode_fixture)) data = {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'} episode.update_episode(podcast_id, episode_id, data) episode_model.get_episode_by_id.assert_called_once_with(1) data['trailer_type'] = None episode_model.update_episode.assert_called_once_with(1, data) assert not megaphone.call_episode_api.called @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) @patch('owsrequest.request.get') def test_update_episode_show_family_access_success_and_not_reviewed_not_sent_to_megaphone( request_mock, mock_g, mock_get_feature_flag, episode_fixture, oat_response_fixture, mock_current_podcast_level_user, monkeypatch, output_cdn_fixture, get_assets_mock, mock_megaphone ): """Test update episode success with show family access but not reviewed so not sent megaphone.""" request_mock.return_value.json.return_value = oat_response_fixture podcast_id = 1 episode_id = 1 episode_fixture['is_reviewed'] = False monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock(return_value=False)) monkeypatch.setattr(episode_model, 'get_episode_by_id', MagicMock()) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value=episode_fixture)) data = {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'} episode.update_episode(podcast_id, episode_id, data) episode_model.get_episode_by_id.assert_called_once_with(1) data['trailer_type'] = None episode_model.update_episode.assert_called_once_with(1, data) assert not megaphone.call_episode_api.called @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) @patch('podcast.utils.feature_flag_utils.g', spec=['request_context']) def test_update_episode_show_family_access_failure( mock_g, mock_get_feature_flag, mock_current_network_admin_user ): """Test update episode fails if no show family access.""" podcast_id = 2 episode_id = 3 with pytest.raises(OwsError) as err: episode.update_episode( podcast_id, episode_id, {'title': 'new title', 'draft': True, 'content': 'explicit', 'episode_type': 'bonus'}, ) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_create_planned_inventory_show_family_access_success( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_network_admin_user, mock_megaphone ): """Test create planned inventory success with show family access.""" podcast_id = 1 data = {'cow': 'man', 'draft': True} episode_fixture['published_date'] = datetime(2019, 11, 26, 16, 52, 1) monkeypatch.setattr( episode_model, 'create_planned_inventory', MagicMock( return_value={'items': [episode_fixture]})) monkeypatch.setattr(episode_model, 'update_episode', MagicMock(return_value={})) result = episode.create_planned_inventory(podcast_id, data) episode_model.create_planned_inventory.assert_called_once_with(1, data) megaphone.call_episode_api.assert_called_once episode_model.update_episode.assert_called_once_with(4, {'megaphone_id': 1, 'megaphone_uid': 'cowhouse'}) assert result['items'] == [episode_fixture] @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_create_planned_inventory_show_family_access_failure( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_user ): """Test create planned inventory with no show family access.""" podcast_id = 2 data = {'cow': 'man', 'draft': True} with pytest.raises(OwsError) as err: episode.create_planned_inventory(podcast_id, data) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_episode_is_processing_show_family_access_success( mock_get_feature_flag, monkeypatch, mock_current_podcast_level_user): """Test episode is processing success with show family access.""" monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) response = episode.get_episode_is_processing(1, 1) megaphone.is_megaphone_audio_processing.assert_called_once_with( '6bf5bea4-a1d4-11e6-8dea-b334e2aa4710', '6bf5bea4-a1d4-11e6-8dea-b334e2aa4880', 1) response == {'is_processing': False} @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_episode_is_processing_show_family_access_failure( mock_get_feature_flag, monkeypatch, mock_current_user ): """Test get episode is processing with no show family access.""" monkeypatch.setattr(megaphone, 'is_megaphone_audio_processing', MagicMock( return_value=False )) with pytest.raises(OwsError) as err: episode.get_episode_is_processing(2, 2) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_by_id_show_family_access_success(mock_get_feature_flag, mock_current_user): """Test to get episode by podcast_id and episode_id with show family access.""" podcast_id = 1 episode_id = 1 result = episode.get_episode_by_id(podcast_id, episode_id) assert result['id'] == episode_id assert result['podcast_id'] == podcast_id @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_episode_by_id_show_family_access_failure( mock_get_feature_flag, mock_current_network_admin_user_second): """Test to get episode by id with no show family access.""" podcast_id = 2 episode_id = 1 with pytest.raises(OwsError) as err: episode.get_episode_by_id(podcast_id, episode_id) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_episodes_show_family_access_success(mock_get_feature_flag, monkeypatch, mock_current_user): """Test get episodes for a podcast with show family access.""" podcast_id = 1 episode_data = dict( id=3, podcast_id=1, title='Episode Title', summary='Episode Summary', season_number=1, episode_number=1, episode_type='full', content='clean', created_by='test_user', updated_by='test_user', created_date=datetime.now(), updated_date=datetime.now() ) monkeypatch.setattr( episode_model, 'get_episodes', MagicMock( return_value={'items': [episode_data]})) monkeypatch.setattr( episode_model, 'get_drafts_count', MagicMock(return_value=4)) monkeypatch.setattr( episode_model, 'get_done_count', MagicMock(return_value=2)) result = episode.get_episodes(podcast_id) assert result['items'][0] == episode_data assert result['pagination'] == { 'total_records': 6, 'total_done': 2, 'total_drafts': 4, } @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_episodes_show_family_access_failure( mock_get_feature_flag, monkeypatch, mock_current_user): """Test get episodes for a podcast when you have no show family access.""" podcast_id = 2 with pytest.raises(OwsError) as err: episode.get_episodes(podcast_id) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_episodes_by_ids_show_family_access_success( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_podcast_level_user): """Test get episodes by ids with show family access.""" return_value = {'items': [episode_fixture]} monkeypatch.setattr(episode_model, 'get_episodes_by_ids', MagicMock(return_value=return_value)) result = episode.get_episodes_by_ids([1, 2]) episode_model.get_episodes_by_ids.assert_called_once_with([1, 2]) assert result == return_value @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_episodes_by_ids_show_family_access_failure( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_network_admin_user_second): """Test get many episodes by ids no show family access.""" return_value = {'items': [episode_fixture, {'podcast_id': 2}]} monkeypatch.setattr(episode_model, 'get_episodes_by_ids', MagicMock(return_value=return_value)) with pytest.raises(OwsError) as err: episode.get_episodes_by_ids([1, 2]) assert err.value.status == 403 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_most_recent_show_family_access_success( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_podcast_level_user): """Test get most recent with show family access.""" monkeypatch.setattr( episode_model, 'get_most_recent_episodes_by_podcast_ids', MagicMock(return_value='return_value')) result = episode.get_most_recent(10) episode_model.get_most_recent_episodes_by_podcast_ids.assert_called_once_with([1, 3], 10) assert result == 'return_value' @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_most_recent_no_show_family_access( mock_get_feature_flag, monkeypatch, episode_fixture, mock_current_network_admin_user_second): """Test get many episodes by ids no show family access.""" monkeypatch.setattr( episode_model, 'get_most_recent_episodes_by_podcast_ids', MagicMock(return_value='return_value')) result = episode.get_most_recent(10) episode_model.get_most_recent_episodes_by_podcast_ids.assert_called_once_with([], 10) assert result == 'return_value' @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_single_episode_and_assets_success( mock_g, mock_replicate_audio_assets, monkeypatch, mock_current_admin_user, episode_fixture_3, mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_2, podcast_2_seasons_replica_fixture, podcast_fixture_2, podcast_fixture_3, podcast_fixture_4, replicate_single_episode_fixture ): """Test replicate single episode and create asset success.""" all_podcasts = [podcast_fixture_2, podcast_fixture_3, podcast_fixture_4] mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_single', MagicMock(side_effect=[mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_2]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-completed'}, {'original_episode_id': 7, 'episode_id': 9, 'status': 'asset-replication-completed'} ] } monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_single_episode_and_assets(replicate_single_episode_fixture) episode_model.get_episode_by_id.assert_called_with(7) podcast_model.get_podcasts_by_ids.assert_called_with([3, 4, 2]) episode._replicate_episode_for_type_single.assert_has_calls([ call(ANY, ANY, ANY, ANY, 3), call(ANY, ANY, ANY, ANY, 4)] ) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 7, 'episode_id': 8}, {'original_episode_id': 7, 'episode_id': 9} ]) episode_replication_status_model.update_status.assert_has_calls( [ call({'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-completed'}), call({'original_episode_id': 7, 'episode_id': 9, 'status': 'asset-replication-completed'}) ] ) assert result == [ { 'episode_id': 8, 'podcast_id': 3, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-completed' }, { 'episode_id': 9, 'podcast_id': 4, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'apple-subscription', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-completed' } ] @patch('podcast.models.insertion_point.create_insertion_points') @patch('podcast.models.insertion_point.get_insertion_points') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_single_episode_and_assets_and_ad_locations_success( mock_g, mock_replicate_audio_assets, mock_get_insertion_points, mock_create_insertion_points, monkeypatch, mock_current_admin_user, episode_fixture_3, mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_3, podcast_2_seasons_replica_fixture, podcast_fixture, podcast_fixture_2, podcast_fixture_3, replicate_single_episode_ad_locations_fixture, ): """Test replicate single episode and create asset with ad locations success.""" all_podcasts = [podcast_fixture, podcast_fixture_2, podcast_fixture_3] mock_g.current_user = {'id': 1} monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_single', MagicMock(side_effect=[mock_replicate_single_episode_fixture_3, mock_replicate_single_episode_fixture]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 7, 'episode_id': 6, 'status': 'asset-replication-completed'}, {'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-failed'} ] } mock_get_insertion_points.return_value = { 'items': [ {'id': 123, 'episode_id': 6, 'point_type': 'pre', 'timecode': '1.0', 'count': '1', 'created_by': 1}, {'id': 124, 'episode_id': 6, 'point_type': 'mid', 'timecode': '25.9', 'count': '2', 'created_by': 1}, {'id': 125, 'episode_id': 6, 'point_type': 'post', 'timecode': '76.3', 'count': '1', 'created_by': 1} ] } monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_single_episode_and_assets(replicate_single_episode_ad_locations_fixture) episode_model.get_episode_by_id.assert_called_with(7) podcast_model.get_podcasts_by_ids.assert_called_with([1, 3, 2]) episode._replicate_episode_for_type_single.assert_has_calls([ call(ANY, ANY, ANY, ANY, 1), call(ANY, ANY, ANY, ANY, 3)] ) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 7, 'episode_id': 6}, {'original_episode_id': 7, 'episode_id': 8} ]) mock_create_insertion_points.assert_called_once_with( 6, [ {'episode_id': 6, 'point_type': 'pre', 'timecode': '1.0', 'count': '1', 'created_by': 1}, {'episode_id': 6, 'point_type': 'mid', 'timecode': '25.9', 'count': '2', 'created_by': 1}, {'episode_id': 6, 'point_type': 'post', 'timecode': '76.3', 'count': '1', 'created_by': 1} ], ANY ) episode_replication_status_model.update_status.assert_has_calls([ call({'original_episode_id': 7, 'episode_id': 6, 'status': 'ad-locations-replication-completed'}), call({'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-failed'}) ]) assert result == [ { 'episode_id': 6, 'podcast_id': 1, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'public-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'ad-locations-replication-completed' }, { 'episode_id': 8, 'podcast_id': 3, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-failed' } ] @patch('podcast.models.insertion_point.create_insertion_points') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_single_episode_and_assets_and_ad_locations_no_public_rss_podcast( mock_g, mock_replicate_audio_assets, mock_create_insertion_points, monkeypatch, mock_current_admin_user, episode_fixture_3, mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_2, podcast_2_seasons_replica_fixture, podcast_fixture_2, podcast_fixture_3, replicate_single_episode_ad_locations_fixture, ): """Test replicate single episode and create asset with ad locations with no public rss podcast.""" replicate_single_episode_ad_locations_fixture['podcast_ids'] = [3, 4] all_podcasts = [podcast_fixture_2, podcast_fixture_3] mock_g.current_user = {'id': 1} monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_single', MagicMock(side_effect=[mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_2]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-completed'}, {'original_episode_id': 7, 'episode_id': 9, 'status': 'asset-replication-failed'} ] } monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_single_episode_and_assets(replicate_single_episode_ad_locations_fixture) episode_model.get_episode_by_id.assert_called_with(7) podcast_model.get_podcasts_by_ids.assert_called_with([3, 4, 2]) episode._replicate_episode_for_type_single.assert_has_calls([ call(ANY, ANY, ANY, ANY, 3), call(ANY, ANY, ANY, ANY, 4)] ) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 7, 'episode_id': 8}, {'original_episode_id': 7, 'episode_id': 9} ]) episode_replication_status_model.update_status.assert_has_calls([ call({'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-completed'}), call({'original_episode_id': 7, 'episode_id': 9, 'status': 'asset-replication-failed'}) ]) assert result == [ { 'episode_id': 8, 'podcast_id': 3, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-completed' }, { 'episode_id': 9, 'podcast_id': 4, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'apple-subscription', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-failed' } ] @patch('podcast.models.insertion_point.create_insertion_points') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_single_episode_and_assets_and_ad_locations_public_rss_podcast_no_asset_replicated( mock_g, mock_replicate_audio_assets, mock_create_insertion_points, monkeypatch, mock_current_admin_user, episode_fixture_3, mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_3, podcast_2_seasons_replica_fixture, podcast_fixture, podcast_fixture_2, podcast_fixture_3, replicate_single_episode_ad_locations_fixture, ): """Test replicate single episode and create asset with ad locations with pulic rss podcast no asset replicated.""" all_podcasts = [podcast_fixture, podcast_fixture_2, podcast_fixture_3] mock_g.current_user = {'id': 1} monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_single', MagicMock(side_effect=[mock_replicate_single_episode_fixture_3, mock_replicate_single_episode_fixture]) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 7, 'episode_id': 6, 'status': 'asset-replication-failed'}, {'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-failed'} ] } monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_single_episode_and_assets(replicate_single_episode_ad_locations_fixture) episode_model.get_episode_by_id.assert_called_with(7) podcast_model.get_podcasts_by_ids.assert_called_with([1, 3, 2]) episode._replicate_episode_for_type_single.assert_has_calls([ call(ANY, ANY, ANY, ANY, 1), call(ANY, ANY, ANY, ANY, 3)] ) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 7, 'episode_id': 6}, {'original_episode_id': 7, 'episode_id': 8} ]) episode_replication_status_model.update_status.assert_has_calls([ call({'original_episode_id': 7, 'episode_id': 6, 'status': 'asset-replication-failed'}), call({'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-failed'}) ]) assert result == [ { 'episode_id': 6, 'podcast_id': 1, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'public-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-failed' }, { 'episode_id': 8, 'podcast_id': 3, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-failed' } ] @patch('podcast.models.insertion_point.get_insertion_points') @patch('podcast.models.ows_asset_transcoder.replicate_episodes_audio_assets') @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_single_episode_and_assets_and_ad_locations_failure( mock_g, mock_replicate_audio_assets, mock_get_insertion_points, monkeypatch, mock_current_admin_user, episode_fixture_3, mock_replicate_single_episode_fixture, mock_replicate_single_episode_fixture_3, podcast_2_seasons_replica_fixture, podcast_fixture, podcast_fixture_2, podcast_fixture_3, replicate_single_episode_ad_locations_fixture, ): """Test replicate single episode and create asset with ad locations failure.""" all_podcasts = [podcast_fixture, podcast_fixture_2, podcast_fixture_3] mock_g.current_user = {'id': 1} monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) monkeypatch.setattr( episode, '_replicate_episode_for_type_single', MagicMock(side_effect=[mock_replicate_single_episode_fixture_3, mock_replicate_single_episode_fixture]) ) monkeypatch.setattr( insertion_point_model, 'create_insertion_points', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) mock_replicate_audio_assets.return_value.status_code = 200 mock_replicate_audio_assets.return_value.json.return_value = { 'items': [ {'original_episode_id': 7, 'episode_id': 6, 'status': 'asset-replication-completed'}, {'original_episode_id': 7, 'episode_id': 8, 'status': 'asset-replication-completed'} ] } mock_get_insertion_points.return_value = MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock() ) result = episode.replicate_single_episode_and_assets(replicate_single_episode_ad_locations_fixture) episode_model.get_episode_by_id.assert_called_with(7) podcast_model.get_podcasts_by_ids.assert_called_with([1, 3, 2]) episode._replicate_episode_for_type_single.assert_has_calls([ call(ANY, ANY, ANY, ANY, 1), call(ANY, ANY, ANY, ANY, 3)] ) mock_replicate_audio_assets.assert_called_once_with([ {'original_episode_id': 7, 'episode_id': 6}, {'original_episode_id': 7, 'episode_id': 8} ]) episode_replication_status_model.update_status.assert_has_calls([ call({'original_episode_id': 7, 'episode_id': 6, 'status': 'ad-locations-replication-failed'}) ]) assert result == [ { 'episode_id': 6, 'podcast_id': 1, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'public-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'ad-locations-replication-failed' }, { 'episode_id': 8, 'podcast_id': 3, 'original_episode_id': 7, 'original_podcast_id': 2, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'asset-replication-completed' } ] def test_replicate_single_episode_and_assets_failure( monkeypatch, mock_current_network_admin_user_second, episode_fixture_3, podcast_fixture_2, podcast_fixture_3, podcast_fixture_4, replicate_single_episode_fixture ): """Test replicate single episode and create asset failure for no show famiily access.""" all_podcasts = [podcast_fixture_2, podcast_fixture_3, podcast_fixture_4] monkeypatch.setattr( episode_model, 'get_episode_by_id', MagicMock(return_value=episode_fixture_3) ) monkeypatch.setattr( podcast_model, 'get_podcasts_by_ids', MagicMock(return_value={'items': all_podcasts}) ) with pytest.raises(OwsError) as err: episode.replicate_single_episode_and_assets(replicate_single_episode_fixture) assert err.value.status == 403 def test_replicate_single_episode_and_assets_failure_read_only( mock_current_read_only_user, replicate_single_episode_fixture): """Test replicate single episode and create asset failure for read only user.""" with pytest.raises(OwsError) as err: episode.replicate_single_episode_and_assets(replicate_single_episode_fixture) assert err.value.status == 403 def test_replicate_episode_for_type_single_success( monkeypatch, mock_current_admin_user, episode_fixture_3, db_session_fixture, single_episode_replication_status_fixture, mock_replicate_single_episode_fixture, podcast_fixture_2, podcast_fixture_3, podcast_fixture_4, podcast_4_seasons_fixture ): """Test _replicate_episode_for_type_single success.""" podcast_fixture_2['seasons'] = [] podcast_fixture_3['seasons'] = [] podcast_fixture_4['seasons'] = podcast_4_seasons_fixture duplicated_episode_fixture = copy.deepcopy(episode_fixture_3) duplicated_episode_fixture['id'] = 8 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=single_episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=duplicated_episode_fixture) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=single_episode_replication_status_fixture) ) result = episode._replicate_episode_for_type_single( podcast_fixture_2, episode_fixture_3, [podcast_fixture_3, podcast_fixture_4], 1, 4 ) episode_replication_status_model.create_status.assert_called_once_with({ 'original_podcast_id': 2, 'original_episode_id': 7, 'podcast_id': 4, 'replication_type': 'single' }, db_session_fixture) episode_model.create_episode.assert_called_once_with({ 'podcast_id': 4, 'title': episode_fixture_3.get('title'), 'description': episode_fixture_3.get('description'), 'episode_type': episode_fixture_3.get('episode_type'), 'trailer_type': episode_fixture_3.get('trailer_type'), 'content': episode_fixture_3.get('content'), 'season_number': episode_fixture_3.get('season_number'), 'episode_number': episode_fixture_3.get('episode_number'), 'external_id': episode_fixture_3.get('external_id'), 'published_date': episode_fixture_3.get('published_date'), 'is_reviewed': False, 'season_id': 8, 'apple_id': 1234567890 }, db_session_fixture, 1) episode_replication_status_model.update_status.assert_called_once_with({ 'id': single_episode_replication_status_fixture['id'], 'episode_id': duplicated_episode_fixture['id'], 'status': 'episode-created' }, db_session_fixture) assert result == { 'original_podcast_id': 2, 'original_episode_id': 7, 'podcast_id': 4, 'episode_id': 8, 'feed_type': 'apple-subscription', 'original_podcast_feed_type': 'public-rss', 'status': 'episode-created' } @patch('podcast.logic.episode.g', spec=['current_user']) def test_replicate_episode_for_type_single_success_show_episodic( mock_g, monkeypatch, mock_current_admin_user, episode_fixture, db_session_fixture, single_episode_replication_status_fixture, mock_replicate_single_episode_fixture, podcast_fixture, podcast_fixture_3, podcast_fixture_4, podcast_4_seasons_fixture ): """Test _replicate_episode_for_type_single success when sgow type episodic.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 9 mock_g.return_value.id.return_value = 1 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=single_episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=duplicated_episode_fixture) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=single_episode_replication_status_fixture) ) result = episode._replicate_episode_for_type_single( podcast_fixture, episode_fixture, [podcast_fixture, podcast_fixture_3, podcast_fixture_4], 1, 3 ) episode_replication_status_model.create_status.assert_called_once_with({ 'original_podcast_id': 1, 'original_episode_id': 4, 'podcast_id': 3, 'replication_type': 'single' }, db_session_fixture) episode_model.create_episode.assert_called_once_with({ 'podcast_id': 3, 'title': episode_fixture.get('title'), 'description': episode_fixture.get('description'), 'episode_type': episode_fixture.get('episode_type'), 'trailer_type': episode_fixture.get('trailer_type'), 'content': episode_fixture.get('content'), 'season_number': episode_fixture.get('season_number'), 'episode_number': episode_fixture.get('episode_number'), 'external_id': episode_fixture.get('external_id'), 'published_date': episode_fixture.get('published_date'), 'is_reviewed': False, 'apple_id': None }, db_session_fixture, 1) episode_replication_status_model.update_status.assert_called_once_with({ 'id': single_episode_replication_status_fixture['id'], 'episode_id': duplicated_episode_fixture['id'], 'status': 'episode-created' }, db_session_fixture) assert result == { 'original_podcast_id': 1, 'original_episode_id': 4, 'podcast_id': 3, 'episode_id': 9, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'status': 'episode-created' } def test_replicate_episode_for_type_single_fails( monkeypatch, mock_current_admin_user, podcast_fixture, podcast_fixture_3, episode_fixture, mock_replicate_single_episode_fixture): """Test replicate_episode_for_type_single fails while creating episode replication status.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 mock_replicate_single_episode_fixture['error_message'] = 'Cannot record status:400-Bad Request' monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=None) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_episode_for_type_single( podcast_fixture, episode_fixture, [podcast_fixture, podcast_fixture_3], 1, 3 ) assert not episode_model.create_episode.called assert not episode_replication_status_model.update_status.called assert result == { 'original_podcast_id': 1, 'original_episode_id': 4, 'podcast_id': 3, 'episode_id': None, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'error_message': 'Cannot record status:400-Bad Request' } def test_replicate_episode_for_type_single_fails_creating_episode( monkeypatch, mock_current_admin_user, podcast_fixture, podcast_fixture_3, episode_fixture, mock_replicate_single_episode_fixture, single_episode_replication_status_fixture): """Test _replicate_episode_for_type_bulk fails while creating episode.""" duplicated_episode_fixture = copy.deepcopy(episode_fixture) duplicated_episode_fixture['id'] = 5 monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=single_episode_replication_status_fixture) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock( side_effect=OwsError.bad_request( message='Bad Request' ) ) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_episode_for_type_single( podcast_fixture, episode_fixture, [podcast_fixture, podcast_fixture_3], 1, 3 ) assert episode_replication_status_model.create_status.called assert episode_model.create_episode.called episode_replication_status_model.update_status.assert_called_once_with( {'id': 3, 'status': 'episode-creation-failed'}) assert result == { 'original_podcast_id': 1, 'original_episode_id': 4, 'podcast_id': 3, 'episode_id': None, 'feed_type': 'private-rss', 'original_podcast_feed_type': 'public-rss', 'error_message': 'Cannot create episode:400-Bad Request' } def test_replicate_episode_for_type_single_fails_invalid_podcast_id( monkeypatch, mock_current_admin_user, podcast_fixture, podcast_fixture_3, episode_fixture, mock_replicate_single_episode_fixture, single_episode_replication_status_fixture): """Test _replicate_episode_for_type_bulk fails if invalid podcast id.""" monkeypatch.setattr( episode_replication_status_model, 'create_status', MagicMock(return_value=None) ) monkeypatch.setattr( episode_model, 'create_episode', MagicMock(return_value=None) ) monkeypatch.setattr( episode_replication_status_model, 'update_status', MagicMock(return_value=None) ) result = episode._replicate_episode_for_type_single( podcast_fixture, episode_fixture, [podcast_fixture, podcast_fixture_3], 1, 100 ) assert not episode_replication_status_model.create_status.called assert not episode_model.create_episode.called assert not episode_replication_status_model.update_status.called assert result == { 'original_podcast_id': 1, 'original_episode_id': 4, 'episode_id': None, 'error_message': 'Podcast id value is invalid, podcast_id: 100' }