2019-05-21 14:56:15 +01:00
|
|
|
|
import pytest
|
|
|
|
|
|
from flask import url_for
|
2020-02-28 12:32:47 +00:00
|
|
|
|
from freezegun import freeze_time
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2025-06-10 11:40:14 -07:00
|
|
|
|
from notifications_python_client.errors import HTTPError
|
2023-07-12 12:09:44 -04:00
|
|
|
|
from tests import organization_json, service_json
|
2019-05-21 14:56:15 +01:00
|
|
|
|
from tests.conftest import (
|
|
|
|
|
|
ORGANISATION_ID,
|
|
|
|
|
|
SERVICE_ONE_ID,
|
2019-06-20 10:35:55 +01:00
|
|
|
|
SERVICE_TWO_ID,
|
2019-12-19 16:59:07 +00:00
|
|
|
|
create_active_user_with_permissions,
|
|
|
|
|
|
create_platform_admin_user,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
normalize_spaces,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_page_shows_all_organizations(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, platform_admin_user, mocker
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
|
|
|
|
|
orgs = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"id": "A3", "name": "Test 3", "active": True, "count_of_live_services": 0},
|
|
|
|
|
|
{"id": "B1", "name": "Test 1", "active": True, "count_of_live_services": 1},
|
|
|
|
|
|
{"id": "C2", "name": "Test 2", "active": False, "count_of_live_services": 2},
|
2019-05-21 14:56:15 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
get_organizations = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.organization.AllOrganizations.client_method", return_value=orgs
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(".organizations")
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("h1").text) == "Organizations"
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2025-10-06 09:38:54 -04:00
|
|
|
|
assert page.select_one("a.usa-button")
|
2021-11-11 14:35:55 +00:00
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
get_organizations.assert_called_once_with()
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
def test_view_organization_shows_the_correct_organization(client_request, mocker):
|
|
|
|
|
|
org = {"id": ORGANISATION_ID, "name": "Test 1", "active": True}
|
|
|
|
|
|
mocker.patch("app.organizations_client.get_organization", return_value=org)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage", return_value={"services": {}}
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2025-10-14 13:20:05 -07:00
|
|
|
|
".organization_usage",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("h1").text) == "Usage"
|
2023-08-28 12:12:21 -04:00
|
|
|
|
assert normalize_spaces(page.select_one(".usa-hint").text) == (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Test 1 has no live services on Notify.gov"
|
2020-03-03 10:34:20 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select("a[download]")
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_page_to_create_new_organization(
|
2019-07-01 10:19:33 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(".add_organization")
|
2019-07-01 10:19:33 +01:00
|
|
|
|
|
|
|
|
|
|
assert [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(input["type"], input["name"], input.get("value"))
|
|
|
|
|
|
for input in page.select("input")
|
2019-07-01 10:19:33 +01:00
|
|
|
|
] == [
|
2025-02-24 15:34:52 -05:00
|
|
|
|
("text", "name", None),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
("radio", "organization_type", "federal"),
|
|
|
|
|
|
("radio", "organization_type", "state"),
|
2023-07-12 12:09:44 -04:00
|
|
|
|
# ('radio', 'organization_type', 'nhs_central'),
|
|
|
|
|
|
# ('radio', 'organization_type', 'nhs_local'),
|
|
|
|
|
|
# ('radio', 'organization_type', 'nhs_gp'),
|
|
|
|
|
|
# ('radio', 'organization_type', 'emergency_service'),
|
|
|
|
|
|
# ('radio', 'organization_type', 'school_or_college'),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
("radio", "organization_type", "other"),
|
|
|
|
|
|
("hidden", "csrf_token", mocker.ANY),
|
2019-07-01 10:19:33 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_create_new_organization(
|
2019-07-01 10:19:33 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.create_organization",
|
2023-07-12 12:09:44 -04:00
|
|
|
|
return_value=organization_json(ORGANISATION_ID),
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2019-07-01 10:19:33 +01:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization",
|
2019-07-01 10:19:33 +01:00
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name": "new name",
|
|
|
|
|
|
"organization_type": "federal",
|
2019-07-02 10:09:08 +01:00
|
|
|
|
},
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
2019-07-02 10:09:08 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
),
|
2019-07-01 10:19:33 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
name="new name",
|
|
|
|
|
|
organization_type="federal",
|
2019-07-01 10:19:33 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_create_new_organization_validates(
|
2019-07-01 10:19:33 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.create_organization"
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2019-07-01 10:19:33 +01:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization",
|
2019-07-01 10:19:33 +01:00
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
)
|
|
|
|
|
|
assert [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(error["data-error-label"], normalize_spaces(error.text))
|
|
|
|
|
|
for error in page.select(".usa-error-message")
|
2019-07-01 10:19:33 +01:00
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
("name", "Error: Cannot be empty"),
|
|
|
|
|
|
("organization_type", "Error: Select the type of organization"),
|
2019-07-01 10:19:33 +01:00
|
|
|
|
]
|
2023-07-12 12:09:44 -04:00
|
|
|
|
assert mock_create_organization.called is False
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("name", "error_message"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
("", "Cannot be empty"),
|
|
|
|
|
|
("a", "at least two alphanumeric characters"),
|
|
|
|
|
|
("a" * 256, "Organization name must be 255 characters or fewer"),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_create_new_organization_fails_with_incorrect_input(
|
2019-11-08 17:12:32 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
mocker,
|
2020-11-30 17:08:40 +00:00
|
|
|
|
name,
|
|
|
|
|
|
error_message,
|
2019-11-08 17:12:32 +00:00
|
|
|
|
):
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.create_organization"
|
2019-11-08 17:12:32 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization",
|
2019-11-08 17:12:32 +00:00
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name": name,
|
|
|
|
|
|
"organization_type": "local",
|
2019-11-08 17:12:32 +00:00
|
|
|
|
},
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
assert mock_create_organization.called is False
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert error_message in page.select_one(".usa-error-message").text
|
2019-11-08 17:12:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_create_new_organization_fails_with_duplicate_name(
|
2021-02-11 12:02:42 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
|
|
|
|
|
def _create(**_kwargs):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
json_mock = mocker.Mock(
|
|
|
|
|
|
return_value={"message": "Organization name already exists"}
|
|
|
|
|
|
)
|
2021-02-15 13:54:52 +00:00
|
|
|
|
resp_mock = mocker.Mock(status_code=400, json=json_mock)
|
2021-02-11 12:02:42 +00:00
|
|
|
|
http_error = HTTPError(response=resp_mock, message="Default message")
|
|
|
|
|
|
raise http_error
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch("app.organizations_client.create_organization", side_effect=_create)
|
2021-02-11 12:02:42 +00:00
|
|
|
|
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization",
|
2021-02-11 12:02:42 +00:00
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name": "Existing org",
|
|
|
|
|
|
"organization_type": "federal",
|
2021-02-11 12:02:42 +00:00
|
|
|
|
},
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
error_message = "This organization name is already in use"
|
|
|
|
|
|
assert error_message in page.select_one(".usa-error-message").text
|
2021-02-11 12:02:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("organization_type", "organization", "expected_status"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
("nhs_gp", None, 200),
|
|
|
|
|
|
("central", None, 403),
|
|
|
|
|
|
("nhs_gp", organization_json(organization_type="nhs_gp"), 403),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.skip(reason="Update for TTS")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_gps_can_create_own_organizations(
|
2019-09-04 16:27:25 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_service_organization,
|
2019-09-04 16:27:25 +01:00
|
|
|
|
service_one,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_type,
|
|
|
|
|
|
organization,
|
2019-09-04 16:27:25 +01:00
|
|
|
|
expected_status,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch("app.organizations_client.get_organization", return_value=organization)
|
|
|
|
|
|
service_one["organization_type"] = organization_type
|
2019-09-04 16:27:25 +01:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization_from_gp_service",
|
2019-09-04 16:27:25 +01:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_expected_status=expected_status,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if expected_status == 403:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("input[type=text]")["name"] == "name"
|
|
|
|
|
|
assert normalize_spaces(page.select_one("label[for=name]").text) == (
|
|
|
|
|
|
"What’s your practice called?"
|
2019-09-04 16:27:25 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("data", "expected_service_name"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"same_as_service_name": False,
|
|
|
|
|
|
"name": "Dr. Example",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Dr. Example",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"same_as_service_name": True,
|
|
|
|
|
|
"name": "This is ignored",
|
|
|
|
|
|
},
|
|
|
|
|
|
"service one",
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.skip(reason="Update for TTS")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_gps_can_name_their_organization(
|
2019-09-04 16:27:25 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_service_organization,
|
2019-09-05 12:10:24 +01:00
|
|
|
|
data,
|
|
|
|
|
|
expected_service_name,
|
2019-09-04 16:27:25 +01:00
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_one["organization_type"] = "nhs_gp"
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.create_organization",
|
2023-07-12 12:09:44 -04:00
|
|
|
|
return_value=organization_json(ORGANISATION_ID),
|
2019-09-04 16:27:25 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization_from_gp_service",
|
2019-09-04 16:27:25 +01:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
2019-09-05 12:10:24 +01:00
|
|
|
|
_data=data,
|
2019-09-04 16:27:25 +01:00
|
|
|
|
_expected_status=302,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_create_organization.assert_called_once_with(
|
2019-09-05 12:10:24 +01:00
|
|
|
|
name=expected_service_name,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
organization_type="nhs_gp",
|
|
|
|
|
|
)
|
|
|
|
|
|
mock_update_service_organization.assert_called_once_with(
|
|
|
|
|
|
SERVICE_ONE_ID, ORGANISATION_ID
|
2019-09-04 16:27:25 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("data", "expected_error"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "Dr. Example",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select yes or no",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"same_as_service_name": False,
|
|
|
|
|
|
"name": "",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Cannot be empty",
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.skip(reason="Update for TTS")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_validation_of_gps_creating_organizations(
|
2019-09-05 12:10:24 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
data,
|
|
|
|
|
|
expected_error,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_one["organization_type"] = "nhs_gp"
|
2019-09-05 12:10:24 +01:00
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".add_organization_from_gp_service",
|
2019-09-05 12:10:24 +01:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data=data,
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert expected_error in page.select_one(".usa-error-message, .error-message").text
|
2019-09-05 12:10:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
2020-02-28 12:32:47 +00:00
|
|
|
|
@freeze_time("2020-02-20 20:20")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_shows_live_services_and_usage(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
2020-02-25 17:49:55 +00:00
|
|
|
|
mock = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "1",
|
|
|
|
|
|
"chargeable_billable_sms": 250122,
|
|
|
|
|
|
"emails_sent": 13000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 122,
|
|
|
|
|
|
"sms_cost": 0,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_TWO_ID,
|
|
|
|
|
|
"service_name": "5",
|
|
|
|
|
|
"chargeable_billable_sms": 0,
|
|
|
|
|
|
"emails_sent": 20000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 2500,
|
|
|
|
|
|
"sms_cost": 42.0,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
2025-10-14 13:20:05 -07:00
|
|
|
|
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
|
2024-02-07 09:38:18 -07:00
|
|
|
|
mock.assert_called_once_with(ORGANISATION_ID, 2020)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
services = page.select("main h3")
|
2023-08-28 12:12:21 -04:00
|
|
|
|
usage_rows = page.select("main .grid-col-6")
|
2019-05-21 14:56:15 +01:00
|
|
|
|
assert len(services) == 2
|
|
|
|
|
|
|
2020-02-27 15:21:08 +00:00
|
|
|
|
# Totals
|
2020-02-28 15:41:59 +00:00
|
|
|
|
assert normalize_spaces(usage_rows[0].text) == "Emails 33,000 sent"
|
2022-10-12 20:16:22 +00:00
|
|
|
|
assert normalize_spaces(usage_rows[1].text) == "Text messages $42.00 spent"
|
2020-02-27 15:21:08 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(services[0].text) == "1"
|
|
|
|
|
|
assert normalize_spaces(services[1].text) == "5"
|
|
|
|
|
|
assert services[0].find("a")["href"] == url_for(
|
|
|
|
|
|
"main.usage", service_id=SERVICE_ONE_ID
|
|
|
|
|
|
)
|
2020-02-27 15:21:08 +00:00
|
|
|
|
|
2022-12-05 15:33:44 -05:00
|
|
|
|
assert normalize_spaces(usage_rows[2].text) == "13,000 emails sent"
|
|
|
|
|
|
assert normalize_spaces(usage_rows[3].text) == "122 free text messages sent"
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert services[1].find("a")["href"] == url_for(
|
|
|
|
|
|
"main.usage", service_id=SERVICE_TWO_ID
|
|
|
|
|
|
)
|
2022-12-05 15:33:44 -05:00
|
|
|
|
assert normalize_spaces(usage_rows[4].text) == "20,000 emails sent"
|
|
|
|
|
|
assert normalize_spaces(usage_rows[5].text) == "$42.00 spent on text messages"
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2020-03-03 10:34:20 +00:00
|
|
|
|
# Ensure there’s no ‘this org has no services message’
|
2023-08-28 12:12:21 -04:00
|
|
|
|
assert not page.select(".usa-hint")
|
2020-03-03 10:34:20 +00:00
|
|
|
|
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2020-03-05 14:40:32 +00:00
|
|
|
|
@freeze_time("2020-02-20 20:20")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_shows_live_services_and_usage_with_count_of_1(
|
2020-03-05 14:40:32 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2020-03-05 14:40:32 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "1",
|
|
|
|
|
|
"chargeable_billable_sms": 1,
|
|
|
|
|
|
"emails_sent": 1,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 1,
|
|
|
|
|
|
"sms_cost": 0,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2020-03-05 14:40:32 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
2025-10-14 13:20:05 -07:00
|
|
|
|
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
|
2020-03-05 14:40:32 +00:00
|
|
|
|
|
2023-08-28 12:12:21 -04:00
|
|
|
|
usage_rows = page.select("main .grid-col-6")
|
2020-03-05 14:40:32 +00:00
|
|
|
|
|
|
|
|
|
|
# Totals
|
|
|
|
|
|
assert normalize_spaces(usage_rows[0].text) == "Emails 1 sent"
|
2022-10-12 20:16:22 +00:00
|
|
|
|
assert normalize_spaces(usage_rows[1].text) == "Text messages $0.00 spent"
|
2020-03-05 14:40:32 +00:00
|
|
|
|
|
2022-12-05 15:33:44 -05:00
|
|
|
|
assert normalize_spaces(usage_rows[2].text) == "1 email sent"
|
|
|
|
|
|
assert normalize_spaces(usage_rows[3].text) == "1 free text message sent"
|
2020-03-05 14:40:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
2020-02-28 12:32:47 +00:00
|
|
|
|
@freeze_time("2020-02-20 20:20")
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("financial_year", "expected_selected"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(2018, "2018 to 2019 fiscal year"),
|
|
|
|
|
|
(2019, "2019 to 2020 fiscal year"),
|
2024-02-07 09:38:18 -07:00
|
|
|
|
(2020, "2020 to 2021 fiscal year"),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_filters_by_financial_year(
|
2020-02-28 12:32:47 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2020-02-28 12:32:47 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
financial_year,
|
|
|
|
|
|
expected_selected,
|
|
|
|
|
|
):
|
|
|
|
|
|
mock = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage", return_value={"services": []}
|
2020-02-28 12:32:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
page = client_request.get(
|
2025-10-14 13:20:05 -07:00
|
|
|
|
".organization_usage",
|
2020-02-28 12:32:47 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
year=financial_year,
|
|
|
|
|
|
)
|
|
|
|
|
|
mock.assert_called_once_with(ORGANISATION_ID, financial_year)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one(".pill").text) == (
|
2024-02-07 09:38:18 -07:00
|
|
|
|
"2020 to 2021 fiscal year "
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"2019 to 2020 fiscal year "
|
2024-02-07 09:38:18 -07:00
|
|
|
|
"2018 to 2019 fiscal year"
|
2020-02-28 12:32:47 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one(".pill-item--selected").text) == (
|
2020-02-28 12:32:47 +00:00
|
|
|
|
expected_selected
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-02-28 16:14:28 +00:00
|
|
|
|
@freeze_time("2020-02-20 20:20")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_shows_search_bar(
|
2020-02-28 16:14:28 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2020-02-28 16:14:28 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "Service 1",
|
|
|
|
|
|
"chargeable_billable_sms": 250122,
|
|
|
|
|
|
"emails_sent": 13000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 122,
|
|
|
|
|
|
"sms_cost": 1.93,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
* 8
|
|
|
|
|
|
},
|
2020-02-28 16:14:28 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
2025-10-14 13:20:05 -07:00
|
|
|
|
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
|
2020-02-28 16:14:28 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
services = page.select(".organization-service")
|
2020-02-28 16:14:28 +00:00
|
|
|
|
assert len(services) == 8
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one(".live-search")["data-targets"] == ".organization-service"
|
2020-02-28 16:14:28 +00:00
|
|
|
|
assert [
|
|
|
|
|
|
normalize_spaces(service_name.text)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
for service_name in page.select(".live-search-relevant")
|
2020-02-28 16:14:28 +00:00
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
|
|
|
|
|
"Service 1",
|
2020-02-28 16:14:28 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@freeze_time("2020-02-20 20:20")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_hides_search_bar_for_7_or_fewer_services(
|
2020-02-28 16:14:28 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2020-02-28 16:14:28 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "Service 1",
|
|
|
|
|
|
"chargeable_billable_sms": 250122,
|
|
|
|
|
|
"emails_sent": 13000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 122,
|
|
|
|
|
|
"sms_cost": 1.93,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
* 7
|
|
|
|
|
|
},
|
2020-02-28 16:14:28 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
2025-10-14 13:20:05 -07:00
|
|
|
|
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
|
2020-02-28 16:14:28 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
services = page.select(".organization-service")
|
2020-02-28 16:14:28 +00:00
|
|
|
|
assert len(services) == 7
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select_one(".live-search")
|
2020-02-28 16:14:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
2021-11-12 18:32:35 +00:00
|
|
|
|
@freeze_time("2021-11-12 11:09:00.061258")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_services_links_to_downloadable_report(
|
2021-11-12 14:35:16 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2021-11-12 14:35:16 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "Service 1",
|
|
|
|
|
|
"chargeable_billable_sms": 250122,
|
|
|
|
|
|
"emails_sent": 13000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 122,
|
|
|
|
|
|
"sms_cost": 1.93,
|
|
|
|
|
|
"sms_remainder": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
* 2
|
|
|
|
|
|
},
|
2021-11-12 14:35:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
2025-10-14 13:20:05 -07:00
|
|
|
|
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
|
2021-11-12 14:35:16 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
link_to_report = page.select_one("a[download]")
|
|
|
|
|
|
assert normalize_spaces(link_to_report.text) == "Download this report (CSV)"
|
2021-11-12 18:32:35 +00:00
|
|
|
|
assert link_to_report.attrs["href"] == url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".download_organization_usage_report",
|
2021-11-12 18:32:35 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2024-10-09 09:56:19 -04:00
|
|
|
|
selected_year=2021,
|
2021-11-12 18:32:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@freeze_time("2021-11-12 11:09:00.061258")
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_download_organization_usage_report(
|
2021-11-12 18:32:35 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2021-11-12 18:32:35 +00:00
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_services_and_usage",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_ONE_ID,
|
|
|
|
|
|
"service_name": "Service 1",
|
|
|
|
|
|
"chargeable_billable_sms": 22,
|
|
|
|
|
|
"emails_sent": 13000,
|
|
|
|
|
|
"free_sms_limit": 100,
|
|
|
|
|
|
"sms_billable_units": 122,
|
|
|
|
|
|
"sms_cost": 1.934,
|
|
|
|
|
|
"sms_remainder": 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"service_id": SERVICE_TWO_ID,
|
|
|
|
|
|
"service_name": "Service 1",
|
|
|
|
|
|
"chargeable_billable_sms": 222,
|
|
|
|
|
|
"emails_sent": 23000,
|
|
|
|
|
|
"free_sms_limit": 250000,
|
|
|
|
|
|
"sms_billable_units": 322,
|
|
|
|
|
|
"sms_cost": 3.935,
|
|
|
|
|
|
"sms_remainder": 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2021-11-12 18:32:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
|
|
|
|
|
csv_report = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".download_organization_usage_report",
|
2021-11-12 18:32:35 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
selected_year=2021,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_test_page_title=False,
|
2021-11-12 18:32:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert csv_report.string == (
|
2021-12-21 16:05:29 +00:00
|
|
|
|
"Service ID,Service Name,Emails sent,Free text message allowance remaining,"
|
2022-12-05 15:33:44 -05:00
|
|
|
|
"Spent on text messages ($)"
|
|
|
|
|
|
"\r\n596364a0-858e-42c8-9062-a8fe822260eb,Service 1,13000,0,1.93"
|
|
|
|
|
|
"\r\n147ad62a-2951-4fa1-9ca0-093cd1a52c52,Service 1,23000,0,3.94\r\n"
|
2021-11-12 18:32:35 +00:00
|
|
|
|
)
|
2021-11-12 14:35:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_trial_mode_services_shows_all_non_live_services(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
mocker,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_organization_services",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
return_value=[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_json(id_="1", name="1", restricted=False, active=True), # live
|
|
|
|
|
|
service_json(id_="2", name="2", restricted=True, active=True), # trial
|
|
|
|
|
|
service_json(id_="3", name="3", restricted=False, active=False), # archived
|
|
|
|
|
|
],
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".organization_trial_mode_services",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_test_page_title=False,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
services = page.select(".browse-list-item")
|
2025-10-20 13:09:21 -07:00
|
|
|
|
assert len(services) == 1
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(services[0].text) == "2"
|
|
|
|
|
|
assert services[0].find("a")["href"] == url_for(
|
|
|
|
|
|
"main.service_dashboard", service_id="2"
|
|
|
|
|
|
)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_trial_mode_services_doesnt_work_if_not_platform_admin(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
|
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".organization_trial_mode_services",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_status=403,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-01-11 14:16:37 +00:00
|
|
|
|
def test_manage_org_users_shows_correct_link_next_to_each_user(
|
|
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_get_users_for_organization,
|
|
|
|
|
|
mock_get_invited_users_for_organization,
|
2022-01-11 14:16:37 +00:00
|
|
|
|
):
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".manage_org_users",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# No banner confirming a user to be deleted shown
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select_one(".banner-dangerous")
|
2022-01-11 14:16:37 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
users = page.find_all(class_="user-list-item")
|
2022-01-11 14:16:37 +00:00
|
|
|
|
|
|
|
|
|
|
# The first user is an invited user, so has the link to cancel the invitation.
|
|
|
|
|
|
# The second two users are active users, so have the link to be removed from the org
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
normalize_spaces(users[0].text)
|
|
|
|
|
|
== "invited_user@test.gsa.gov (invited) Cancel invitation for invited_user@test.gsa.gov"
|
|
|
|
|
|
)
|
|
|
|
|
|
assert (
|
|
|
|
|
|
normalize_spaces(users[1].text)
|
|
|
|
|
|
== "Test User 1 test@gsa.gov Remove Test User 1 test@gsa.gov"
|
|
|
|
|
|
)
|
|
|
|
|
|
assert (
|
|
|
|
|
|
normalize_spaces(users[2].text)
|
|
|
|
|
|
== "Test User 2 testt@gsa.gov Remove Test User 2 testt@gsa.gov"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert users[0].a["href"] == url_for(
|
|
|
|
|
|
".cancel_invited_org_user",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
invited_user_id="73616d70-6c65-4f6f-b267-5f696e766974",
|
|
|
|
|
|
)
|
|
|
|
|
|
assert users[1].a["href"] == url_for(
|
|
|
|
|
|
".edit_organization_user", org_id=ORGANISATION_ID, user_id="1234"
|
|
|
|
|
|
)
|
|
|
|
|
|
assert users[2].a["href"] == url_for(
|
|
|
|
|
|
".edit_organization_user", org_id=ORGANISATION_ID, user_id="5678"
|
2022-01-11 14:16:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-01-12 11:49:58 +00:00
|
|
|
|
def test_manage_org_users_shows_no_link_for_cancelled_users(
|
|
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_get_users_for_organization,
|
2022-01-12 11:49:58 +00:00
|
|
|
|
sample_org_invite,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
sample_org_invite["status"] = "cancelled"
|
|
|
|
|
|
mocker.patch(
|
|
|
|
|
|
"app.models.user.OrganizationInvitedUsers.client_method",
|
|
|
|
|
|
return_value=[sample_org_invite],
|
|
|
|
|
|
)
|
2022-01-12 11:49:58 +00:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".manage_org_users",
|
2022-01-12 11:49:58 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
users = page.find_all(class_="user-list-item")
|
2022-01-12 11:49:58 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
normalize_spaces(users[0].text)
|
|
|
|
|
|
== "invited_user@test.gsa.gov (cancelled invite)"
|
|
|
|
|
|
)
|
2022-01-12 11:49:58 +00:00
|
|
|
|
assert not users[0].a
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"number_of_users",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(8),
|
|
|
|
|
|
pytest.param(800),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2022-01-11 14:38:58 +00:00
|
|
|
|
def test_manage_org_users_should_show_live_search_if_more_than_7_users(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2022-01-11 14:38:58 +00:00
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
number_of_users,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.OrganizationInvitedUsers.client_method",
|
2022-01-11 14:38:58 +00:00
|
|
|
|
return_value=[],
|
|
|
|
|
|
)
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.OrganizationUsers.client_method",
|
2022-01-11 14:38:58 +00:00
|
|
|
|
return_value=[active_user_with_permissions] * number_of_users,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".manage_org_users",
|
2022-01-11 14:38:58 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("div[data-module=live-search]")["data-targets"] == (
|
2022-01-11 14:38:58 +00:00
|
|
|
|
".user-list-item"
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert len(page.select(".user-list-item")) == number_of_users
|
2022-01-11 14:38:58 +00:00
|
|
|
|
|
2025-04-17 15:38:27 -04:00
|
|
|
|
textbox = page.select_one(".usa-input")
|
|
|
|
|
|
assert textbox is not None
|
|
|
|
|
|
assert textbox.get("value") is None
|
2025-02-24 15:34:52 -05:00
|
|
|
|
assert textbox["name"] == "search"
|
2022-01-11 14:38:58 +00:00
|
|
|
|
# data-module=autofocus is set on a containing element so it
|
|
|
|
|
|
# shouldn’t also be set on the textbox itself
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert "data-module" not in textbox
|
|
|
|
|
|
assert not page.select_one("[data-force-focus]")
|
|
|
|
|
|
assert textbox["class"] == ["usa-input"]
|
|
|
|
|
|
assert (
|
2025-02-24 14:50:29 -05:00
|
|
|
|
normalize_spaces(page.select_one("label[class=usa-label]").text)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
== "Search by name or email address"
|
|
|
|
|
|
)
|
2022-01-11 14:38:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"number_of_users",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(3),
|
|
|
|
|
|
pytest.param(7),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-05-26 12:35:48 -07:00
|
|
|
|
def test_manage_org_users_should_show_live_search_if_7_users_or_less(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2023-05-26 12:35:48 -07:00
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
number_of_users,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.OrganizationInvitedUsers.client_method",
|
2023-05-26 12:35:48 -07:00
|
|
|
|
return_value=[],
|
|
|
|
|
|
)
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.OrganizationUsers.client_method",
|
2023-05-26 12:35:48 -07:00
|
|
|
|
return_value=[active_user_with_permissions] * number_of_users,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".manage_org_users",
|
2023-05-26 12:35:48 -07:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
with pytest.raises(expected_exception=TypeError):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("div[data-module=live-search]")["data-targets"] == (
|
2023-05-26 12:35:48 -07:00
|
|
|
|
".user-list-item"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_edit_organization_user_shows_the_delete_confirmation_banner(
|
2022-01-11 14:16:37 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_get_invites_for_organization,
|
|
|
|
|
|
mock_get_users_for_organization,
|
2022-01-11 14:16:37 +00:00
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
):
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".edit_organization_user",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
user_id=active_user_with_permissions["id"],
|
2022-01-11 14:16:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.h1) == "Team members"
|
2022-01-11 14:16:37 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
banner = page.select_one(".banner-dangerous")
|
|
|
|
|
|
assert "Are you sure you want to remove Test User?" in normalize_spaces(
|
|
|
|
|
|
banner.contents[0]
|
|
|
|
|
|
)
|
|
|
|
|
|
assert banner.form.attrs["action"] == url_for(
|
|
|
|
|
|
"main.remove_user_from_organization",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
user_id=active_user_with_permissions["id"],
|
2022-01-11 14:16:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_remove_user_from_organization_makes_api_request_to_remove_user(
|
2022-01-11 14:16:37 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2022-01-11 14:16:37 +00:00
|
|
|
|
fake_uuid,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_remove_user = mocker.patch(
|
|
|
|
|
|
"app.organizations_client.remove_user_from_organization"
|
|
|
|
|
|
)
|
2022-01-11 14:16:37 +00:00
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".remove_user_from_organization",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
user_id=fake_uuid,
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.show_accounts_or_dashboard",
|
2022-01-11 14:16:37 +00:00
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_remove_user.assert_called_with(ORGANISATION_ID, fake_uuid)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_settings_platform_admin_only(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, mock_get_organization, organization_one
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
2019-06-03 13:29:28 +01:00
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2019-06-03 13:29:28 +01:00
|
|
|
|
_expected_status=403,
|
|
|
|
|
|
)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_settings_for_platform_admin(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, platform_admin_user, mock_get_organization, organization_one
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
|
|
|
|
|
expected_rows = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Label Value Action",
|
|
|
|
|
|
"Name Test organization Change organization name",
|
|
|
|
|
|
"Sector Federal government Change sector for the organization",
|
|
|
|
|
|
"Billing details None Change billing details for the organization",
|
|
|
|
|
|
"Notes None Change the notes for the organization",
|
|
|
|
|
|
"Known email domains None Change known email domains for the organization",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(".organization_settings", org_id=organization_one["id"])
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.find("h1").text == "Settings"
|
|
|
|
|
|
rows = page.select("tr")
|
2019-05-21 14:56:15 +01:00
|
|
|
|
assert len(rows) == len(expected_rows)
|
|
|
|
|
|
for index, row in enumerate(expected_rows):
|
|
|
|
|
|
assert row == " ".join(rows[index].text.split())
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_get_organization.assert_called_with(organization_one["id"])
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("endpoint", "expected_options", "expected_selected"),
|
|
|
|
|
|
[
|
2019-05-21 14:56:15 +01:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".edit_organization_type",
|
|
|
|
|
|
(
|
|
|
|
|
|
{"value": "federal", "label": "Federal government"},
|
|
|
|
|
|
{"value": "state", "label": "State government"},
|
|
|
|
|
|
{"value": "other", "label": "Other"},
|
|
|
|
|
|
),
|
|
|
|
|
|
"federal",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_platform_admin_user(),
|
|
|
|
|
|
),
|
|
|
|
|
|
pytest.param(create_active_user_with_permissions(), marks=pytest.mark.xfail),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_view_organization_settings(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
endpoint,
|
|
|
|
|
|
expected_options,
|
|
|
|
|
|
expected_selected,
|
|
|
|
|
|
user,
|
|
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(endpoint, org_id=organization_one["id"])
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
radios = page.select("input[type=radio]")
|
2025-02-27 14:21:43 -05:00
|
|
|
|
labels = page.select("label.usa-radio__label") # Select all radio labels in order
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
for index, option in enumerate(expected_options):
|
2020-06-26 17:49:07 +01:00
|
|
|
|
option_values = {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"value": radios[index]["value"],
|
2025-02-27 14:21:43 -05:00
|
|
|
|
"label": normalize_spaces(labels[index].text), # Match labels using index
|
2020-06-26 17:49:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
assert option_values == option
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
if expected_selected:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("input[checked]")["value"] == expected_selected
|
2019-05-21 14:56:15 +01:00
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select_one("input[checked]")
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("endpoint", "post_data", "expected_persisted"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(
|
|
|
|
|
|
".edit_organization_type",
|
|
|
|
|
|
{"organization_type": "federal"},
|
|
|
|
|
|
{"cached_service_ids": [], "organization_type": "federal"},
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
".edit_organization_type",
|
|
|
|
|
|
{"organization_type": "state"},
|
|
|
|
|
|
{"cached_service_ids": [], "organization_type": "state"},
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_platform_admin_user(),
|
|
|
|
|
|
),
|
|
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_active_user_with_permissions(),
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_settings(
|
2019-07-24 11:57:42 +01:00
|
|
|
|
mocker,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
endpoint,
|
|
|
|
|
|
post_data,
|
|
|
|
|
|
expected_persisted,
|
|
|
|
|
|
user,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch("app.organizations_client.get_organization_services", return_value=[])
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if user["email_address"] == "platform@admin.gsa.gov":
|
2023-05-26 12:35:48 -07:00
|
|
|
|
expected_status = 302
|
|
|
|
|
|
expected_redirect = url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2023-05-26 12:35:48 -07:00
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
expected_status = 403
|
|
|
|
|
|
expected_redirect = None
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request.post(
|
|
|
|
|
|
endpoint,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
org_id=organization_one["id"],
|
2019-05-21 14:56:15 +01:00
|
|
|
|
_data=post_data,
|
2023-05-26 12:35:48 -07:00
|
|
|
|
_expected_status=expected_status,
|
|
|
|
|
|
_expected_redirect=expected_redirect,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if user["email_address"] == "platform@admin.gsa.gov":
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
organization_one["id"],
|
2023-05-26 12:35:48 -07:00
|
|
|
|
**expected_persisted,
|
|
|
|
|
|
)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_sector_sends_service_id_data_to_api_client(
|
2019-07-24 11:57:42 +01:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization_services,
|
|
|
|
|
|
mock_update_organization,
|
2019-07-24 11:57:42 +01:00
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
):
|
|
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_type",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2025-02-27 14:21:43 -05:00
|
|
|
|
_data={"organization_type": "state"},
|
2019-07-24 11:57:42 +01:00
|
|
|
|
_expected_status=302,
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2019-07-24 11:57:42 +01:00
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
organization_one["id"],
|
|
|
|
|
|
cached_service_ids=["12345", "67890", SERVICE_ONE_ID],
|
2025-02-27 14:21:43 -05:00
|
|
|
|
organization_type="state",
|
2019-07-24 11:57:42 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_platform_admin_user(),
|
|
|
|
|
|
),
|
|
|
|
|
|
pytest.param(create_active_user_with_permissions(), marks=pytest.mark.xfail),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_view_organization_domains(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
mocker,
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
user,
|
|
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.get_organization",
|
2023-07-12 12:09:44 -04:00
|
|
|
|
side_effect=lambda org_id: organization_json(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Org 1",
|
|
|
|
|
|
domains=["example.gsa.gov", "test.example.gsa.gov"],
|
|
|
|
|
|
),
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_domains",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert [textbox.get("value") for textbox in page.select("input[type=text]")] == [
|
|
|
|
|
|
"example.gsa.gov",
|
|
|
|
|
|
"test.example.gsa.gov",
|
2020-08-07 10:48:21 +01:00
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
|
|
|
|
|
None,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("post_data", "expected_persisted"),
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"domains-0": "example.gsa.gov",
|
|
|
|
|
|
"domains-2": "example.gsa.gov",
|
|
|
|
|
|
"domains-3": "EXAMPLE.gsa.gov",
|
|
|
|
|
|
"domains-5": "test.gsa.gov",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"domains": [
|
|
|
|
|
|
"example.gsa.gov",
|
|
|
|
|
|
"test.gsa.gov",
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"domains-0": "",
|
|
|
|
|
|
"domains-1": "",
|
|
|
|
|
|
"domains-2": "",
|
|
|
|
|
|
},
|
|
|
|
|
|
{"domains": []},
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
2023-09-29 17:28:58 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_platform_admin_user(),
|
|
|
|
|
|
),
|
|
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_active_user_with_permissions(),
|
|
|
|
|
|
),
|
2023-09-29 17:28:58 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_domains(
|
2019-05-21 14:56:15 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
post_data,
|
|
|
|
|
|
expected_persisted,
|
|
|
|
|
|
user,
|
|
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if user["email_address"] == "platform@admin.gsa.gov":
|
2023-05-26 12:35:48 -07:00
|
|
|
|
expected_status = 302
|
|
|
|
|
|
expected_redirect = url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2023-05-26 12:35:48 -07:00
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
expected_status = 403
|
|
|
|
|
|
expected_redirect = None
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_domains",
|
2019-05-21 14:56:15 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
_data=post_data,
|
2023-05-26 12:35:48 -07:00
|
|
|
|
_expected_status=expected_status,
|
|
|
|
|
|
_expected_redirect=expected_redirect,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if user["email_address"] == "platform@admin.gsa.gov":
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_once_with(
|
2023-05-26 12:35:48 -07:00
|
|
|
|
ORGANISATION_ID,
|
|
|
|
|
|
**expected_persisted,
|
|
|
|
|
|
)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_domains_when_domain_already_exists(
|
2020-05-15 17:50:30 +01:00
|
|
|
|
mocker,
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
2020-05-15 17:50:30 +01:00
|
|
|
|
):
|
|
|
|
|
|
user = create_platform_admin_user()
|
|
|
|
|
|
client_request.login(user)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch(
|
|
|
|
|
|
"app.organizations_client.update_organization",
|
|
|
|
|
|
side_effect=HTTPError(
|
|
|
|
|
|
response=mocker.Mock(
|
|
|
|
|
|
status_code=400,
|
|
|
|
|
|
json={"result": "error", "message": "Domain already exists"},
|
|
|
|
|
|
),
|
|
|
|
|
|
message="Domain already exists",
|
2020-05-15 17:50:30 +01:00
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
response = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_domains",
|
2020-05-15 17:50:30 +01:00
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"domains": [
|
|
|
|
|
|
"example.gsa.gov",
|
2020-05-15 17:50:30 +01:00
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
response.find("div", class_="banner-dangerous").text.strip()
|
|
|
|
|
|
== "This domain is already in use"
|
|
|
|
|
|
)
|
2020-05-15 17:50:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_name(
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2022-01-11 14:24:25 +00:00
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".edit_organization_name",
|
2022-01-11 14:24:25 +00:00
|
|
|
|
org_id=fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_data={"name": "TestNewOrgName"},
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".organization_settings",
|
2022-01-11 14:24:25 +00:00
|
|
|
|
org_id=fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
),
|
2022-01-11 14:24:25 +00:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_once_with(
|
2022-01-11 14:24:25 +00:00
|
|
|
|
fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
name="TestNewOrgName",
|
2022-01-11 14:24:25 +00:00
|
|
|
|
cached_service_ids=None,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-29 17:28:58 -04:00
|
|
|
|
("name", "error_message"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
("", "Cannot be empty"),
|
|
|
|
|
|
("a", "at least two alphanumeric characters"),
|
|
|
|
|
|
("a" * 256, "Organization name must be 255 characters or fewer"),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_with_incorrect_input(
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
2020-11-30 17:08:40 +00:00
|
|
|
|
name,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
error_message,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".edit_organization_name",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
_data={"name": name},
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_expected_status=200,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert error_message in page.select_one(".usa-error-message").text
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_with_non_unique_name(
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2022-01-11 14:24:25 +00:00
|
|
|
|
fake_uuid,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
2022-01-11 14:24:25 +00:00
|
|
|
|
mocker,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
):
|
2022-01-11 14:24:25 +00:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.organizations_client.update_organization",
|
2022-01-11 14:24:25 +00:00
|
|
|
|
side_effect=HTTPError(
|
|
|
|
|
|
response=mocker.Mock(
|
|
|
|
|
|
status_code=400,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
json={"result": "error", "message": "Organization name already exists"},
|
2022-01-11 14:24:25 +00:00
|
|
|
|
),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
message="Organization name already exists",
|
|
|
|
|
|
),
|
2022-01-11 14:24:25 +00:00
|
|
|
|
)
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".edit_organization_name",
|
2022-01-11 14:24:25 +00:00
|
|
|
|
org_id=fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_data={"name": "TestNewOrgName"},
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_expected_status=200,
|
2019-05-21 14:56:15 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
"This organization name is already in use"
|
|
|
|
|
|
in page.select_one(".usa-error-message").text
|
|
|
|
|
|
)
|
2019-05-21 14:56:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_settings_links_to_edit_organization_notes_page(
|
2021-02-05 10:52:08 +00:00
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
organization_one,
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2021-02-05 10:52:08 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(".organization_settings", org_id=organization_one["id"])
|
|
|
|
|
|
assert (
|
|
|
|
|
|
len(
|
|
|
|
|
|
page.find_all(
|
|
|
|
|
|
"a",
|
|
|
|
|
|
attrs={
|
|
|
|
|
|
"href": "/organizations/{}/settings/notes".format(
|
|
|
|
|
|
organization_one["id"]
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
== 1
|
2021-12-30 16:13:49 +00:00
|
|
|
|
)
|
2021-02-04 18:00:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_view_edit_organization_notes(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
2021-02-04 18:00:41 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_notes",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2021-12-30 16:13:49 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("h1").text == "Edit organization notes"
|
|
|
|
|
|
assert page.find("label", class_="usa-label").text.strip() == "Notes"
|
|
|
|
|
|
assert page.find("textarea").attrs["name"] == "notes"
|
2021-02-04 17:58:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_notes(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2021-02-04 17:58:35 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_notes",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
_data={"notes": "Very fluffy"},
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2021-02-04 17:58:35 +00:00
|
|
|
|
),
|
|
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
organization_one["id"], cached_service_ids=None, notes="Very fluffy"
|
2021-02-04 17:58:35 +00:00
|
|
|
|
)
|
2021-02-04 18:19:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_notes_errors_when_user_not_platform_admin(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2021-02-16 11:39:22 +00:00
|
|
|
|
):
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_notes",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
_data={"notes": "Very fluffy"},
|
2021-02-16 11:39:22 +00:00
|
|
|
|
_expected_status=403,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_notes_doesnt_call_api_when_notes_dont_change(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_update_organization,
|
|
|
|
|
|
mocker,
|
2021-02-10 11:56:09 +00:00
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch(
|
|
|
|
|
|
"app.organizations_client.get_organization",
|
|
|
|
|
|
return_value=organization_json(
|
|
|
|
|
|
id_=organization_one["id"], name="Test Org", notes="Very fluffy"
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_notes",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
_data={"notes": "Very fluffy"},
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2021-02-10 11:56:09 +00:00
|
|
|
|
),
|
|
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
assert not mock_update_organization.called
|
2021-02-10 11:56:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_settings_links_to_edit_organization_billing_details_page(
|
2021-02-04 18:19:54 +00:00
|
|
|
|
mocker,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
organization_one,
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
2021-02-04 18:19:54 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
page = client_request.get(".organization_settings", org_id=organization_one["id"])
|
|
|
|
|
|
assert (
|
|
|
|
|
|
len(
|
|
|
|
|
|
page.find_all(
|
|
|
|
|
|
"a",
|
|
|
|
|
|
attrs={
|
|
|
|
|
|
"href": "/organizations/{}/settings/edit-billing-details".format(
|
|
|
|
|
|
organization_one["id"]
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
== 1
|
2021-12-30 16:13:49 +00:00
|
|
|
|
)
|
2021-02-05 11:56:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_view_edit_organization_billing_details(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
2021-02-05 11:56:05 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_billing_details",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2021-02-05 11:56:05 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("h1").text == "Edit organization billing details"
|
|
|
|
|
|
labels = page.find_all("label", class_="usa-label")
|
2021-02-05 11:56:05 +00:00
|
|
|
|
labels_list = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Contact email addresses",
|
|
|
|
|
|
"Contact names",
|
|
|
|
|
|
"Reference",
|
|
|
|
|
|
"Purchase order number",
|
|
|
|
|
|
"Notes",
|
2021-02-05 11:56:05 +00:00
|
|
|
|
]
|
|
|
|
|
|
for label in labels:
|
|
|
|
|
|
assert label.text.strip() in labels_list
|
2023-08-28 14:40:33 -04:00
|
|
|
|
textbox_names = page.find_all("input", class_="usa-input ")
|
2021-02-05 11:56:05 +00:00
|
|
|
|
names_list = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"billing_contact_email_addresses",
|
|
|
|
|
|
"billing_contact_names",
|
|
|
|
|
|
"billing_reference",
|
|
|
|
|
|
"purchase_order_number",
|
2021-02-05 11:56:05 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for name in textbox_names:
|
|
|
|
|
|
assert name.attrs["name"] in names_list
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.find("textarea").attrs["name"] == "notes"
|
2021-02-05 11:59:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_billing_details(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
platform_admin_user,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2021-02-05 11:59:31 +00:00
|
|
|
|
):
|
2021-12-30 16:13:49 +00:00
|
|
|
|
client_request.login(platform_admin_user)
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_billing_details",
|
|
|
|
|
|
org_id=organization_one["id"],
|
2021-12-30 16:13:49 +00:00
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"billing_contact_email_addresses": "accounts@fluff.gsa.gov",
|
|
|
|
|
|
"billing_contact_names": "Flannellette von Fluff",
|
|
|
|
|
|
"billing_reference": "",
|
|
|
|
|
|
"purchase_order_number": "PO1234",
|
|
|
|
|
|
"notes": "very fluffy, give extra allowance",
|
2021-12-30 16:13:49 +00:00
|
|
|
|
},
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.organization_settings",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
),
|
2021-02-05 11:59:31 +00:00
|
|
|
|
)
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_update_organization.assert_called_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
organization_one["id"],
|
2021-02-05 11:59:31 +00:00
|
|
|
|
cached_service_ids=None,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
billing_contact_email_addresses="accounts@fluff.gsa.gov",
|
|
|
|
|
|
billing_contact_names="Flannellette von Fluff",
|
|
|
|
|
|
billing_reference="",
|
|
|
|
|
|
purchase_order_number="PO1234",
|
|
|
|
|
|
notes="very fluffy, give extra allowance",
|
2021-02-05 11:59:31 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_update_organization_billing_details_errors_when_user_not_platform_admin(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
organization_one,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mock_update_organization,
|
2021-02-05 11:59:31 +00:00
|
|
|
|
):
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.edit_organization_billing_details",
|
|
|
|
|
|
org_id=organization_one["id"],
|
|
|
|
|
|
_data={"notes": "Very fluffy"},
|
2021-02-05 11:59:31 +00:00
|
|
|
|
_expected_status=403,
|
|
|
|
|
|
)
|
Add new 'Billing' page for organisations
We want organisation team members to be able to see the MOU details for
their organisation. This change creates a new page called billing, which
contains these details. It's only visible to platform admin users now -
the plan is to add more information to this page, then to make it visible
to all organisation users.
The page showing the MOU covers the case of when agreement_signed is
True, when an agreement_signed is False, and when agreement_signed is
None. The case when an agreement_signed is None is very rare - it
signifies that the agreement is not signed but that we have some
service-specific agreements in place. We only have a few organisations
in this state, so it's unlikely that the content for this scenario will
be seen.
When an organisation has signed the agreement we may know the full
details (signing date, version signed, the person who signed it or who it
was signed on behalf of), or we may only have the name of the person who
signed the agreement. We show the more detailed content if possible, and
a less detailed version of the content if not.
There's a new route for downloading the agreement which is almost
identical to the existing `.service_download_agreement` route (plus the
test is almost the same), except that it takes an organisation ID
instead of a service ID.
2021-12-07 13:42:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-07-12 12:09:44 -04:00
|
|
|
|
def test_organization_billing_page_not_accessible_if_not_platform_admin(
|
Add new 'Billing' page for organisations
We want organisation team members to be able to see the MOU details for
their organisation. This change creates a new page called billing, which
contains these details. It's only visible to platform admin users now -
the plan is to add more information to this page, then to make it visible
to all organisation users.
The page showing the MOU covers the case of when agreement_signed is
True, when an agreement_signed is False, and when agreement_signed is
None. The case when an agreement_signed is None is very rare - it
signifies that the agreement is not signed but that we have some
service-specific agreements in place. We only have a few organisations
in this state, so it's unlikely that the content for this scenario will
be seen.
When an organisation has signed the agreement we may know the full
details (signing date, version signed, the person who signed it or who it
was signed on behalf of), or we may only have the name of the person who
signed the agreement. We show the more detailed content if possible, and
a less detailed version of the content if not.
There's a new route for downloading the agreement which is almost
identical to the existing `.service_download_agreement` route (plus the
test is almost the same), except that it takes an organisation ID
instead of a service ID.
2021-12-07 13:42:44 +00:00
|
|
|
|
client_request,
|
2023-07-12 12:09:44 -04:00
|
|
|
|
mock_get_organization,
|
Add new 'Billing' page for organisations
We want organisation team members to be able to see the MOU details for
their organisation. This change creates a new page called billing, which
contains these details. It's only visible to platform admin users now -
the plan is to add more information to this page, then to make it visible
to all organisation users.
The page showing the MOU covers the case of when agreement_signed is
True, when an agreement_signed is False, and when agreement_signed is
None. The case when an agreement_signed is None is very rare - it
signifies that the agreement is not signed but that we have some
service-specific agreements in place. We only have a few organisations
in this state, so it's unlikely that the content for this scenario will
be seen.
When an organisation has signed the agreement we may know the full
details (signing date, version signed, the person who signed it or who it
was signed on behalf of), or we may only have the name of the person who
signed the agreement. We show the more detailed content if possible, and
a less detailed version of the content if not.
There's a new route for downloading the agreement which is almost
identical to the existing `.service_download_agreement` route (plus the
test is almost the same), except that it takes an organisation ID
instead of a service ID.
2021-12-07 13:42:44 +00:00
|
|
|
|
):
|
|
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".organization_billing", org_id=ORGANISATION_ID, _expected_status=403
|
Add new 'Billing' page for organisations
We want organisation team members to be able to see the MOU details for
their organisation. This change creates a new page called billing, which
contains these details. It's only visible to platform admin users now -
the plan is to add more information to this page, then to make it visible
to all organisation users.
The page showing the MOU covers the case of when agreement_signed is
True, when an agreement_signed is False, and when agreement_signed is
None. The case when an agreement_signed is None is very rare - it
signifies that the agreement is not signed but that we have some
service-specific agreements in place. We only have a few organisations
in this state, so it's unlikely that the content for this scenario will
be seen.
When an organisation has signed the agreement we may know the full
details (signing date, version signed, the person who signed it or who it
was signed on behalf of), or we may only have the name of the person who
signed the agreement. We show the more detailed content if possible, and
a less detailed version of the content if not.
There's a new route for downloading the agreement which is almost
identical to the existing `.service_download_agreement` route (plus the
test is almost the same), except that it takes an organisation ID
instead of a service ID.
2021-12-07 13:42:44 +00:00
|
|
|
|
)
|
2025-10-16 16:56:29 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_organization_dashboard_shows_message_usage(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
):
|
|
|
|
|
|
mock_message_usage = mocker.patch(
|
2025-10-20 12:26:07 -07:00
|
|
|
|
"app.organizations_client.get_organization_message_usage",
|
2025-10-16 16:56:29 -07:00
|
|
|
|
return_value={
|
|
|
|
|
|
"messages_sent": 1000,
|
|
|
|
|
|
"messages_remaining": 2000,
|
|
|
|
|
|
"total_message_limit": 3000,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
mocker.patch(
|
|
|
|
|
|
"app.organizations_client.get_organization_services",
|
|
|
|
|
|
return_value=[],
|
|
|
|
|
|
)
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
|
|
|
|
|
page = client_request.get(
|
|
|
|
|
|
".organization_dashboard",
|
|
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_message_usage.assert_called_once_with(ORGANISATION_ID)
|
|
|
|
|
|
|
|
|
|
|
|
assert normalize_spaces(page.select_one("h1").text) == "Organization Dashboard"
|
|
|
|
|
|
|
|
|
|
|
|
chart_container = page.select_one("#totalMessageChartContainer")
|
|
|
|
|
|
assert chart_container["data-messages-sent"] == "1000"
|
|
|
|
|
|
assert chart_container["data-messages-remaining"] == "2000"
|
|
|
|
|
|
assert chart_container["data-total-message-limit"] == "3000"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_organization_dashboard_shows_service_counts(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mock_get_organization,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
):
|
|
|
|
|
|
mocker.patch(
|
2025-10-20 12:28:34 -07:00
|
|
|
|
"app.organizations_client.get_organization_message_usage",
|
2025-10-16 16:56:29 -07:00
|
|
|
|
return_value={
|
|
|
|
|
|
"messages_sent": 0,
|
|
|
|
|
|
"messages_remaining": 0,
|
|
|
|
|
|
"total_message_limit": 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
mocker.patch(
|
|
|
|
|
|
"app.organizations_client.get_organization_services",
|
|
|
|
|
|
return_value=[
|
|
|
|
|
|
service_json(id_="1", name="Live Service", restricted=False, active=True),
|
|
|
|
|
|
service_json(id_="2", name="Trial Service", restricted=True, active=True),
|
|
|
|
|
|
service_json(id_="3", name="Suspended", restricted=False, active=False),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
|
|
|
|
|
page = client_request.get(
|
|
|
|
|
|
".organization_dashboard",
|
|
|
|
|
|
org_id=ORGANISATION_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
summary_boxes = page.select(".usa-summary-box")
|
|
|
|
|
|
assert len(summary_boxes) == 2
|
|
|
|
|
|
|
|
|
|
|
|
service_box = summary_boxes[0]
|
|
|
|
|
|
assert "Total Services" in normalize_spaces(service_box.text)
|
|
|
|
|
|
assert "3" in normalize_spaces(service_box.text)
|
|
|
|
|
|
assert "1 Live" in normalize_spaces(service_box.text)
|
|
|
|
|
|
assert "1 Trial" in normalize_spaces(service_box.text)
|
|
|
|
|
|
assert "1 Suspended" in normalize_spaces(service_box.text)
|