"""Unit tests for Episode model.""" import datetime from dateutil.tz import tzutc from oto import status import pytest from sqlalchemy.exc import IntegrityError from podcast.connectors import mysql from podcast.constants import api as api_const from podcast.models import episode from podcast.utils import exc from tests.utils import db_operations def test_get_episodes(): """Test episode model returns paginated data when seeded.""" limit = 0 offset = 0 podcast_id = 1 result_episodes = episode.get_episodes(podcast_id, limit, offset) assert len(result_episodes['items']) == 3 result_episode = result_episodes['items'][2] assert result_episode['id'] == db_operations.podcast_data[0]['id'] assert result_episode['title'] == db_operations.podcast_data[0]['title'] def test_get_episodes_draft(output_cdn_fixture): """Test episode model returns drafts.""" limit = 0 offset = 0 filter_by_state = 'DRAFT' podcast_id = 1 result_episodes = episode.get_episodes(podcast_id, limit, offset, filter_by_state) assert len(result_episodes['items']) == 2 def test_get_episodes_published(): """Test episode model returns drafts.""" limit = 0 offset = 0 filter_by_state = 'DONE' podcast_id = 1 result_episodes = episode.get_episodes(podcast_id, limit, offset, filter_by_state) assert len(result_episodes['items']) == 1 def test_get_episodes_start_date(): """Test episode model returns after start date.""" limit = 0 offset = 0 podcast_id = 1 result_episodes = episode.get_episodes(podcast_id, limit, offset, 'ALL', order_by='published_date', sort_order='asc', start_date='2018-02-02') assert len(result_episodes['items']) == 1 def test_get_episodes_end_date(): """Test episode model returns before end date.""" limit = 0 offset = 0 podcast_id = 1 result_episodes = episode.get_episodes(podcast_id, limit, offset, 'ALL', order_by='published_date', sort_order='asc', start_date='2019-02-02') assert len(result_episodes['items']) == 1 def test_get_episodes_sorting_by_title(): """Test that episode model sorts episodes.""" podcast_id = 1 result_episodes = episode.get_episodes( podcast_id=podcast_id, order_by='title', sort_order='desc' ) assert len(result_episodes['items']) == 3 assert result_episodes['items'][0]['title'] == 'title2' assert result_episodes['items'][1]['title'] == 'title four' assert result_episodes['items'][2]['title'] == 'title' result_episodes = episode.get_episodes( podcast_id=podcast_id, order_by='title', sort_order='asc' ) assert result_episodes['items'][0]['title'] == 'title' assert result_episodes['items'][1]['title'] == 'title four' assert result_episodes['items'][2]['title'] == 'title2' def test_get_episodes_by_mp_ids(): """Test can get many episodes by mp ids.""" results = episode.get_episodes_by_megaphone_ids([ '6bf5bea4-a1d4-11e6-8dea-b334e2aa4880', ]) assert len(results['items']) == 1 def test_get_episode_by_id(): """Test episode model return data by episode_id.""" episode_id = 1 result = episode.get_episode_by_id(episode_id) assert result['id'] == episode_id def test_get_episode_by_id_not_found(): """Test episode model raises if episode_id doesn't exist.""" episode_id = 999 with pytest.raises(exc.OwsError) as err: episode.get_episode_by_id(episode_id) assert err.value.status == 404 def test_get_episodes_empty(): """Test not found response when there is no episodes data.""" podcast_id = 100 result = episode.get_episodes(podcast_id) assert len(result['items']) == 0 def test_get_episodes_with_insertion_points(): """Test that get_episodes produces insertion_points.""" podcast_id = 1 result = episode.get_episodes(podcast_id) assert len(result['items']) > 0 assert all( isinstance(item['insertion_points'], list) for item in result['items']) def test_get_episode_by_id_with_insertion_points(): """Test that get_episode_by_id produces insertion_points.""" episode_id = 1 result = episode.get_episode_by_id(episode_id) assert len(result['insertion_points']) == 3 def test_get_episode_computed_status(mock_current_admin_user): """Test that computed_status property works correctly.""" podcast_id = 1 date_before_now = (datetime.datetime.now() - datetime.timedelta(hours=1)).isoformat() episode_data = dict( id=555, podcast_id=podcast_id, title='title', description='description', published_date=date_before_now, draft=False ) episode.create_episode(episode_data) result = episode.get_episode_by_id(555) assert result['status'] == api_const.EPISODE_STATUS_PUBLISHED def test_search_episodes(): """Test search on episodes.""" result = episode.search_episodes('title', [1, 2]) assert len(result) == 1 result_episode = result[0] assert result_episode['id'] == db_operations.episode_data[0]['id'] assert result_episode['title'] == db_operations.episode_data[0]['title'] def test_create_episode(mock_current_admin_user, episode_fixture): """Test that a episode can be created.""" del episode_fixture['id'] created_episode = episode.create_episode(episode_fixture) assert created_episode['id'] == 5 assert created_episode['external_id'] is None assert created_episode['is_reviewed'] is True def test_create_episode_with_external_id(mock_current_admin_user, episode_fixture): """Test that a episode can be created with external_id.""" episode_fixture['external_id'] = 'test_external_id' del episode_fixture['id'] created_episode = episode.create_episode(episode_fixture) assert created_episode['id'] == 5 assert created_episode['external_id'] == episode_fixture['external_id'] def test_create_episode_empty_body(): """Test that a episode requires fields to be created.""" data = {} with pytest.raises(exc.OwsError) as err: episode.create_episode(data) assert err.value.status == 400 def test_create_episode_with_insertion_points(mock_current_admin_user, episode_fixture): """Test that created episode involves insertion points' empty list.""" del episode_fixture['id'] created_episode = episode.create_episode(episode_fixture) assert isinstance(created_episode['insertion_points'], list) assert not created_episode['insertion_points'] def test_create_html_cleaned(monkeypatch, mock_current_admin_user): """Test create episode html stripped.""" episode_data = dict( id=33, podcast_id=1, title='Episode Title', description='' ) result = episode.create_episode(episode_data) assert result['description'] == '<script>alert("hi")</script>' def test_update_html_cleaned(monkeypatch, mock_current_admin_user): """Test update episode html stripped.""" podcast_id = 1 episode_id = 1 episode_data = dict( id=episode_id, podcast_id=podcast_id, title='Episode Title', description='', megaphone_id='f64b0f8e-ea77-11e9-9831-df1b1d22b24a' ) result = episode.update_episode(episode_id, episode_data) assert result['description'] == '<script>alert("hi")</script>' def test_clean_episode_input_data(): """Test that timestamp is converting correctly.""" data = {'published_date': '2019-12-26T16:52:01.000Z'} validated = episode._clean_episode_input_data(data) assert validated['published_date'] == datetime.datetime(2019, 12, 26, 16, 52, 1, tzinfo=tzutc()) def test_create_episode_published_date(mock_current_admin_user, episode_fixture): """Test that published date is correctly inserted.""" episode_fixture['published_date'] = '2019-12-26T16:52:01.000Z' del episode_fixture['id'] created_episode = episode.create_episode(episode_fixture) assert isinstance(created_episode['published_date'], datetime.datetime) def test_update_episode(mock_current_admin_user): """Test that a episode can be updated.""" episode_id = 1 data = dict( title='title 2', description='description 2') updated_episode = episode.update_episode(episode_id, data) assert updated_episode['title'] == data['title'] assert updated_episode['description'] == data['description'] def test_update_episode_for_external_id(mock_current_admin_user): """Test that a episode can be updated for external_id.""" episode_id = 1 data = dict( title='title 2', description='description 2', external_id='test_external_id') updated_episode = episode.update_episode(episode_id, data) assert updated_episode['external_id'] == data['external_id'] data['external_id'] = None updated_episode = episode.update_episode(episode_id, data) assert updated_episode['external_id'] is None def test_update_episode_for_is_reviewed(mock_current_admin_user): """Test that a episode can be updated for is_reviewed.""" episode_id = 2 data = dict( title='title 2', description='description 2', is_reviewed=True) updated_episode = episode.update_episode(episode_id, data) assert updated_episode['is_reviewed'] is True def test_update_episode_empty_body(): """Test that a podcast requires fields to be updated.""" episode_id = 2 data = {} with pytest.raises(exc.OwsError) as err: episode.update_episode(episode_id, data) assert err.value.status == 400 def test_update_episode_null_podcast_id(mock_current_admin_user): """Test that episode update will fail with new podcast_id is None.""" episode_id = 1 data = { 'podcast_id': None } with pytest.raises(IntegrityError) as err: episode.update_episode(episode_id, data) assert err.type == IntegrityError def test_update_episode_with_insertion_points(mock_current_admin_user): """Test that updated episode involves insetion points.""" episode_id = 1 data = dict( title='title 2', description='description 2') result = episode.update_episode(episode_id, data) assert result['insertion_points'] def test_update_episode_published_date(mock_current_admin_user): """Test that published date is correctly updated.""" episode_id = 1 data = dict( published_date='2019-12-26T16:52:01.000Z') result = episode.update_episode(episode_id, data) assert isinstance(result['published_date'], datetime.datetime) def test_delete_episode(mock_current_admin_user, output_cdn_fixture): """Test delete episode.""" episode_id = 1 podcast_id = 1 result_episode = episode.get_episodes(podcast_id) starting_count = len(result_episode['items']) with mysql.pod_db_session() as session: result = episode.delete_episode(podcast_id, episode_id, session) assert result['id'] == podcast_id result_episode = episode.get_episodes(podcast_id) assert len(result_episode['items']) == starting_count - 1 def test_delete_episode_not_found(): """Test delete episode with not found response.""" episode_id = 10 podcast_id = 10 with mysql.pod_db_session() as session: with pytest.raises(exc.OwsError) as err: episode.delete_episode(podcast_id, episode_id, session) assert err.value.status == 404 def test_delete_episode_with_insertion_points(mock_current_admin_user): """Test delete episode returns insertion points as well.""" episode_id = 1 podcast_id = 1 with mysql.pod_db_session() as session: result = episode.delete_episode(podcast_id, episode_id, session) assert result['insertion_points'] def test_soft_delete_doesnt_fetch_rows(mock_current_admin_user, output_cdn_fixture): """Test with is_deleted=True rows are not fetched.""" podcast_id = 1 episode_id = 1 result = episode.get_episode_by_id(episode_id) assert result['id'] == episode_id result = episode.get_episodes(podcast_id) starting_count = len(result['items']) episode.update_episode(episode_id, {'is_deleted': True}) with pytest.raises(exc.OwsError) as err: episode.get_episode_by_id(episode_id) assert err.value.status == 404 result = episode.get_episodes(podcast_id) assert len(result['items']) == starting_count - 1 def test_create_planned_inventory(mock_current_admin_user, planned_inventory_fixture): """Test create planned inventory inserts.""" podcast_id = 1 planned_inventory_fixture['dates'].append('2019-11-22T16:52:01.000Z') starting_num_episodes = len(episode.get_episodes(podcast_id)['items']) episode.create_planned_inventory(1, planned_inventory_fixture) episodes = episode.get_episodes(podcast_id)['items'] finished_num_episodes = len(episodes) assert finished_num_episodes == starting_num_episodes + 2 assert episodes[3]['title'] == 'Draft Episode for 11/22/2019' assert episodes[3]['planned_mid_roll_count'] == 0 assert episodes[3]['planned_post_roll_count'] == 2 assert episodes[3]['planned_pre_roll_count'] == 1 assert episodes[4]['title'] == 'Draft Episode for 11/26/2019' def test_create_planned_inventory_no_podcast(mock_current_admin_user, planned_inventory_fixture): """Test create planned inventory has no podcast.""" with pytest.raises(exc.OwsError) as err: episode.create_planned_inventory(None, planned_inventory_fixture) assert err.value.status == status.BAD_REQUEST def test_create_planned_inventory_no_data(mock_current_admin_user): """Test create planned inventory has no data.""" podcast_id = 1 with pytest.raises(exc.OwsError) as err: episode.create_planned_inventory(podcast_id, None) assert err.value.status == status.BAD_REQUEST def test_get_episodes_by_ids(): """Test can get many episodes by ids.""" results = episode.get_episodes_by_ids([1, 2]) assert len(results['items']) == 2 def test_get_num_episodes_for_podcast_ids(): """Test get number of episodes for podcast ids.""" results = episode.get_num_episodes_for_podcast_ids([1, 2]) assert results == {1: 1} def test_episodes_gone_live_within_hour(mock_current_admin_user): """Test episodes gone live within an hour.""" now = datetime.datetime.utcnow() half_hour_ago = (now - datetime.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%S.000Z') assert len(episode.episodes_gone_live_within_hour()) == 0 episode.update_episode(1, { 'published_date': half_hour_ago, 'draft': False }) assert len(episode.episodes_gone_live_within_hour()) == 1 hour_ago = (now - datetime.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%M:%S.000Z') episode.update_episode(1, { 'published_date': hour_ago, 'draft': False }) assert len(episode.episodes_gone_live_within_hour()) == 0 episode.update_episode(1, { 'published_date': now.strftime('%Y-%m-%dT%H:%M:%S.000Z'), 'draft': False }) assert len(episode.episodes_gone_live_within_hour()) == 1 in_one_min = (now + datetime.timedelta(minutes=1)).strftime('%Y-%m-%dT%H:%M:%S.000Z') episode.update_episode(1, { 'published_date': in_one_min, 'draft': False }) assert len(episode.episodes_gone_live_within_hour()) == 0 def test_episodes_going_live_tomorrow(mock_current_admin_user): """Test episodes going live tomorrow.""" episode.update_episode(1, {'draft': False}) now = datetime.datetime.utcnow() episode.update_episode(1, {'published_date': now.strftime('%Y-%m-%dT%H:%M:%S.000Z')}) assert len(episode.episodes_going_live_tomorrow()) == 0 in_24_hours = (now + datetime.timedelta(hours=24)).strftime('%Y-%m-%dT%H:%M:%S.000Z') episode.update_episode(1, {'published_date': in_24_hours}) assert len(episode.episodes_going_live_tomorrow()) == 1 in_23_hours = (now + datetime.timedelta(hours=23)).strftime('%Y-%m-%dT%H:%M:%S.000Z') episode.update_episode(1, {'published_date': in_23_hours}) assert len(episode.episodes_going_live_tomorrow()) == 0