mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-09 04:43:54 -05:00
1290 lines
39 KiB
Python
1290 lines
39 KiB
Python
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
|