"""Unit tests for AdAction model.""" from datetime import datetime, timedelta, timezone from unittest.mock import patch from botocore.signers import CloudFrontSigner import pytest from podcast.models import ad_action from podcast.models import user as user_model from podcast.utils import exc from podcast.utils import signed_urls @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions(mock_get_feature_flag, output_cdn_fixture, mock_current_admin_user): """Test ad actions get.""" expire_date = datetime.now(timezone.utc) + timedelta(minutes=120) expires_at = int(expire_date.timestamp()) result_ad = ad_action.get_ad_actions() copy_url = 'https://output.com/fake/path.pdf' assert len(result_ad['items']) == 3 assert result_ad['items'][0]['title'] == 'Cool Ad' assert result_ad['items'][0]['copy_url_path'] == '{}?Policy=policy&Signature=signature&Key-Pair-Id=key-pair'.format( copy_url) assert signed_urls._sign_url.call_args[0][0] == copy_url assert isinstance(signed_urls._sign_url.call_args[0][1], CloudFrontSigner) statement = signed_urls._sign_url.call_args[0][2]['Statement'][0] assert statement['Resource'] == copy_url # expires_at can be off by a ms or two so relax assertion a bit assert statement['Condition']['DateLessThan']['AWS:EpochTime'] > expires_at - 5 assert statement['Condition']['DateLessThan']['AWS:EpochTime'] < expires_at + 5 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_due_date_filtered(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions filters out completed ad actions that have due dates older than two weeks.""" result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 3 ad_action.update_ad_action(5, {'due_date': datetime.now(timezone.utc) - timedelta(days=13)}) result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 4 assert result_ad['items'][1]['id'] == 5 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_archived_filtered(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions filters out ads that are archived early.""" result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 3 ad_id = result_ad['items'][0]['id'] ad_action.update_ad_action(ad_id, {'is_archived_early': True}) result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 2 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_virus_filtered(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions returns virus free filtered items.""" result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 3 ad_action.update_ad_action(4, {'is_virus_free': True}) result_ad = ad_action.get_ad_actions() assert len(result_ad['items']) == 4 assert result_ad['items'][1]['id'] == 4 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_archived_early(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions returns early archives.""" result_ad = ad_action.get_ad_actions(True) count = len(result_ad['items']) ad_action.update_ad_action(4, { 'is_virus_free': True, 'is_archived_early': True, 'status': 'completed', 'due_date': datetime.now()}) result_ad = ad_action.get_ad_actions(True) assert len(result_ad['items']) == count + 1 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_archived(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions returns archives.""" result_ad = ad_action.get_ad_actions(True) count = len(result_ad['items']) ad_action.update_ad_action(4, { 'is_virus_free': True, 'status': 'completed', 'due_date': datetime(2019, 1, 1)}) result_ad = ad_action.get_ad_actions(True) assert len(result_ad['items']) == count + 1 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_limit_offset(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions returns archives.""" ad_action.update_ad_action(4, { 'is_virus_free': True, 'status': 'completed', 'due_date': datetime(2019, 1, 1)}) result_ad = ad_action.get_ad_actions(True, False, 1, 1) assert result_ad['items'][0]['id'] == 5 assert result_ad['pagination']['total_records'] == 2 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_actions_only_show_mine(mock_get_feature_flag, mock_current_admin_user): """Test get ad actions returns archives.""" ad_action.update_ad_action(5, { 'created_by': 1, 'assignee_id': 1}) count = len(ad_action.get_ad_actions(True, True)['items']) ad_action.update_ad_action(5, { 'created_by': 2, 'assignee_id': 1}) result_ad = ad_action.get_ad_actions(True, True) assert len(result_ad['items']) == count + 1 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_action_no_asset_by_filename(mock_get_feature_flag, output_cdn_fixture): """Test get_action_no_asset_by_filename method.""" result = ad_action.get_ad_action_no_assets_by_filename('copies/unique-ad_copy-filename.pdf') assert result == { 'id': 4, 'campaign_id': '3', 'order_id': 'mp1', 'advertisement_id': 'mpad1', 'roll_type': 'mid', 'assignee_ids': [2], 'created_by': 2, 'created_date': datetime(2019, 1, 1, 0, 0), 'updated_date': datetime(2019, 1, 1, 0, 0), 'title': 'Unowned By User', 'copy_url_path': 'https://output.com/copies/unique-ad_copy-filename.pdf?Policy=policy&Signature=signature&Key' '-Pair-Id=key-pair', 'copy_filename': 'filename.pdf', 'copy_updated_date': datetime(2019, 1, 1, 0, 0), 'due_date': datetime(2019, 1, 1), 'status': 'new', 'rejection_reason': None, 'ad_read_url': None, 'requires_approval': True, 'is_archived_early': False, 'is_deleted': False, 'is_virus_free': False, 'updated_by': 2, 'is_copy_updated': False } @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_get_ad_action_show_level(mock_get_feature_flag, mock_current_podcast_level_user): """Test get ad action as a show level user.""" results = ad_action.get_ad_actions(False, False)['items'] assert len(results) == 3 ad_actions_ids = [result['id'] for result in results] ad_order_ids = [result['order_id'] for result in results] assert ad_actions_ids == [1, 6, 7] assert ad_order_ids == ['mp1', 'mp1', 'mp1'] @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=True) def test_get_ad_action_show_family_level(mock_get_feature_flag, mock_current_podcast_level_user): """Test get ad action as a show family level user.""" results = ad_action.get_ad_actions(False, False)['items'] assert len(results) == 3 ad_actions_ids = [result['id'] for result in results] ad_order_ids = [result['order_id'] for result in results] assert ad_actions_ids == [1, 6, 7] assert ad_order_ids == ['mp1', 'mp1', 'mp1'] def test_get_action_no_asset_by_filename_not_found(): """Test get_action_no_asset_by_filename raises 404 if not found.""" with pytest.raises(exc.OwsError) as err: ad_action.get_ad_action_no_assets_by_filename('non-existing-filename') assert err.value.status == 404 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_delete_ad_action_success_admin(mock_get_feature_flag, mock_current_admin_user): """Test delete ad action success for org admin.""" current_user = user_model.get_user_by_id(2) result = ad_action.delete_ad_action(1, current_user, False) assert len(ad_action.get_ad_actions()['items']) == 2 assert result['is_deleted'] is True assert result['updated_by'] == 2 def test_delete_ad_action_success_network_admin(mock_current_network_admin_user): """Test delete ad action success for network admin.""" current_user = user_model.get_user_by_id(6) result = ad_action.delete_ad_action(1, current_user, True) assert result['is_deleted'] is True assert result['updated_by'] == 6 def test_delete_ad_action_success_network_admin_no_check(): """Test delete ad action success for network admin and extra check not needed.""" current_user = user_model.get_user_by_id(10) result = ad_action.delete_ad_action(1, current_user, True) assert result['is_deleted'] is True assert result['updated_by'] == 10 def test_delete_ad_action_failure(): """Test delete ad action fails for wrong ad read id for admin user.""" current_user = user_model.get_user_by_id(2) with pytest.raises(exc.OwsError): ad_action.delete_ad_action(10000, current_user, False) assert len(ad_action.get_ad_actions()['items']) == 1 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_delete_ad_action_failure_network_admin(mock_get_feature_flag): """Test delete ad action fails for network admin user not allowed to access network.""" current_user = user_model.get_user_by_id(12) with pytest.raises(exc.OwsError): ad_action.delete_ad_action(1, current_user, True) assert len(ad_action.get_ad_actions()['items']) == 1 @patch('podcast.utils.feature_flag_utils.get_feature_flag', return_value=False) def test_create_ad_action(mock_get_feature_flag, ad_read_fixture, mock_current_admin_user, get_assets_mock): """Test create ad action.""" initial_length = len(ad_action.get_ad_actions()['items']) ad_read_fixture['due_date'] = datetime(2019, 1, 1) ad_read_fixture['is_virus_free'] = True ad_read_fixture['assignee_ids'] = [2, 3, 4, 5, 6] result = ad_action.create_ad_action(ad_read_fixture) assert result['id'] == 10 assert result['status'] == 'new' assert result['created_by'] == 2 assert result['updated_by'] == 2 assert result['assignee_ids'] == [2, 3, 4, 5, 6] assert result['ad_read_url'] is None assert len(ad_action.get_ad_actions()['items']) == initial_length + 1 def test_has_duplicate_request(): """Test has duplicate.""" assert ad_action.has_duplicate_request('1', 'mp1', 'mpad1') assert ad_action.has_duplicate_request('1', 'mp1', '20') is None ad_action.update_ad_action(1, {'is_deleted': True}) assert ad_action.has_duplicate_request('1', 'mp1', 'mpad1') is None ad_action.update_ad_action(1, {'is_deleted': False, 'status': 'completed'}) assert ad_action.has_duplicate_request('1', 'mp1', 'mpad1') is None def test_update_ad_action(ad_read_fixture, mock_current_admin_user): """Test update ad action.""" ad_read_fixture['due_date'] = datetime(2019, 1, 1) result = ad_action.create_ad_action(ad_read_fixture) assert result['id'] == 10 updated = ad_action.update_ad_action( result['id'], { 'status': 'rejected', 'is_virus_free': True } ) assert updated['status'] == 'rejected' def test_update_ad_action_assignees_and_due_date(ad_read_fixture, mock_current_admin_user): """Test uodate ad action assigness and due date.""" new_due_date = datetime(2019, 1, 2) ad_read_fixture['due_date'] = new_due_date updated = ad_action.update_ad_action( 1, { 'assignee_ids': [2, 3, 4, 5, 6], 'due_date': new_due_date } ) assert updated['assignee_ids'] == [2, 3, 4, 5, 6] assert updated['due_date'] == new_due_date def test_due_in(ad_read_fixture, mock_current_admin_user): """Test ad action due in.""" now = datetime.utcnow() in_24_hours = (now + timedelta(hours=24)) ad_action.update_ad_action(1, {'due_date': in_24_hours}) assert len(ad_action.due_in(24)) == 1 assert len(ad_action.due_in(23)) == 0 assert len(ad_action.due_in(25)) == 0 yesterday = (now - timedelta(hours=24)) ad_action.update_ad_action(1, {'due_date': yesterday}) assert len(ad_action.due_in(-24)) == 1 ad_action.update_ad_action(1, {'status': 'completed'}) assert len(ad_action.due_in(24)) == 0