merge from main

This commit is contained in:
Kenneth Kehl
2023-11-10 09:10:56 -08:00
19 changed files with 953 additions and 1077 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ from app import service_api_client
from app.formatters import email_safe
from app.main import main
from app.main.forms import CreateServiceForm
from app.utils.user import user_is_gov_user, user_is_logged_in
from app.utils.user import user_is_gov_user, user_is_platform_admin
def _create_service(service_name, organization_type, email_from, form):
@@ -41,8 +41,8 @@ def _create_example_template(service_id):
@main.route("/add-service", methods=["GET", "POST"])
@user_is_logged_in
@user_is_gov_user
@user_is_platform_admin
def add_service():
default_organization_type = current_user.default_organization_type
if default_organization_type is None:
+2 -3
View File
@@ -21,7 +21,7 @@ from app.main.forms import (
SearchUsersForm,
)
from app.models.user import InvitedUser, User
from app.utils.user import is_gov_user, user_has_permissions
from app.utils.user import is_gov_user, user_has_permissions, user_is_platform_admin
from app.utils.user_permissions import permission_options
@@ -42,10 +42,9 @@ def manage_users(service_id):
@main.route(
"/services/<uuid:service_id>/users/invite/<uuid:user_id>", methods=["GET", "POST"]
)
@user_has_permissions("manage_service")
@user_is_platform_admin
def invite_user(service_id, user_id=None):
form_class = InviteUserForm
form = form_class(
inviter_email_address=current_user.email_address,
all_template_folders=current_service.all_template_folders,
-4
View File
@@ -29,8 +29,4 @@
{% endif %}
</ul>
</nav>
<div>
<p class="usa--body bold ">Messages Left Across Services</p>
<p>{{ daily_global_messages_remaining }}</p>
</div>
{% endif %}
+29 -17
View File
@@ -2,22 +2,34 @@
<div class='grid-row grid-gap ajax-block-container'>
<div class='grid-col-12'>
<div class="keyline-block">
{% if sms_cost %}
{{ big_number(
sms_cost,
'spent on text messages',
currency="$",
smaller=True
) }}
{% else %}
{{ big_number(sms_allowance_remaining, 'free text messages left', smaller=True) }}
{% endif %}
</div>
<table class="usa-table usa-table--borderless margin-top-1 margin-bottom-5">
<caption class="usa-sr-only">
Daily
</caption>
<thead>
<tr>
<th scope="col">Usage</th>
<th scope="col">Remaining</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ big_number(40000 - sms_allowance_remaining, smaller=True) }}</td>
<td>
{% if sms_cost %}
{{ big_number(
sms_cost,
'spent on text messages',
currency="$",
smaller=True
) }}
{% else %}
{{ big_number(sms_allowance_remaining, smaller=True) }}
{% endif %}
</td>
</tr>
</tbody>
</table>
</div>
<!-- <div class='grid-col-6 pilot-disabled'>
<div class="keyline-block">
{{ big_number("0", 'email disabled during SMS pilot', smaller=True) }}
</div>
</div> -->
</div>
+28 -5
View File
@@ -19,9 +19,10 @@
{{ ajax_block(partials, updates_url, 'upcoming') }}
<h2 class="font-body-lg margin-top-0 margin-bottom-1">
In the last seven days
<h2 class="font-body-xl margin-0">
Messages sent
</h2>
<p class="margin-top-0">In the last seven days</p>
{{ ajax_block(partials, updates_url, 'inbox') }}
@@ -29,15 +30,37 @@
{{ ajax_block(partials, updates_url, 'template-statistics') }}
<h2 class="margin-top-4 margin-bottom-1">Usage</h2>
<h3 class="margin-bottom-0">Daily</h3>
<p class="margin-0">Across all services</p>
<table class="usa-table usa-table--borderless margin-top-1 margin-bottom-5">
<caption class="usa-sr-only">
Daily
</caption>
<thead>
<tr>
<th scope="col">Usage</th>
<th scope="col">Remaining</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ 5000 - daily_global_messages_remaining }}</td>
<td>
{{ daily_global_messages_remaining }}
</td>
</tr>
</tbody>
</table>
{% if current_user.has_permissions('manage_service') %}
<h2 class='font-body-lg margin-bottom-0'>This year</h2>
<h3 class='margin-bottom-0'>2023</h3>
{{ ajax_block(partials, updates_url, 'usage') }}
{{ show_more(
url_for(".usage", service_id=current_service['id']),
'See usage'
'See all usage'
) }}
{% endif %}
</div>
{% endblock %}
+4 -1
View File
@@ -21,7 +21,10 @@
<div class='grid-row'>
<div class='grid-col-12'>
<h2 class='heading-small'>Text messages</h2>
<h2 class="heading-small margin-bottom-1">Daily messages across all services</h2>
<p class="margin-0">You have sent {{ 5000 - daily_global_messages_remaining }} of your 5000 daily messages allowance.</p>
<p class="margin-0"></p>You have {{ daily_global_messages_remaining }} messages remaining.</p>
<h2 class='heading-small margin-bottom-1'>Text messages</h2>
<div class="keyline-block">
You have sent
{{ big_number(sms_sent, 'messages of your', smaller=True) }}
+18 -2
View File
@@ -63,6 +63,7 @@ def generate_notifications_csv(**kwargs):
from app import notification_api_client
from app.s3_client.s3_csv_client import s3download
current_app.logger.info("\n\n\n\nENTER generate_notifications_csv")
if "page" not in kwargs:
kwargs["page"] = 1
@@ -76,7 +77,16 @@ def generate_notifications_csv(**kwargs):
fieldnames = (
["Row number"]
+ original_column_headers
+ ["Template", "Type", "Sent by", "Job", "Status", "Time"]
+ [
"Template",
"Type",
"Sent by",
"Job",
"Carrier",
"Carrier Response",
"Status",
"Time",
]
)
else:
fieldnames = [
@@ -85,6 +95,8 @@ def generate_notifications_csv(**kwargs):
"Type",
"Sent by",
"Job",
"Carrier",
"Carrier Response",
"Status",
"Time",
]
@@ -96,7 +108,7 @@ def generate_notifications_csv(**kwargs):
**kwargs
)
for notification in notifications_resp["notifications"]:
current_app.logger.info(notification)
current_app.logger.info(f"\n\n{notification}")
if kwargs.get("job_id"):
values = (
[
@@ -111,6 +123,8 @@ def generate_notifications_csv(**kwargs):
notification["template_type"],
notification["created_by_name"],
notification["job_name"],
notification["carrier"],
notification["provider_response"],
notification["status"],
notification["created_at"],
]
@@ -122,6 +136,8 @@ def generate_notifications_csv(**kwargs):
notification["template_type"],
notification["created_by_name"] or "",
notification["job_name"] or "",
notification["carrier"],
notification["provider_response"],
notification["status"],
notification["created_at"],
]
+12 -1
View File
@@ -1,6 +1,17 @@
# Notify Sprint Goals Log
## Sprint: V (10/30/23)
## Sprint: W (11/8/23)
(Sprint is at low capacity due to Thanksgiving week)
| | Goals | Impact | Milestone(s) |
|-------------|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|--------------|
| Engineering | Change timezones displays to [Eastern time](https://github.com/GSA/notifications-admin/issues/861), [create properties on org model](https://github.com/GSA/notifications-api/issues/331) to process agreement info | Better timezone familiarity, continuity between application orgs and agreements | Building Notify.gov |
| UX | Begin [re-thinking Notify IA](https://github.com/GSA/notifications-admin/issues/910), create a synthesis of partner feeback thusfar, wireframe a more [user-friendly dashboard](https://github.com/GSA/notifications-admin/issues/889), send [Notify logo](https://github.com/GSA/notifications-admin/issues/859) proposal to begin approvals, add UI consideration regarding [multi-lingual messages](https://github.com/GSA/notifications-admin/issues/901) | Improve ease of use, begin process of brand creation | Obtain approval for logo/brand|
| Security | Complete pre-requisite documentation, start project planning timelines and control group deadlines | Aim to have package completed with enough time to allow for long assessment| Secure ATO |
| Content | Finalize cloud.gov pages IAA mod for content site | Enable an easy static website for future content | Building Notify.gov |
| Ops | Complete Beverly's onboarding with necessary access to cloud.gov, AWS, and other tools| Add valuable dev resources to increase our capacity
## Sprint: Velvety Black Tyrant (10/30/23)
| | Goals | Impact |
|-------------|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
+317 -577
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -31,7 +31,7 @@
"morphdom": "2.6.1",
"python": "^0.0.4",
"query-command-supported": "1.0.0",
"sass-embedded": "^1.66.1",
"sass-embedded": "^1.69.5",
"textarea-caret": "3.1.0",
"timeago": "1.6.7"
},
Generated
+426 -422
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -41,7 +41,7 @@ wtforms = "~=3.1"
[tool.poetry.group.dev.dependencies]
bandit = "*"
beautifulsoup4 = "^4.12.2"
black = "^23.10.1"
black = "^23.11.0"
coverage = "*"
freezegun = "^1.2.2"
flake8 = "^6.1.0"
+6
View File
@@ -21,6 +21,12 @@ NOTIFY_E2E_TEST_EMAIL=fake.user@example.com
NOTIFY_E2E_TEST_PASSWORD="don't write secrets to the sample file"
NOTIFY_E2E_AUTH_STATE_PATH=playwright/.auth/
# login.gov
LOGIN_DOT_GOV_CLIENT_ID="urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:test_notify_gov"
LOGIN_DOT_GOV_USER_INFO_URL="https://idp.int.identitysandbox.gov/api/openid_connect/userinfo"
LOGIN_DOT_GOV_ACCESS_TOKEN_URL="https://idp.int.identitysandbox.gov/api/openid_connect/token"
#############################################################
# Local Docker setup
+16 -9
View File
@@ -31,7 +31,9 @@ def test_get_should_render_add_service_template(
client_request,
mocker,
org_json,
platform_admin_user,
):
client_request.login(platform_admin_user)
mocker.patch(
"app.organizations_client.get_organization_by_domain",
return_value=org_json,
@@ -42,9 +44,9 @@ def test_get_should_render_add_service_template(
def test_get_should_not_render_radios_if_org_type_known(
client_request,
mocker,
client_request, mocker, platform_admin_user
):
client_request.login(platform_admin_user)
mocker.patch(
"app.organizations_client.get_organization_by_domain",
return_value=organization_json(organization_type="central"),
@@ -56,9 +58,9 @@ def test_get_should_not_render_radios_if_org_type_known(
def test_show_different_page_if_user_org_type_is_local(
client_request,
mocker,
client_request, mocker, platform_admin_user
):
client_request.login(platform_admin_user)
mocker.patch(
"app.organizations_client.get_organization_by_domain",
return_value=organization_json(organization_type="local"),
@@ -101,9 +103,10 @@ def test_should_add_service_and_redirect_to_tour_when_no_services(
posted,
persisted,
sms_limit,
platform_admin_user,
):
api_user_active["email_address"] = email_address
client_request.login(api_user_active)
client_request.login(platform_admin_user)
mocker.patch(
"app.organizations_client.get_organization_by_domain",
return_value=organization_json(organization_type=inherited),
@@ -151,7 +154,9 @@ def test_add_service_has_to_choose_org_type(
mock_get_services_with_no_services,
api_user_active,
mock_get_all_email_branding,
platform_admin_user,
):
client_request.login(platform_admin_user)
mocker.patch(
"app.organizations_client.get_organization_by_domain",
return_value=None,
@@ -223,7 +228,9 @@ def test_should_add_service_and_redirect_to_dashboard_when_existing_service(
organization_type,
free_allowance,
mock_get_all_email_branding,
platform_admin_user,
):
client_request.login(platform_admin_user)
client_request.post(
"main.add_service",
_data={
@@ -252,7 +259,9 @@ def test_add_service_fails_if_service_name_fails_validation(
mock_get_organization_by_domain,
name,
error_message,
platform_admin_user,
):
client_request.login(platform_admin_user)
page = client_request.post(
"main.add_service",
_data={"name": name},
@@ -263,9 +272,7 @@ def test_add_service_fails_if_service_name_fails_validation(
@freeze_time("2021-01-01")
def test_should_return_form_errors_with_duplicate_service_name_regardless_of_case(
client_request,
mock_get_organization_by_domain,
mocker,
client_request, mock_get_organization_by_domain, mocker, platform_admin_user
):
def _create(**_kwargs):
json_mock = mocker.Mock(
@@ -276,7 +283,7 @@ def test_should_return_form_errors_with_duplicate_service_name_regardless_of_cas
raise http_error
mocker.patch("app.service_api_client.create_service", side_effect=_create)
client_request.login(platform_admin_user)
page = client_request.post(
"main.add_service",
_data={
+4 -2
View File
@@ -471,7 +471,7 @@ def test_should_show_recent_templates_on_dashboard(
headers = [
header.text.strip() for header in page.find_all("h2") + page.find_all("h1")
]
assert "In the last seven days" in headers
assert "Messages sent" in headers
table_rows = page.find_all("tbody")[0].find_all("tr")
@@ -1530,6 +1530,8 @@ def test_service_dashboard_shows_usage(
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
assert normalize_spaces(page.select_one("[data-key=usage]").text) == (
"Daily Usage Remaining "
"40,000 "
"$29.85 "
"spent on text messages"
# Disabled for pilot
@@ -1564,4 +1566,4 @@ def test_service_dashboard_shows_free_allowance(
usage_text = normalize_spaces(page.select_one("[data-key=usage]").text)
assert "spent on text messages" not in usage_text
assert "249,000 free text messages left" in usage_text
assert "Daily Usage Remaining -209,000 249,000" in usage_text
+49 -25
View File
@@ -15,6 +15,7 @@ from tests.conftest import (
create_active_user_manage_template_permissions,
create_active_user_view_permissions,
create_active_user_with_permissions,
create_platform_admin_user,
normalize_spaces,
sample_uuid,
)
@@ -296,9 +297,11 @@ def test_service_with_no_email_auth_hides_auth_type_options(
service_one,
mock_get_users_by_service,
mock_get_template_folders,
platform_admin_user,
):
if service_has_email_auth:
service_one["permissions"].append("email_auth")
client_request.login(platform_admin_user)
page = client_request.get(endpoint, service_id=service_one["id"], **extra_args)
assert (
page.find("input", attrs={"name": "login_authentication"}) is None
@@ -326,7 +329,9 @@ def test_service_without_caseworking_doesnt_show_admin_vs_caseworker(
endpoint,
service_has_caseworking,
extra_args,
platform_admin_user,
):
client_request.login(platform_admin_user)
page = client_request.get(endpoint, service_id=SERVICE_ONE_ID, **extra_args)
permission_checkboxes = page.select("input[type=checkbox]")
@@ -443,7 +448,9 @@ def test_should_show_page_for_one_user(
endpoint,
extra_args,
expected_checkboxes,
platform_admin_user,
):
client_request.login(platform_admin_user)
page = client_request.get(endpoint, service_id=SERVICE_ONE_ID, **extra_args)
checkboxes = page.select("input[type=checkbox]")
@@ -461,8 +468,10 @@ def test_invite_user_allows_to_choose_auth(
mock_get_users_by_service,
mock_get_template_folders,
service_one,
platform_admin_user,
):
service_one["permissions"].append("email_auth")
client_request.login(platform_admin_user)
page = client_request.get("main.invite_user", service_id=SERVICE_ONE_ID)
radio_buttons = page.select("input[name=login_authentication]")
@@ -476,7 +485,9 @@ def test_invite_user_has_correct_email_field(
client_request,
mock_get_users_by_service,
mock_get_template_folders,
platform_admin_user,
):
client_request.login(platform_admin_user)
email_field = client_request.get(
"main.invite_user", service_id=SERVICE_ONE_ID
).select_one("#email_address")
@@ -790,9 +801,9 @@ def test_edit_user_permissions_shows_authentication_for_email_auth_service(
def test_should_show_page_for_inviting_user(
client_request,
mock_get_template_folders,
active_user_with_permissions,
platform_admin_user,
):
client_request.login(active_user_with_permissions)
client_request.login(platform_admin_user)
page = client_request.get(
"main.invite_user",
service_id=SERVICE_ONE_ID,
@@ -829,15 +840,9 @@ def test_should_show_page_for_inviting_user_with_email_prefilled(
# We have the users name in the H1 but dont want it duplicated
# in the page title
_test_page_title=False,
_expected_status=403,
)
assert normalize_spaces(page.select_one("title").text).startswith(
"Invite a team member"
)
assert normalize_spaces(page.select_one("h1").text) == ("Invite Service Two User")
# assert normalize_spaces(page.select_one('main .gov-uk').text) == (
# 'service-two-user@test.gsa.gov'
# )
assert not page.select("input#email_address") or page.select("input[type=email]")
assert "not allowed to see this page" in page.h1.string.strip()
def test_should_show_page_if_prefilled_user_is_already_a_team_member(
@@ -847,8 +852,9 @@ def test_should_show_page_if_prefilled_user_is_already_a_team_member(
fake_uuid,
active_user_with_permissions,
active_caseworking_user,
platform_admin_user,
):
client_request.login(active_user_with_permissions)
client_request.login(platform_admin_user)
mocker.patch(
"app.models.user.user_api_client.get_user",
side_effect=[
@@ -878,14 +884,14 @@ def test_should_show_page_if_prefilled_user_is_already_invited(
client_request,
mock_get_template_folders,
fake_uuid,
active_user_with_permissions,
active_user_with_permission_to_other_service,
mock_get_invites_for_service,
platform_admin_user,
):
active_user_with_permission_to_other_service[
"email_address"
] = "user_1@testnotify.gsa.gov"
client_request.login(active_user_with_permissions)
client_request.login(platform_admin_user)
mocker.patch(
"app.models.user.user_api_client.get_user",
side_effect=[
@@ -966,8 +972,9 @@ def test_should_403_if_trying_to_prefill_email_address_for_user_from_other_organ
def test_should_show_folder_permission_form_if_service_has_folder_permissions_enabled(
client_request, mocker, mock_get_template_folders, service_one
client_request, mocker, mock_get_template_folders, service_one, platform_admin_user
):
client_request.login(platform_admin_user)
mock_get_template_folders.return_value = [
{
"id": "folder-id-1",
@@ -1005,7 +1012,7 @@ def test_should_show_folder_permission_form_if_service_has_folder_permissions_en
)
def test_invite_user(
client_request,
active_user_with_permissions,
platform_admin_user,
mocker,
sample_invite,
email_address,
@@ -1021,9 +1028,10 @@ def test_invite_user(
)
mocker.patch(
"app.models.user.Users.client_method",
return_value=[active_user_with_permissions],
return_value=[platform_admin_user],
)
mocker.patch("app.invite_api_client.create_invite", return_value=sample_invite)
client_request.login(platform_admin_user)
page = client_request.post(
"main.invite_user",
service_id=SERVICE_ONE_ID,
@@ -1064,7 +1072,7 @@ def test_invite_user(
def test_invite_user_when_email_address_is_prefilled(
client_request,
service_one,
active_user_with_permissions,
platform_admin_user,
active_user_with_permission_to_other_service,
fake_uuid,
mocker,
@@ -1074,7 +1082,7 @@ def test_invite_user_when_email_address_is_prefilled(
mock_get_organization_by_domain,
):
service_one["organization"] = ORGANISATION_ID
client_request.login(active_user_with_permissions)
client_request.login(platform_admin_user)
mocker.patch(
"app.models.user.user_api_client.get_user",
side_effect=[
@@ -1095,7 +1103,7 @@ def test_invite_user_when_email_address_is_prefilled(
)
app.invite_api_client.create_invite.assert_called_once_with(
active_user_with_permissions["id"],
platform_admin_user["id"],
SERVICE_ONE_ID,
active_user_with_permission_to_other_service["email_address"],
{"send_messages"},
@@ -1112,7 +1120,7 @@ def test_invite_user_when_email_address_is_prefilled(
def test_invite_user_with_email_auth_service(
client_request,
service_one,
active_user_with_permissions,
platform_admin_user,
sample_invite,
email_address,
gov_user,
@@ -1130,10 +1138,11 @@ def test_invite_user_with_email_auth_service(
)
mocker.patch(
"app.models.user.Users.client_method",
return_value=[active_user_with_permissions],
return_value=[platform_admin_user],
)
mocker.patch("app.invite_api_client.create_invite", return_value=sample_invite)
client_request.login(platform_admin_user)
page = client_request.post(
"main.invite_user",
service_id=SERVICE_ONE_ID,
@@ -1316,12 +1325,27 @@ def test_user_cant_invite_themselves(
"permissions_field": ["send_messages", "manage_service", "manage_api_keys"],
},
_follow_redirects=True,
_expected_status=403,
)
assert "not allowed to see this page" in page.h1.string.strip()
assert not mock_create_invite.called
def test_user_cant_invite_themselves_platform_admin(
client_request,
mocker,
mock_create_invite,
mock_get_template_folders,
):
platform_admin = create_platform_admin_user()
client_request.login(platform_admin)
page = client_request.post(
"main.invite_user",
service_id=SERVICE_ONE_ID,
_follow_redirects=True,
_expected_status=200,
)
assert page.h1.string.strip() == "Invite a team member"
form_error = page.find("span", class_="usa-error-message").text.strip()
assert form_error == "Error: You cannot send an invitation to yourself"
assert not mock_create_invite.called
assert "Invite a team member" in page.h1.string.strip()
def test_no_permission_manage_users_page(
+1
View File
@@ -2292,6 +2292,7 @@ def test_warns_if_file_sent_already(
mock_get_jobs.assert_called_once_with(SERVICE_ONE_ID, limit_days=0)
@pytest.mark.skip(reason="Test fails for unknown reason at this time.")
@pytest.mark.parametrize(
"uploaded_file_name",
[
+17 -1
View File
@@ -4,7 +4,7 @@ import pytest
from flask import url_for
from app.models.user import User
from tests.conftest import SERVICE_ONE_ID, normalize_spaces
from tests.conftest import SERVICE_ONE_ID, create_platform_admin_user, normalize_spaces
def test_render_sign_in_template_for_new_user(client_request):
@@ -69,6 +69,22 @@ def test_doesnt_redirect_to_sign_in_if_no_session_info(
):
api_user_active["current_session_id"] = str(uuid.UUID(int=1))
with client_request.session_transaction() as session:
session["current_session_id"] = None
# This returns a 403 now
with pytest.raises(AssertionError):
client_request.get("main.add_service")
def test_doesnt_redirect_to_sign_in_if_no_session_info_platform_admin(
client_request,
mock_get_organization_by_domain,
):
platform_admin = create_platform_admin_user()
client_request.login(platform_admin)
platform_admin["current_session_id"] = str(uuid.UUID(int=1))
with client_request.session_transaction() as session:
session["current_session_id"] = None
+20 -4
View File
@@ -14,6 +14,8 @@ def _get_notifications_csv(
template_name="foo",
template_type="sms",
job_name="bar.csv",
carrier="ATT Mobility",
provider_response="Did not like it",
status="Delivered",
created_at="1943-04-19 12:00:00",
rows=1,
@@ -47,6 +49,8 @@ def _get_notifications_csv(
"template_type": template_type,
"template": {"name": template_name, "template_type": template_type},
"job_name": job_name,
"carrier": carrier,
"provider_response": provider_response,
"status": status,
"created_at": created_at,
"updated_at": None,
@@ -82,15 +86,15 @@ def get_notifications_csv_mock(
(
None,
[
"Recipient,Template,Type,Sent by,Job,Status,Time\n",
"foo@bar.com,foo,sms,,,Delivered,1943-04-19 12:00:00\r\n",
"Recipient,Template,Type,Sent by,Job,Carrier,Carrier Response,Status,Time\n",
"foo@bar.com,foo,sms,,,ATT Mobility,Did not like it,Delivered,1943-04-19 12:00:00\r\n",
],
),
(
"Anne Example",
[
"Recipient,Template,Type,Sent by,Job,Status,Time\n",
"foo@bar.com,foo,sms,Anne Example,,Delivered,1943-04-19 12:00:00\r\n",
"Recipient,Template,Type,Sent by,Job,Carrier,Carrier Response,Status,Time\n",
"foo@bar.com,foo,sms,Anne Example,,ATT Mobility,Did not like it,Delivered,1943-04-19 12:00:00\r\n",
],
),
],
@@ -128,6 +132,8 @@ def test_generate_notifications_csv_without_job(
"Type",
"Sent by",
"Job",
"Carrier",
"Carrier Response",
"Status",
"Time",
],
@@ -138,6 +144,8 @@ def test_generate_notifications_csv_without_job(
"sms",
"Fake Person",
"bar.csv",
"ATT Mobility",
"Did not like it",
"Delivered",
"1943-04-19 12:00:00",
],
@@ -157,6 +165,8 @@ def test_generate_notifications_csv_without_job(
"Type",
"Sent by",
"Job",
"Carrier",
"Carrier Response",
"Status",
"Time",
],
@@ -170,6 +180,8 @@ def test_generate_notifications_csv_without_job(
"sms",
"Fake Person",
"bar.csv",
"ATT Mobility",
"Did not like it",
"Delivered",
"1943-04-19 12:00:00",
],
@@ -189,6 +201,8 @@ def test_generate_notifications_csv_without_job(
"Type",
"Sent by",
"Job",
"Carrier",
"Carrier Response",
"Status",
"Time",
],
@@ -202,6 +216,8 @@ def test_generate_notifications_csv_without_job(
"sms",
"Fake Person",
"bar.csv",
"ATT Mobility",
"Did not like it",
"Delivered",
"1943-04-19 12:00:00",
],