mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-18 13:39:41 -04:00
Optimizing polling (#2946)
* Optimizing polling * Fixed formatting issue
This commit is contained in:
141
tests/app/main/views/test_job_notification_updates.py
Normal file
141
tests/app/main/views/test_job_notification_updates.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Tests for job notification update logic during polling.
|
||||
|
||||
These tests verify the poll status endpoint behavior and document
|
||||
the JavaScript notification refresh logic:
|
||||
1. Notifications update for first 50 messages
|
||||
2. Notifications stop updating after 50 messages (to prevent performance issues)
|
||||
3. Notifications always update when job finishes
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from tests import job_json, user_json
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("delivered", "failed", "pending", "finished", "js_should_update_notifications", "reason"),
|
||||
[
|
||||
(20, 10, 70, False, True, "30 messages processed (≤50 threshold)"),
|
||||
(40, 10, 50, False, True, "50 messages processed (exactly at threshold)"),
|
||||
(45, 15, 40, False, False, "60 messages processed (>50 threshold)"),
|
||||
(450, 50, 0, True, True, "500 messages but job finished (always updates)"),
|
||||
],
|
||||
)
|
||||
def test_poll_status_notification_update_logic(
|
||||
client_request,
|
||||
service_one,
|
||||
active_user_with_permissions,
|
||||
mock_get_service_data_retention,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
delivered,
|
||||
failed,
|
||||
pending,
|
||||
finished,
|
||||
js_should_update_notifications,
|
||||
reason,
|
||||
):
|
||||
"""
|
||||
Test poll status endpoint for various scenarios.
|
||||
|
||||
The JavaScript updates notifications when:
|
||||
processedCount ≤ 50 AND job not finished
|
||||
job is finished (regardless of count)
|
||||
"""
|
||||
total = delivered + failed + pending
|
||||
job_status = "finished" if finished else "sending"
|
||||
|
||||
mock_job = mocker.patch("app.job_api_client.get_job")
|
||||
mock_job.return_value = {
|
||||
"data": {
|
||||
**job_json(
|
||||
service_one["id"],
|
||||
created_by=user_json(),
|
||||
job_id=fake_uuid,
|
||||
job_status=job_status,
|
||||
notification_count=total,
|
||||
notifications_requested=total,
|
||||
),
|
||||
"statistics": [
|
||||
{"status": "delivered", "count": delivered},
|
||||
{"status": "failed", "count": failed},
|
||||
{"status": "pending", "count": pending},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
response = client_request.get_response(
|
||||
"main.view_job_status_poll",
|
||||
service_id=service_one["id"],
|
||||
job_id=fake_uuid,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.get_data(as_text=True))
|
||||
|
||||
# Verify the response
|
||||
assert data["sent_count"] == delivered
|
||||
assert data["failed_count"] == failed
|
||||
assert data["pending_count"] == pending
|
||||
assert data["total_count"] == total
|
||||
assert data["finished"] is finished
|
||||
|
||||
processed_count = delivered + failed
|
||||
|
||||
if js_should_update_notifications:
|
||||
# JavaScript would call: await updateNotifications()
|
||||
if finished:
|
||||
assert finished, f"JS updates notifications: {reason}"
|
||||
else:
|
||||
assert processed_count <= 50, f"JS updates notifications: {reason}"
|
||||
assert not finished, f"JS updates notifications: {reason}"
|
||||
else:
|
||||
# JavaScript would NOT update notifications
|
||||
assert processed_count > 50, f"JS skips notification update: {reason}"
|
||||
assert not finished, f"JS skips notification update: {reason}"
|
||||
|
||||
|
||||
def test_poll_status_provides_required_fields(
|
||||
client_request,
|
||||
service_one,
|
||||
active_user_with_permissions,
|
||||
mock_get_service_data_retention,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
):
|
||||
"""Verify poll status endpoint returns all fields needed for notification update logic."""
|
||||
mock_job = mocker.patch("app.job_api_client.get_job")
|
||||
mock_job.return_value = {
|
||||
"data": {
|
||||
**job_json(
|
||||
service_one["id"],
|
||||
created_by=user_json(),
|
||||
job_id=fake_uuid,
|
||||
job_status="sending",
|
||||
notification_count=25,
|
||||
notifications_requested=25,
|
||||
),
|
||||
"statistics": [
|
||||
{"status": "delivered", "count": 15},
|
||||
{"status": "failed", "count": 5},
|
||||
{"status": "pending", "count": 5},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
response = client_request.get_response(
|
||||
"main.view_job_status_poll",
|
||||
service_id=service_one["id"],
|
||||
job_id=fake_uuid,
|
||||
)
|
||||
|
||||
data = json.loads(response.get_data(as_text=True))
|
||||
|
||||
required_fields = {"sent_count", "failed_count", "finished", "pending_count", "total_count"}
|
||||
assert set(data.keys()) == required_fields
|
||||
|
||||
response_size = len(response.get_data(as_text=True))
|
||||
assert response_size < 200, f"Response too large: {response_size} bytes"
|
||||
@@ -499,3 +499,143 @@ def test_should_show_message_note(
|
||||
'Messages are sent immediately to the cell phone carrier, but will remain in "pending" status until we hear '
|
||||
"back from the carrier they have received it and attempted deliver. More information on delivery status."
|
||||
)
|
||||
|
||||
|
||||
def test_poll_status_endpoint(
|
||||
client_request,
|
||||
service_one,
|
||||
active_user_with_permissions,
|
||||
mock_get_service_data_retention,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
):
|
||||
"""Test that the poll status endpoint returns only required data without notifications"""
|
||||
mock_job = mocker.patch("app.job_api_client.get_job")
|
||||
mock_job.return_value = {
|
||||
"data": {
|
||||
**job_json(
|
||||
service_one["id"],
|
||||
created_by=user_json(),
|
||||
job_id=fake_uuid,
|
||||
job_status="finished",
|
||||
notification_count=100,
|
||||
notifications_requested=100,
|
||||
),
|
||||
"statistics": [
|
||||
{"status": "delivered", "count": 90},
|
||||
{"status": "failed", "count": 10},
|
||||
{"status": "pending", "count": 0},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
response = client_request.get_response(
|
||||
"main.view_job_status_poll",
|
||||
service_id=service_one["id"],
|
||||
job_id=fake_uuid,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.get_data(as_text=True))
|
||||
|
||||
expected_keys = {
|
||||
"sent_count",
|
||||
"failed_count",
|
||||
"pending_count",
|
||||
"total_count",
|
||||
"finished",
|
||||
}
|
||||
assert set(data.keys()) == expected_keys
|
||||
|
||||
assert data["sent_count"] == 90
|
||||
assert data["failed_count"] == 10
|
||||
assert data["pending_count"] == 0
|
||||
assert data["total_count"] == 100
|
||||
assert data["finished"] is True
|
||||
|
||||
|
||||
def test_poll_status_with_zero_notifications(
|
||||
client_request,
|
||||
service_one,
|
||||
active_user_with_permissions,
|
||||
mock_get_service_data_retention,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
):
|
||||
"""Test poll status endpoint handles edge case of no notifications"""
|
||||
mock_job = mocker.patch("app.job_api_client.get_job")
|
||||
mock_job.return_value = {
|
||||
"data": {
|
||||
**job_json(
|
||||
service_one["id"],
|
||||
created_by=user_json(),
|
||||
job_id=fake_uuid,
|
||||
job_status="pending",
|
||||
notification_count=0,
|
||||
notifications_requested=0,
|
||||
),
|
||||
"statistics": [],
|
||||
}
|
||||
}
|
||||
|
||||
response = client_request.get_response(
|
||||
"main.view_job_status_poll",
|
||||
service_id=service_one["id"],
|
||||
job_id=fake_uuid,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.get_data(as_text=True))
|
||||
|
||||
assert data["total_count"] == 0
|
||||
assert (
|
||||
data["finished"] is True
|
||||
)
|
||||
|
||||
|
||||
def test_poll_status_endpoint_does_not_query_notifications_table(
|
||||
client_request,
|
||||
service_one,
|
||||
active_user_with_permissions,
|
||||
mock_get_service_data_retention,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
):
|
||||
"""Critical regression test: ensure poll status endpoint never queries notifications"""
|
||||
mock_job = mocker.patch("app.job_api_client.get_job")
|
||||
mock_job.return_value = {
|
||||
"data": {
|
||||
**job_json(
|
||||
service_one["id"],
|
||||
created_by=user_json(),
|
||||
job_id=fake_uuid,
|
||||
job_status="sending",
|
||||
notification_count=500,
|
||||
notifications_requested=500,
|
||||
),
|
||||
"statistics": [
|
||||
{"status": "delivered", "count": 300},
|
||||
{"status": "failed", "count": 50},
|
||||
{"status": "pending", "count": 150},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
mock_get_notifications = mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service"
|
||||
)
|
||||
|
||||
response = client_request.get_response(
|
||||
"main.view_job_status_poll",
|
||||
service_id=service_one["id"],
|
||||
job_id=fake_uuid,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify no notifications were fetched
|
||||
mock_get_notifications.assert_not_called()
|
||||
|
||||
data = json.loads(response.get_data(as_text=True))
|
||||
assert data["total_count"] == 500
|
||||
assert data["sent_count"] == 300
|
||||
|
||||
@@ -1533,9 +1533,7 @@ def test_should_be_able_to_move_to_new_folder(
|
||||
],
|
||||
},
|
||||
_expected_status=302,
|
||||
_expected_redirect=url_for(
|
||||
"main.choose_template", service_id=SERVICE_ONE_ID
|
||||
),
|
||||
_expected_redirect=url_for("main.choose_template", service_id=SERVICE_ONE_ID),
|
||||
)
|
||||
|
||||
mock_create_template_folder.assert_called_once_with(
|
||||
|
||||
@@ -242,6 +242,7 @@ EXCLUDED_ENDPOINTS = tuple(
|
||||
"verify_email",
|
||||
"view_job",
|
||||
"view_job_csv",
|
||||
"view_job_status_poll",
|
||||
"view_job_updates",
|
||||
"view_jobs",
|
||||
"view_notification",
|
||||
|
||||
Reference in New Issue
Block a user