import datetime import re import uuid from functools import partial from unittest.mock import ANY, call import pytest from flask import url_for from freezegun import freeze_time from app.main.views.platform_admin import ( create_global_stats, format_stats_by_service, get_tech_failure_status_box_data, is_over_threshold, sum_service_usage, ) from tests import service_json from tests.conftest import SERVICE_ONE_ID, SERVICE_TWO_ID, normalize_spaces @pytest.mark.parametrize( "endpoint", [ "main.platform_admin", "main.live_services", "main.trial_services", ], ) def test_should_redirect_if_not_logged_in(client_request, endpoint, mocker): mocker.patch("app.notify_client.user_api_client.UserApiClient.deactivate_user") client_request.logout() client_request.get( endpoint, _expected_redirect=url_for("main.sign_in", next=url_for(endpoint)), ) @pytest.mark.parametrize( "endpoint", [ "main.platform_admin", "main.platform_admin_splash_page", "main.live_services", "main.trial_services", ], ) def test_should_403_if_not_platform_admin( client_request, endpoint, ): client_request.get(endpoint, _expected_status=403) @pytest.mark.parametrize( ("endpoint", "expected_services_shown"), [ ("main.live_services", 1), ("main.trial_services", 1), ], ) def test_should_render_platform_admin_page( client_request, platform_admin_user, mock_get_detailed_services, endpoint, expected_services_shown, ): client_request.login(platform_admin_user) page = client_request.get(endpoint) assert [ normalize_spaces(column.text) for column in page.select("tbody tr")[1].select("td") ] == [ "0 emails sent", "0 text messages sent", ] mock_get_detailed_services.assert_called_once_with( {"detailed": True, "include_from_test_key": True, "only_active": False} ) @pytest.mark.parametrize( "endpoint", [ "main.live_services", "main.trial_services", ], ) @pytest.mark.parametrize( ("partial_url_for", "inc"), [ (partial(url_for), True), (partial(url_for, include_from_test_key="y", start_date="", end_date=""), True), (partial(url_for, start_date="", end_date=""), False), ], ) def test_live_trial_services_toggle_including_from_test_key( partial_url_for, client_request, platform_admin_user, mock_get_detailed_services, endpoint, inc, ): client_request.login(platform_admin_user) client_request.get_url(partial_url_for(endpoint)) mock_get_detailed_services.assert_called_once_with( { "detailed": True, "only_active": False, "include_from_test_key": inc, } ) @pytest.mark.parametrize("endpoint", ["main.live_services", "main.trial_services"]) def test_live_trial_services_with_date_filter( client_request, platform_admin_user, mock_get_detailed_services, endpoint ): client_request.login(platform_admin_user) page = client_request.get( endpoint, start_date="2016-12-20", end_date="2016-12-28", ) assert "Platform admin" in page.text mock_get_detailed_services.assert_called_once_with( { "include_from_test_key": False, "end_date": datetime.date(2016, 12, 28), "start_date": datetime.date(2016, 12, 20), "detailed": True, "only_active": False, } ) @pytest.mark.parametrize( ("endpoint", "expected_big_numbers"), [ ( "main.live_services", ( "55 emails sent 5 failed – 5.0%", "110 text messages sent 10 failed – 5.0%", ), ), ( "main.trial_services", ( "6 emails sent 1 failed – 10.0%", "11 text messages sent 1 failed – 5.0%", ), ), ], ) def test_should_show_total_on_live_trial_services_pages( client_request, platform_admin_user, mock_get_detailed_services, endpoint, fake_uuid, expected_big_numbers, ): services = [ service_json(fake_uuid, "My Service 1", [], restricted=False), service_json(fake_uuid, "My Service 2", [], restricted=True), ] services[0]["statistics"] = create_stats( emails_requested=100, emails_delivered=50, emails_failed=5, sms_requested=200, sms_delivered=100, sms_failed=10, ) services[1]["statistics"] = create_stats( emails_requested=10, emails_delivered=5, emails_failed=1, sms_requested=20, sms_delivered=10, sms_failed=1, ) mock_get_detailed_services.return_value = {"data": services} client_request.login(platform_admin_user) page = client_request.get(endpoint) assert ( normalize_spaces(page.select(".big-number-with-status")[0].text), normalize_spaces(page.select(".big-number-with-status")[1].text), # normalize_spaces(page.select('.big-number-with-status')[2].text), ) == expected_big_numbers def test_create_global_stats_sets_failure_rates(fake_uuid): services = [service_json(fake_uuid, "a", []), service_json(fake_uuid, "b", [])] services[0]["statistics"] = create_stats( emails_requested=1, emails_delivered=1, emails_failed=0, ) services[1]["statistics"] = create_stats( emails_requested=2, emails_delivered=1, emails_failed=1, ) stats = create_global_stats(services) assert stats == { "email": {"delivered": 2, "failed": 1, "requested": 3, "failure_rate": "33.3"}, "sms": {"delivered": 0, "failed": 0, "requested": 0, "failure_rate": "0"}, } def create_stats( emails_requested=0, emails_delivered=0, emails_failed=0, sms_requested=0, sms_delivered=0, sms_failed=0, ): return { "sms": { "requested": sms_requested, "delivered": sms_delivered, "failed": sms_failed, }, "email": { "requested": emails_requested, "delivered": emails_delivered, "failed": emails_failed, }, } def test_format_stats_by_service_returns_correct_values(fake_uuid): services = [service_json(fake_uuid, "a", [])] services[0]["statistics"] = create_stats( emails_requested=10, emails_delivered=3, emails_failed=5, sms_requested=50, sms_delivered=7, sms_failed=11, ) ret = list(format_stats_by_service(services)) assert len(ret) == 1 assert ret[0]["stats"]["email"]["requested"] == 10 assert ret[0]["stats"]["email"]["delivered"] == 3 assert ret[0]["stats"]["email"]["failed"] == 5 assert ret[0]["stats"]["sms"]["requested"] == 50 assert ret[0]["stats"]["sms"]["delivered"] == 7 assert ret[0]["stats"]["sms"]["failed"] == 11 @pytest.mark.parametrize( ("endpoint", "restricted", "research_mode"), [("main.trial_services", True, False), ("main.live_services", False, False)], ) def test_should_show_email_and_sms_stats_for_all_service_types( endpoint, restricted, research_mode, client_request, platform_admin_user, mock_get_detailed_services, fake_uuid, ): services = [ service_json( fake_uuid, "My Service", [], restricted=restricted, research_mode=research_mode, ) ] services[0]["statistics"] = create_stats( emails_requested=10, emails_delivered=3, emails_failed=5, sms_requested=50, sms_delivered=7, sms_failed=11, ) mock_get_detailed_services.return_value = {"data": services} client_request.login(platform_admin_user) page = client_request.get(endpoint) mock_get_detailed_services.assert_called_once_with( {"detailed": True, "include_from_test_key": True, "only_active": ANY} ) table_body = page.find_all("table")[0].find_all("tbody")[0] service_row_group = table_body.find_all("tbody")[0].find_all("tr") email_stats = service_row_group[1].select(".big-number-number")[0] sms_stats = service_row_group[1].select(".big-number-number")[1] assert normalize_spaces(email_stats.text) == "10" assert normalize_spaces(sms_stats.text) == "50" @pytest.mark.parametrize( ("endpoint", "restricted"), [("main.live_services", False), ("main.trial_services", True)], ids=["live", "trial"], ) def test_should_show_archived_services_last( endpoint, client_request, platform_admin_user, mock_get_detailed_services, restricted, ): services = [ service_json( name="C", restricted=restricted, active=False, created_at="2002-02-02 12:00:00", ), service_json( name="B", restricted=restricted, active=True, created_at="2001-01-01 12:00:00", ), service_json( name="A", restricted=restricted, active=True, created_at="2003-03-03 12:00:00", ), ] services[0]["statistics"] = create_stats() services[1]["statistics"] = create_stats() services[2]["statistics"] = create_stats() mock_get_detailed_services.return_value = {"data": services} client_request.login(platform_admin_user) page = client_request.get(endpoint) mock_get_detailed_services.assert_called_once_with( {"detailed": True, "include_from_test_key": True, "only_active": ANY} ) table_body = page.find_all("table")[0].find_all("tbody")[0] services = [service.tr for service in table_body.find_all("tbody")] assert len(services) == 3 assert normalize_spaces(services[0].td.text) == "A" assert normalize_spaces(services[1].td.text) == "B" assert normalize_spaces(services[2].td.text) == "C Archived" @pytest.mark.parametrize( ("endpoint", "restricted", "research_mode"), [("main.trial_services", True, False), ("main.live_services", False, False)], ) def test_should_order_services_by_usage_with_inactive_last( endpoint, restricted, research_mode, client_request, platform_admin_user, mock_get_detailed_services, fake_uuid, ): services = [ service_json( fake_uuid, "My Service 1", [], restricted=restricted, research_mode=research_mode, ), service_json( fake_uuid, "My Service 2", [], restricted=restricted, research_mode=research_mode, ), service_json( fake_uuid, "My Service 3", [], restricted=restricted, research_mode=research_mode, active=False, ), ] services[0]["statistics"] = create_stats( emails_requested=100, emails_delivered=25, emails_failed=25, sms_requested=100, sms_delivered=25, sms_failed=25, ) services[1]["statistics"] = create_stats( emails_requested=200, emails_delivered=50, emails_failed=50, sms_requested=200, sms_delivered=50, sms_failed=50, ) services[2]["statistics"] = create_stats( emails_requested=200, emails_delivered=50, emails_failed=50, sms_requested=200, sms_delivered=50, sms_failed=50, ) mock_get_detailed_services.return_value = {"data": services} client_request.login(platform_admin_user) page = client_request.get(endpoint) mock_get_detailed_services.assert_called_once_with( {"detailed": True, "include_from_test_key": True, "only_active": ANY} ) table_body = page.find_all("table")[0].find_all("tbody")[0] services = [service.tr for service in table_body.find_all("tbody")] assert len(services) == 3 assert normalize_spaces(services[0].td.text) == "My Service 2" assert normalize_spaces(services[1].td.text) == "My Service 1" assert normalize_spaces(services[2].td.text) == "My Service 3 Archived" def test_sum_service_usage_is_sum_of_all_activity(fake_uuid): service = service_json(fake_uuid, "My Service 1") service["statistics"] = create_stats( emails_requested=100, emails_delivered=25, emails_failed=25, sms_requested=100, sms_delivered=25, sms_failed=25, ) assert sum_service_usage(service) == 200 def test_sum_service_usage_with_zeros(fake_uuid): service = service_json(fake_uuid, "My Service 1") service["statistics"] = create_stats( emails_requested=0, emails_delivered=0, emails_failed=25, sms_requested=0, sms_delivered=0, sms_failed=0, ) assert sum_service_usage(service) == 0 def test_platform_admin_list_complaints(client_request, platform_admin_user, mocker): complaint = { "id": str(uuid.uuid4()), "notification_id": str(uuid.uuid4()), "service_id": str(uuid.uuid4()), "service_name": "Sample service", "ses_feedback_id": "Some ses id", "complaint_type": "abuse", "complaint_date": "2018-06-05T13:50:30.012354", "created_at": "2018-06-05T13:50:30.012354", } mock = mocker.patch( "app.complaint_api_client.get_all_complaints", return_value={"complaints": [complaint], "links": {}}, ) client_request.login(platform_admin_user) page = client_request.get("main.platform_admin_list_complaints") assert "Email complaints" in page.text assert mock.called def test_should_show_complaints_with_next_previous( client_request, platform_admin_user, mocker, service_one, fake_uuid, ): api_response = { "complaints": [ { "complaint_date": None, "complaint_type": None, "created_at": "2017-12-18T05:00:00.000000Z", "id": fake_uuid, "notification_id": fake_uuid, "service_id": service_one["id"], "service_name": service_one["name"], "ses_feedback_id": "None", } ], "links": { "last": "/complaint?page=3", "next": "/complaint?page=3", "prev": "/complaint?page=1", }, } mocker.patch( "app.complaint_api_client.get_all_complaints", return_value=api_response ) client_request.login(platform_admin_user) page = client_request.get( "main.platform_admin_list_complaints", page=2, ) next_page_link = page.find("a", {"rel": "next"}) prev_page_link = page.find("a", {"rel": "previous"}) assert ( url_for("main.platform_admin_list_complaints", page=3) in next_page_link["href"] ) assert "Next page" in next_page_link.text.strip() assert "page 3" in next_page_link.text.strip() assert ( url_for("main.platform_admin_list_complaints", page=1) in prev_page_link["href"] ) assert "Previous page" in prev_page_link.text.strip() assert "page 1" in prev_page_link.text.strip() def test_platform_admin_list_complaints_returns_404_with_invalid_page( client_request, platform_admin_user, mocker, ): mocker.patch( "app.complaint_api_client.get_all_complaints", return_value={"complaints": [], "links": {}}, ) client_request.login(platform_admin_user) client_request.get( "main.platform_admin_list_complaints", page="invalid", _expected_status=404, ) @pytest.mark.parametrize( ("number", "total", "threshold", "result"), [ (0, 0, 0, False), (1, 1, 0, True), (2, 3, 66, True), (2, 3, 67, False), ], ) def test_is_over_threshold(number, total, threshold, result): assert is_over_threshold(number, total, threshold) is result def test_get_tech_failure_status_box_data_removes_percentage_data(): stats = { "failures": { "permanent-failure": 0, "technical-failure": 0, "temporary-failure": 1, }, "test-key": 0, "total": 5589, } tech_failure_data = get_tech_failure_status_box_data(stats) assert "percentage" not in tech_failure_data def test_platform_admin_splash_doesnt_talk_to_api( client_request, platform_admin_user, ): client_request.login(platform_admin_user) page = client_request.get("main.platform_admin_splash_page") assert page.select_one("main .usa-body a")["href"] == url_for( "main.platform_admin", ) def test_platform_admin_with_start_and_end_dates_provided( mocker, client_request, platform_admin_user, ): start_date = "2018-01-01" end_date = "2018-06-01" api_args = { "start_date": datetime.date(2018, 1, 1), "end_date": datetime.date(2018, 6, 1), } mocker.patch("app.main.views.platform_admin.make_columns") aggregate_stats_mock = mocker.patch( "app.main.views.platform_admin.platform_stats_api_client.get_aggregate_platform_stats" ) complaint_count_mock = mocker.patch( "app.main.views.platform_admin.complaint_api_client.get_complaint_count" ) client_request.login(platform_admin_user) client_request.get( "main.platform_admin", start_date=start_date, end_date=end_date, ) aggregate_stats_mock.assert_called_with(api_args) complaint_count_mock.assert_called_with(api_args) @freeze_time("2018-6-11") def test_platform_admin_with_only_a_start_date_provided( mocker, client_request, platform_admin_user, ): start_date = "2018-01-01" api_args = { "start_date": datetime.date(2018, 1, 1), "end_date": datetime.datetime.utcnow().date(), } mocker.patch("app.main.views.platform_admin.make_columns") aggregate_stats_mock = mocker.patch( "app.main.views.platform_admin.platform_stats_api_client.get_aggregate_platform_stats" ) complaint_count_mock = mocker.patch( "app.main.views.platform_admin.complaint_api_client.get_complaint_count" ) client_request.login(platform_admin_user) client_request.get( "main.platform_admin", start_date=start_date, ) aggregate_stats_mock.assert_called_with(api_args) complaint_count_mock.assert_called_with(api_args) def test_platform_admin_without_dates_provided( mocker, client_request, platform_admin_user, ): api_args = {} mocker.patch("app.main.views.platform_admin.make_columns") aggregate_stats_mock = mocker.patch( "app.main.views.platform_admin.platform_stats_api_client.get_aggregate_platform_stats" ) complaint_count_mock = mocker.patch( "app.main.views.platform_admin.complaint_api_client.get_complaint_count" ) client_request.login(platform_admin_user) client_request.get("main.platform_admin") aggregate_stats_mock.assert_called_with(api_args) complaint_count_mock.assert_called_with(api_args) def test_platform_admin_displays_stats_in_right_boxes_and_with_correct_styling( mocker, client_request, platform_admin_user, ): platform_stats = { "email": { "failures": { "permanent-failure": 3, "technical-failure": 0, "temporary-failure": 0, }, "test-key": 0, "total": 145, }, "sms": { "failures": { "permanent-failure": 0, "technical-failure": 1, "temporary-failure": 0, }, "test-key": 5, "total": 168, }, } mocker.patch( "app.main.views.platform_admin.platform_stats_api_client.get_aggregate_platform_stats", return_value=platform_stats, ) mocker.patch( "app.main.views.platform_admin.complaint_api_client.get_complaint_count", return_value=15, ) client_request.login(platform_admin_user) page = client_request.get("main.platform_admin") # Email permanent failure status box - number is correct assert "3 permanent failures" in page.find_all("div", class_="grid-col-6")[0].find( string=re.compile("permanent") ) # Email complaints status box - link exists and number is correct assert page.find("a", string="15 complaints") # SMS total box - number is correct assert page.find_all("span", class_="big-number-number")[1].text.strip() == "168" # Test SMS box - number is correct assert "5" in page.find_all("div", class_="grid-col-6")[3].text # SMS technical failure status box - number is correct and failure class is used assert ( "1 technical failures" in page.find_all("div", class_="grid-col-6")[1] .find("div", class_="big-number-status--failing") .text ) def test_clear_cache_shows_form( client_request, platform_admin_user, mocker, ): redis = mocker.patch("app.main.views.platform_admin.redis_client") client_request.login(platform_admin_user) page = client_request.get("main.clear_cache") assert not redis.delete_by_pattern.called radios = {el["value"] for el in page.select("input[type=checkbox]")} assert radios == { "user", "service", "template", "organization", } @pytest.mark.parametrize( ("model_type", "expected_calls", "expected_confirmation"), [ ( "template", [ call("service-????????-????-????-????-????????????-templates"), call( "service-????????-????-????-????-????????????-template" "-????????-????-????-????-????????????-version-*" ), call( "service-????????-????-????-????-????????????-template" "-????????-????-????-????-????????????-versions" ), ], "Removed 6 objects across 3 key formats for template", ), ( ["service", "organization"], [ call("has_jobs-????????-????-????-????-????????????"), call("service-????????-????-????-????-????????????"), call("service-????????-????-????-????-????????????-templates"), call("service-????????-????-????-????-????????????-data-retention"), call("service-????????-????-????-????-????????????-template-folders"), call("organizations"), call("domains"), call("live-service-and-organization-counts"), call("organization-????????-????-????-????-????????????-name"), ], "Removed 18 objects across 9 key formats for service, organization", ), ], ) def test_clear_cache_submits_and_tells_you_how_many_things_were_deleted( client_request, platform_admin_user, mocker, model_type, expected_calls, expected_confirmation, ): redis = mocker.patch("app.main.views.platform_admin.redis_client") redis.delete_by_pattern.return_value = 2 client_request.login(platform_admin_user) page = client_request.post( "main.clear_cache", _data={"model_type": model_type}, _expected_status=200 ) assert redis.delete_by_pattern.call_args_list == expected_calls flash_banner = page.find("div", class_="banner-default") assert flash_banner.text.strip() == expected_confirmation def test_clear_cache_requires_option( client_request, platform_admin_user, mocker, ): redis = mocker.patch("app.main.views.platform_admin.redis_client") client_request.login(platform_admin_user) page = client_request.post("main.clear_cache", _data={}, _expected_status=200) assert ( normalize_spaces(page.find("span", class_="usa-error-message").text) == "Error: Select at least one option" ) assert not redis.delete_by_pattern.called def test_reports_page( client_request, platform_admin_user, ): client_request.login(platform_admin_user) page = client_request.get("main.platform_admin_reports") assert ( page.find("a", text="Download live services csv report").attrs["href"] == "/platform-admin/reports/live-services.csv" ) assert page.find("a", text="Monthly notification statuses for live services").attrs[ "href" ] == url_for("main.notifications_sent_by_service") def test_get_live_services_report( client_request, platform_admin_user, mocker, ): mocker.patch( "app.service_api_client.get_live_services_data", return_value={ "data": [ { "service_id": 1, "service_name": "jessie the oak tree", "organization_name": "Forest", "consent_to_research": True, "contact_name": "Forest fairy", "organization_type": "Ecosystem", "contact_email": "forest.fairy@digital.cabinet-office.gov.uk", "contact_mobile": "+12028675109", "live_date": "Sat, 29 Mar 2014 00:00:00 GMT", "sms_volume_intent": 100, "email_volume_intent": 50, "sms_totals": 300, "email_totals": 1200, "free_sms_fragment_limit": 100, }, { "service_id": 2, "service_name": "james the pine tree", "organization_name": "Forest", "consent_to_research": None, "contact_name": None, "organization_type": "Ecosystem", "contact_email": None, "contact_mobile": None, "live_date": None, "sms_volume_intent": None, "email_volume_intent": 60, "sms_totals": 0, "email_totals": 0, "free_sms_fragment_limit": 200, }, ] }, ) client_request.login(platform_admin_user) response = client_request.get_response( "main.live_services_csv", ) report = response.get_data(as_text=True) assert report.strip() == ( "Service ID,Organization,Organization type,Service name,Consent to research,Main contact,Contact email," + "Contact mobile,Live date,SMS volume intent,Email volume intent,SMS sent this year," + "Emails sent this year,Free sms allowance\r\n" + "1,Forest,Ecosystem,jessie the oak tree,True,Forest fairy,forest.fairy@digital.cabinet-office.gov.uk," + "+12028675109,29-03-2014,100,50,300,1200,100\r\n" + "2,Forest,Ecosystem,james the pine tree,,,,,,,60,0,0,200" ) def test_get_notifications_sent_by_service_shows_date_form( client_request, platform_admin_user, ): client_request.login(platform_admin_user) page = client_request.get("main.notifications_sent_by_service") assert [ (input["type"], input["name"], input.get("value")) for input in page.select("input") ] == [ ("text", "start_date", None), ("text", "end_date", None), ("hidden", "csrf_token", ANY), ] def test_get_notifications_sent_by_service_validates_form( mocker, client_request, platform_admin_user, ): mock_get_stats_from_api = mocker.patch( "app.main.views.platform_admin.notification_api_client" ) client_request.login(platform_admin_user) page = client_request.post( "main.notifications_sent_by_service", _expected_status=200, _data={"start_date": "", "end_date": "20190101"}, ) errors = page.select(".usa-error-message") assert len(errors) == 2 for error in errors: assert "Not a valid date value" in error.text assert mock_get_stats_from_api.called is False def test_get_billing_report_when_no_results_for_date( client_request, platform_admin_user, mocker ): client_request.login(platform_admin_user) mocker.patch( "app.main.views.platform_admin.billing_api_client.get_data_for_billing_report", return_value=[], ) page = client_request.post( "main.get_billing_report", _expected_status=200, _data={"start_date": "2019-01-01", "end_date": "2019-03-31"}, ) error = page.select_one(".banner-dangerous") assert normalize_spaces(error.text) == "No results for dates" def test_get_billing_report_calls_api_and_download_data( client_request, platform_admin_user, mocker ): mocker.patch( "app.main.views.platform_admin.billing_api_client.get_data_for_billing_report", return_value=[ { "organization_id": "7832a1be-a1f0-4f2a-982f-05adfd3d6354", "organization_name": "Org for a - with sms", "service_id": "48e82ac0-c8c4-4e46-8712-c83c35a94006", "service_name": "a - with sms", "sms_cost": 0, "sms_chargeable_units": 0, "purchase_order_number": "PO1234", "contact_names": "Anne, Marie, Josh", "contact_email_addresses": "billing@example.com, accounts@example.com", "billing_reference": "Notify2020", } ], ) client_request.login(platform_admin_user) response = client_request.post_response( "main.get_billing_report", _data={"start_date": "2019-01-01", "end_date": "2019-03-31"}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert response.headers["Content-Disposition"] == ( 'attachment; filename="Billing Report from {} to {}.csv"'.format( "2019-01-01", "2019-03-31" ) ) assert response.get_data(as_text=True) == ( "organization_id,organization_name,service_id,service_name,sms_cost,sms_chargeable_units," + "purchase_order_number,contact_names,contact_email_addresses," + "billing_reference\r\n" + "7832a1be-a1f0-4f2a-982f-05adfd3d6354," + "Org for a - with sms," + "48e82ac0-c8c4-4e46-8712-c83c35a94006," + "a - with sms," + "0," + "0," + 'PO1234,"Anne, Marie, Josh","billing@example.com, accounts@example.com",Notify2020' + "\r\n" ) def test_get_notifications_sent_by_service_calls_api_and_downloads_data( mocker, client_request, platform_admin_user, service_one, service_two, ): api_data = [ [ "2019-01-01", SERVICE_ONE_ID, service_one["name"], "email", 191, 0, 0, 14, 0, 0, ], ["2019-01-01", SERVICE_ONE_ID, service_one["name"], "sms", 42, 0, 0, 8, 0, 0], ["2019-01-01", SERVICE_TWO_ID, service_two["name"], "email", 3, 1, 0, 2, 0, 0], ] mocker.patch( "app.main.views.platform_admin.notification_api_client.get_notification_status_by_service", return_value=api_data, ) start_date = datetime.date(2019, 1, 1) end_date = datetime.date(2019, 1, 31) client_request.login(platform_admin_user) response = client_request.post_response( "main.notifications_sent_by_service", _data={"start_date": start_date, "end_date": end_date}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert response.headers["Content-Disposition"] == ( 'attachment; filename="{} to {} notification status per service report.csv"'.format( start_date, end_date ) ) assert response.get_data(as_text=True) == ( "date_created,service_id,service_name,notification_type,count_sending,count_delivered," + "count_technical_failure,count_temporary_failure,count_permanent_failure,count_sent\r\n" + "2019-01-01,596364a0-858e-42c8-9062-a8fe822260eb,service one,email,191,0,0,14,0,0\r\n" + "2019-01-01,596364a0-858e-42c8-9062-a8fe822260eb,service one,sms,42,0,0,8,0,0\r\n" + "2019-01-01,147ad62a-2951-4fa1-9ca0-093cd1a52c52,service two,email,3,1,0,2,0,0\r\n" ) def test_get_volumes_by_service_report_calls_api_and_download_data( client_request, platform_admin_user, mocker ): mocker.patch( "app.main.views.platform_admin.billing_api_client.get_data_for_volumes_by_service_report", return_value=[ { "organization_id": "7832a1be-a1f0-4f2a-982f-05adfd3d6354", "organization_name": "Org name", "service_id": "48e82ac0-c8c4-4e46-8712-c83c35a94006", "service_name": "service name", "free_allowance": 10000, "sms_notifications": 10, "sms_chargeable_units": 20, "email_totals": 8, } ], ) client_request.login(platform_admin_user) response = client_request.post_response( "main.get_volumes_by_service", _data={"start_date": "2019-01-01", "end_date": "2019-03-31"}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert response.headers["Content-Disposition"] == ( 'attachment; filename="Volumes by service report from {} to {}.csv"'.format( "2019-01-01", "2019-03-31" ) ) assert response.get_data(as_text=True) == ( "organization id,organization name,service id,service name,free allowance,sms notifications," + "sms chargeable units,email totals\r\n" + "7832a1be-a1f0-4f2a-982f-05adfd3d6354," + "Org name," + "48e82ac0-c8c4-4e46-8712-c83c35a94006," + "service name," + "10000," + "10," + "20," + "8" + "\r\n" ) def test_get_daily_volumes_report_calls_api_and_download_data( client_request, platform_admin_user, mocker ): mocker.patch( "app.main.views.platform_admin.billing_api_client.get_data_for_daily_volumes_report", return_value=[ { "day": "2019-01-01", "sms_totals": 20, "sms_fragment_totals": 40, "sms_chargeable_units": 60, "email_totals": 100, } ], ) client_request.login(platform_admin_user) response = client_request.post_response( "main.get_daily_volumes", _data={"start_date": "2019-01-01", "end_date": "2019-03-31"}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert response.headers["Content-Disposition"] == ( 'attachment; filename="Daily volumes report from {} to {}.csv"'.format( "2019-01-01", "2019-03-31" ) ) assert response.get_data(as_text=True) == ( "day,sms totals,sms fragment totals,sms chargeable units,email totals\r\n" + "2019-01-01," + "20," + "40," + "60," + "100" + "\r\n" ) def test_get_users_report(client_request, platform_admin_user, mocker): mocker.patch( "app.main.views.platform_admin.user_api_client.get_all_users", return_value=[ { "name": "Johnny Sokko", "organizations": [], "password_changed_at": "2023-07-21 14:12:54.832850", "permissions": { "test service": [ "manage_users", "manage_templates", "manage_settings", "send_texts", "send_emails", "manage_api_keys", "view_activity", ] }, "platform_admin": True, "services": ["test service"], "state": "active", } ], ) client_request.login(platform_admin_user) response = client_request.post_response( "main.get_users_report", _data={}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert "attachment" in response.headers["Content-Disposition"] assert "filename" in response.headers["Content-Disposition"] assert "User Report" in response.headers["Content-Disposition"] my_response = response.get_data(as_text=True) assert "Johnny Sokko" in my_response assert "manage_users" in my_response assert "test service" in my_response assert "active" in my_response def test_get_daily_sms_provider_volumes_report_calls_api_and_download_data( client_request, platform_admin_user, mocker ): mocker.patch( "app.main.views.platform_admin.billing_api_client.get_data_for_daily_sms_provider_volumes_report", return_value=[ { "day": "2019-01-01", "provider": "foo", "sms_totals": 20, "sms_fragment_totals": 40, "sms_chargeable_units": 60, "sms_cost": 80, } ], ) client_request.login(platform_admin_user) response = client_request.post_response( "main.get_daily_sms_provider_volumes", _data={"start_date": "2019-01-01", "end_date": "2019-03-31"}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert response.headers["Content-Disposition"] == ( 'attachment; filename="Daily SMS provider volumes report from {} to {}.csv"'.format( "2019-01-01", "2019-03-31" ) ) assert response.get_data(as_text=True) == ( "day,provider,sms totals,sms fragment totals,sms chargeable units,sms cost\r\n" + "2019-01-01," + "foo," + "20," + "40," + "60," + "80" + "\r\n" ) def test_download_all_users(client_request, platform_admin_user, mocker): mocker.patch( "app.main.views.platform_admin.user_api_client.get_all_users_detailed", return_value=[ { "name": "Johnny Sokko", "email_address": "j_sokko@unicorn.gov", "mobile_number": "15555555555", "service": "Emperor, Guillotine, Service", } ], ) client_request.login(platform_admin_user) response = client_request.get_response( "main.download_all_users", _data={}, _expected_status=200, ) assert response.content_type == "text/csv; charset=utf-8" assert "attachment" in response.headers["Content-Disposition"] assert "filename" in response.headers["Content-Disposition"] assert "users" in response.headers["Content-Disposition"] my_response = response.get_data(as_text=True) assert "Johnny Sokko" in my_response assert "Emperor Guillotine Service" in my_response assert "j_sokko@unicorn.gov" in my_response assert "15555555555" in my_response