def test_action_delete_job(self): to_get_deleted = [ JobFactory.create(status=Job.DRAFT), JobFactory.create(status=Job.DRAFT), ] to_remain = [ JobFactory(status=Job.CANCELED), JobFactory(status=Job.COMPLETED), JobFactory(status=Job.PUBLISHED), ] queryset = Job.objects.filter(id__in=[x.id for x in to_get_deleted + to_remain]) request = Mock() request.user = UserFactory.create() with patch.multiple('snippets.base.admin.adminmodels.messages', warning=DEFAULT_MOCK, success=DEFAULT_MOCK) as message_mocks: JobAdmin(Job, None).action_delete_job(request, queryset) self.assertEqual( set(Job.objects.all()), set(to_remain) ) self.assertTrue(message_mocks['warning'].called) self.assertTrue(message_mocks['success'].called)
def test_action_cancel_job(self): to_get_canceled = [ JobFactory.create(status=Job.PUBLISHED), JobFactory.create(status=Job.SCHEDULED), ] already_cancelled = JobFactory(status=Job.CANCELED) completed = JobFactory(status=Job.COMPLETED) JobFactory.create_batch(2, status=Job.DRAFT) queryset = Job.objects.filter(id__in=[ to_get_canceled[0].id, to_get_canceled[1].id, already_cancelled.id, completed.id, ]) request = Mock() request.user = UserFactory.create() with patch.multiple('snippets.base.admin.adminmodels.messages', warning=DEFAULT_MOCK, success=DEFAULT_MOCK) as message_mocks: JobAdmin(Job, None).action_cancel_job(request, queryset) self.assertEqual( set(Job.objects.filter(status=Job.CANCELED)), set(to_get_canceled + [already_cancelled]) ) self.assertTrue(message_mocks['warning'].called) self.assertTrue(message_mocks['success'].called)
def test_base(self): job1, job2 = JobFactory.create_batch(2) job4 = JobFactory.create(publish_end=datetime(2019, 2, 2)) job3 = JobFactory.create(publish_start=datetime(2019, 1, 1), publish_end=datetime(2019, 2, 2)) filtr = JobFilter(QueryDict(query_string='only_scheduled=all'), queryset=models.Job.objects.all()) self.assertEqual(set([job1, job2, job3, job4]), set(filtr.qs))
def test_locale(self): job = JobFactory.create(snippet__locale='fr') JobFactory.create(snippet__locale='de') JobFactory.create(snippet__locale='xx') locale = job.snippet.locale filtr = JobFilter( QueryDict(query_string=f'only_scheduled=all&locale={locale.id}'), queryset=models.Job.objects.all()) self.assertEqual(set([job]), set(filtr.qs))
def test_only_scheduled_true(self): job1 = JobFactory.create(publish_end=datetime(2019, 2, 2)) job2 = JobFactory.create(publish_end=datetime(2019, 2, 2)) job3 = JobFactory.create(publish_start=datetime(2019, 2, 2), publish_end=datetime(2019, 2, 3)) JobFactory.create(publish_start=None, publish_end=None) filtr = JobFilter(QueryDict(query_string='only_scheduled=true'), queryset=models.Job.objects.all()) self.assertEqual(set([job1, job2, job3]), set(filtr.qs))
def test_clean(self): job_clean = JobFactory.create( publish_start=datetime.utcnow() + timedelta(days=1), publish_end=datetime.utcnow() + timedelta(days=2)) job_clean.clean() job_dirty = JobFactory.create( publish_start=datetime.utcnow() + timedelta(days=3), publish_end=datetime.utcnow() + timedelta(days=2)) self.assertRaisesMessage( ValidationError, 'Publish start must come before publish end.', job_dirty.clean)
def test_get_admin_url(self): job = JobFactory.create() self.assertEqual( job.get_admin_url(), f'http://example.com/admin/base/job/{job.id}/change/') self.assertEqual(job.get_admin_url(full=False), f'/admin/base/job/{job.id}/change/')
def test_change_status_to_completed(self): job = JobFactory.create(status=Job.DRAFT) with patch('snippets.base.models.datetime') as datetime_mock: datetime_mock.utcnow.return_value = datetime(2019, 1, 1, 0, 0) job.change_status(status=Job.COMPLETED, user=None, send_slack=False) job.refresh_from_db() self.assertEqual(job.status, Job.COMPLETED) self.assertEqual(job.completed_on, datetime(2019, 1, 1, 0, 0))
def test_channels(self): job = JobFactory.create(targets=[ TargetFactory.create(on_release=True), TargetFactory.create(on_beta=True, on_nightly=True), TargetFactory.create(on_release=False, on_esr=False, on_aurora=False, on_beta=False, on_nightly=False), ]) self.assertTrue(job.channels, set(['release', 'beta', 'nightly']))
def test_change_status(self): job = JobFactory.create(status=Job.DRAFT) with patch('snippets.base.models.slack') as slack_mock: with patch('snippets.base.models.LogEntry') as log_entry_mock: job.change_status(status=Job.SCHEDULED, user=job.creator, send_slack=True) job.refresh_from_db() self.assertEqual(job.status, Job.SCHEDULED) log_entry_mock.objects.log_action.assert_called() slack_mock._send_slack.assert_called()
def test_render_always_eval_to_false(self): job = JobFactory.create(weight=10, campaign__slug='demo-campaign', targets=[ TargetFactory(on_release=False, on_beta=False, on_esr=False, on_aurora=False, on_nightly=True, jexl_expr='(la==lo)'), ]) job.snippet.render = Mock() job.snippet.render.return_value = {} generated_output = job.render(always_eval_to_false=True) self.assertEqual(generated_output['targeting'], '(la==lo) && false')
def test_render(self): self.maxDiff = None job = JobFactory.create(weight=10, campaign__slug='demo-campaign', targets=[ TargetFactory(on_release=False, on_beta=False, on_esr=False, on_aurora=False, on_nightly=True, jexl_expr='(la==lo)'), TargetFactory(on_release=False, on_beta=True, on_esr=False, on_aurora=False, on_nightly=True), TargetFactory(on_release=False, on_beta=True, on_esr=False, on_aurora=False, on_nightly=True, jexl_expr='foo==bar'), ]) snippet_render = { 'template': 'simple_snippet', 'template_version': 'xx.xx', 'content': { 'block_button_text': 'Block me', 'text': 'This is text [[job_id]]', } } expected_output = copy.deepcopy(snippet_render) expected_output.update({ 'id': str(job.id), 'weight': 10, 'campaign': 'demo-campaign', 'targeting': '(la==lo) && foo==bar', 'content': { 'block_button_text': 'Block me', 'text': f'This is text {job.id}', } }) job.snippet.render = Mock() job.snippet.render.return_value = snippet_render generated_output = job.render() self.assertEqual(generated_output, expected_output)
def test_duplicate(self): user = UserFactory.create() job = JobFactory.create(status=Job.PUBLISHED, metric_impressions=500, metric_clicks=500, metric_blocks=500, completed_on=datetime.utcnow()) duplicate_job = job.duplicate(user) job.refresh_from_db() for attr in ['id', 'creator', 'created', 'modified', 'uuid']: self.assertNotEqual(getattr(job, attr), getattr(duplicate_job, attr)) self.assertEqual(duplicate_job.status, Job.DRAFT) self.assertEqual(duplicate_job.metric_impressions, 0) self.assertEqual(duplicate_job.metric_clicks, 0) self.assertEqual(duplicate_job.metric_blocks, 0) self.assertEqual(duplicate_job.completed_on, None)
def test_name(self): JobFactory.create(id=2990, snippet__id=20000, snippet__name='foo 1') JobFactory.create(id=2991, snippet__id=20001, snippet__name='foo 2') job = JobFactory.create(id=2992, snippet__id=20002, snippet__name='bar 1') JobFactory.create(id=2993, snippet__name='foo lala foo') filtr = JobFilter( QueryDict(query_string='only_scheduled=all&name=bar'), queryset=models.Job.objects.all()) self.assertEqual(set([job]), set(filtr.qs)) # Test search with Job ID filtr = JobFilter( QueryDict(query_string=f'only_scheduled=all&name={job.id}'), queryset=models.Job.objects.all()) self.assertEqual(set([job]), set(filtr.qs)) # Test search with Snippet ID filtr = JobFilter(QueryDict( query_string=f'only_scheduled=all&name={job.snippet.id}'), queryset=models.Job.objects.all()) self.assertEqual(set([job]), set(filtr.qs))
def test_render_client_limits(self): # Combined job = JobFactory.create( client_limit_lifetime=100, client_limit_per_hour=10, client_limit_per_month=100, campaign__slug='demo_campaign', ) expected_output = { 'id': str(job.id), 'weight': 100, 'campaign': 'demo_campaign', 'targeting': '', 'frequency': { 'lifetime': 100, 'custom': [{ 'period': 3600000, 'cap': 10 }, { 'period': 2592000000, 'cap': 100 }] } } job.snippet.render = Mock() job.snippet.render.return_value = {} generated_output = job.render() self.assertEqual(generated_output, expected_output) # Lifetime limit only job = JobFactory.create( client_limit_lifetime=100, campaign__slug='demo_campaign_1', ) expected_output = { 'id': str(job.id), 'weight': 100, 'campaign': 'demo_campaign_1', 'targeting': '', 'frequency': { 'lifetime': 100, } } job.snippet.render = Mock() job.snippet.render.return_value = {} generated_output = job.render() self.assertEqual(generated_output, expected_output) # Custom limits only # Lifetime limit only job = JobFactory.create( client_limit_per_week=9, client_limit_per_fortnight=99, campaign__slug='demo_campaign_2', ) expected_output = { 'id': str(job.id), 'weight': 100, 'campaign': 'demo_campaign_2', 'targeting': '', 'frequency': { 'custom': [{ 'period': 604800000, 'cap': 9 }, { 'period': 1296000000, 'cap': 99 }] } } job.snippet.render = Mock() job.snippet.render.return_value = {} generated_output = job.render() self.assertEqual(generated_output, expected_output)
def test_base(self): JobFactory.create( id=1000, status=Job.COMPLETED, publish_start=date(2020, 1, 1), completed_on=date(2020, 1, 10), ) JobFactory.create(id=2000) JobDailyPerformance(date=date(2020, 1, 9), impression=100, job=Job.objects.get(id=1000)).save() rows = [ [ { 'message_id': '1000', 'event_context': '{}', 'event': 'CLICK_BUTTON', 'channel': 'release', 'country_code': 'GR', 'counts': 5, 'no_clients': 2, 'no_clients_total': 0, }, { 'message_id': '1000', 'event_context': '{}', 'event': 'IMPRESSION', 'channel': 'release', 'country_code': 'ES', 'counts': 30, 'no_clients': 10, 'no_clients_total': 0, }, { 'message_id': '1000', 'event_context': '{}', 'event': 'IMPRESSION', 'channel': 'release', 'country_code': 'IT', 'counts': 50, 'no_clients': 20, 'no_clients_total': 0, }, { 'message_id': '1000', 'event_context': '{}', 'event': 'BLOCK', 'channel': 'releases', 'country_code': 'UK', 'counts': 23, 'no_clients': 9, 'no_clients_total': 0, }, { 'message_id': '1000', 'event_context': '{}', 'event': 'BLOCK', 'channel': 'beta-test', 'country_code': 'SW', 'counts': 27, 'no_clients': 50, 'no_clients_total': 0, }, # To be discarded { 'message_id': '500', 'event_context': '{}', 'event': 'CLICK', 'channel': 'demo', 'country_code': 'GR', 'counts': 5, 'no_clients': 10, 'no_clients_total': 0, }, { 'message_id': '1000', 'event_context': '', 'event': 'CLICK', 'channel': 'release', 'country_code': 'GR', 'counts': 6, 'no_clients': 10, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': '{}', 'event': 'CLICK_BUTTON', 'additional_properties': '{"value": "scene1-button-learn-more", "foo": "bar"}', 'channel': 'release', 'country_code': 'GR', 'counts': 44, 'no_clients': 33, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': '{}', 'event': 'CLICK_BUTTON', 'channel': 'release', 'country_code': 'BG', 'counts': 3, 'no_clients': 10, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': '{}', 'event': 'CLICK_BUTTON', 'channel': 'release', 'country_code': 'AL', 'counts': 1, 'no_clients': 10, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': 'conversion-subscribe-activation', 'event': 'CLICK_BUTTON', 'additional_properties': '{"foo": "bar"}', 'channel': 'release', 'country_code': 'GR', 'counts': 5, 'no_clients': 8, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': 'subscribe-error', 'event': 'CLICK_BUTTON', 'additional_properties': '{"foo": "bar"}', 'channel': 'release', 'country_code': 'GR', 'counts': 3, 'no_clients': 4, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': 'subscribe-success', 'event': 'CLICK_BUTTON', 'channel': 'release', 'country_code': 'ERROR', 'counts': 9, 'no_clients': 57, 'no_clients_total': 0, }, { 'message_id': '2000', 'event_context': '', 'event': 'DISMISS', 'channel': 'beta', 'country_code': 'ERROR', 'counts': 1, 'no_clients': 1, 'no_clients_total': 0, }, ], [ { 'message_id': '1000', 'event_context': '', 'event': 'IMPRESSION', 'channel': 'release', 'country_code': 'ES', 'counts': 0, 'no_clients': 0, 'no_clients_total': 232, }, { 'message_id': '1000', 'event_context': '', 'event': 'IMPRESSION', 'channel': 'release', 'country_code': 'IT', 'counts': 0, 'no_clients': 0, 'no_clients_total': 421, }, ], ] with patch('snippets.base.etl.redash_rows') as redash_rows_mock: redash_rows_mock.side_effect = rows result = etl.update_job_metrics(date(2020, 1, 10)) self.assertEqual(redash_rows_mock.call_count, 2) self.assertTrue(result) self.assertEqual(JobDailyPerformance.objects.count(), 3) jdp1 = JobDailyPerformance.objects.filter( job_id=1000).order_by('-id')[0] self.assertEqual(jdp1.impression, 80) self.assertEqual(jdp1.impression_no_clients, 30) self.assertEqual(jdp1.impression_no_clients_total, 653) self.assertEqual(jdp1.click, 11) self.assertEqual(jdp1.click_no_clients, 12) self.assertEqual(jdp1.click_no_clients_total, 0) self.assertEqual(jdp1.block, 50) self.assertEqual(jdp1.block_no_clients, 59) self.assertEqual(jdp1.block_no_clients_total, 0) self.assertEqual(jdp1.dismiss, 0) self.assertEqual(jdp1.dismiss_no_clients, 0) self.assertEqual(jdp1.dismiss_no_clients_total, 0) self.assertEqual(jdp1.go_to_scene2, 0) self.assertEqual(jdp1.go_to_scene2_no_clients, 0) self.assertEqual(jdp1.go_to_scene2_no_clients_total, 0) self.assertEqual(jdp1.subscribe_error, 0) self.assertEqual(jdp1.subscribe_error_no_clients, 0) self.assertEqual(jdp1.subscribe_error_no_clients_total, 0) self.assertEqual(jdp1.subscribe_success, 0) self.assertEqual(jdp1.subscribe_success_no_clients, 0) self.assertEqual(jdp1.subscribe_success_no_clients_total, 0) self.assertEqual(jdp1.other_click, 0) self.assertEqual(jdp1.other_click_no_clients, 0) self.assertEqual(jdp1.other_click_no_clients_total, 0) self.assertEqual(len(jdp1.details), 5) for detail in [{ 'event': 'click', 'counts': 11, 'channel': 'release', 'country': 'GR', 'no_clients': 12, 'no_clients_total': 0 }, { 'event': 'impression', 'counts': 30, 'channel': 'release', 'country': 'ES', 'no_clients': 10, 'no_clients_total': 232 }, { 'event': 'impression', 'counts': 50, 'channel': 'release', 'country': 'IT', 'no_clients': 20, 'no_clients_total': 421 }, { 'event': 'block', 'counts': 23, 'channel': 'release', 'country': 'UK', 'no_clients': 9, 'no_clients_total': 0 }, { 'event': 'block', 'counts': 27, 'channel': 'beta', 'country': 'SW', 'no_clients': 50, 'no_clients_total': 0 }]: self.assertTrue(detail in jdp1.details) jdp2 = JobDailyPerformance.objects.get(job_id=2000) self.assertEqual(jdp2.impression, 0) self.assertEqual(jdp2.impression_no_clients, 0) self.assertEqual(jdp2.impression_no_clients_total, 0) self.assertEqual(jdp2.click, 5) self.assertEqual(jdp2.click_no_clients, 8) self.assertEqual(jdp2.click_no_clients_total, 0) self.assertEqual(jdp2.block, 0) self.assertEqual(jdp2.block_no_clients, 0) self.assertEqual(jdp2.block_no_clients_total, 0) self.assertEqual(jdp2.dismiss, 1) self.assertEqual(jdp2.dismiss_no_clients, 1) self.assertEqual(jdp2.dismiss_no_clients_total, 0) self.assertEqual(jdp2.go_to_scene2, 44) self.assertEqual(jdp2.go_to_scene2_no_clients, 33) self.assertEqual(jdp2.go_to_scene2_no_clients_total, 0) self.assertEqual(jdp2.subscribe_error, 3) self.assertEqual(jdp2.subscribe_error_no_clients, 4) self.assertEqual(jdp2.subscribe_error_no_clients_total, 0) self.assertEqual(jdp2.subscribe_success, 9) self.assertEqual(jdp2.subscribe_success_no_clients, 57) self.assertEqual(jdp2.subscribe_success_no_clients_total, 0) self.assertEqual(jdp2.other_click, 4) self.assertEqual(jdp2.other_click_no_clients, 20) self.assertEqual(jdp2.other_click_no_clients_total, 0) self.assertEqual(len(jdp2.details), 7) for detail in [{ 'event': 'go_to_scene2', 'counts': 44, 'channel': 'release', 'country': 'GR', 'no_clients': 33, 'no_clients_total': 0 }, { 'event': 'other_click', 'counts': 3, 'channel': 'release', 'country': 'BG', 'no_clients': 10, 'no_clients_total': 0 }, { 'event': 'other_click', 'counts': 1, 'channel': 'release', 'country': 'AL', 'no_clients': 10, 'no_clients_total': 0 }, { 'event': 'click', 'counts': 5, 'channel': 'release', 'country': 'GR', 'no_clients': 8, 'no_clients_total': 0 }, { 'event': 'subscribe_error', 'counts': 3, 'channel': 'release', 'country': 'GR', 'no_clients': 4, 'no_clients_total': 0 }, { 'event': 'subscribe_success', 'counts': 9, 'channel': 'release', 'country': 'XX', 'no_clients': 57, 'no_clients_total': 0 }, { 'event': 'dismiss', 'counts': 1, 'channel': 'beta', 'country': 'XX', 'no_clients': 1, 'no_clients_total': 0 }]: self.assertTrue(detail in jdp2.details)