mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-10-01 14:03:11 +00:00
Use the form deadline in the Monday Slack post, and compare that message exactly so CodeQL does not treat the test as URL sanitization.
145 lines
5 KiB
Python
145 lines
5 KiB
Python
"""publish_menu writes the menu, then the form, then Slack."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from server.jobs import publish_menu
|
|
from server.jobs import run_job
|
|
|
|
|
|
MENU = {
|
|
"scraped_at": "2026-09-21T07:30:00-04:00",
|
|
"menu_url": "https://www.redefinemeals.com/menu",
|
|
"meal_count": 1,
|
|
"meals": [{"name": "Korean Steak Bowl", "price": 12.49}],
|
|
}
|
|
|
|
ENV = {
|
|
"MENU_URL": "https://www.redefinemeals.com/menu",
|
|
"GOOGLE_CLIENT_ID": "client.apps.googleusercontent.com",
|
|
"FORM_BUCKET": "meal-order-manager-form-test",
|
|
"DISTRIBUTION_ID": "E123",
|
|
"FORM_URL": "https://orders.seahaven.com",
|
|
"TABLE_NAME": "meal-order-manager-orders",
|
|
}
|
|
|
|
|
|
def _clients(s3, cloudfront):
|
|
def client(name, **_kwargs):
|
|
if name == "s3":
|
|
return s3
|
|
if name == "cloudfront":
|
|
return cloudfront
|
|
raise AssertionError(name)
|
|
|
|
return client
|
|
|
|
|
|
@patch("shared.slack.post_channel_message", return_value={"ok": True})
|
|
@patch("server.jobs.publish_menu.put_menu")
|
|
@patch(
|
|
"server.jobs.publish_menu.get_settings",
|
|
return_value={"bulk_discount_percent": 10, "company_subsidy_percent": 50},
|
|
)
|
|
@patch("server.jobs.publish_menu.generate_form", return_value="<html>form</html>")
|
|
@patch("server.jobs.publish_menu.current_week", return_value="2026-W38")
|
|
@patch("server.jobs.publish_menu.fetch_menu", return_value=MENU)
|
|
@patch("server.jobs.publish_menu.boto3.client")
|
|
def test_publish_menu_uploads_after_the_menu_write(
|
|
mock_client,
|
|
mock_fetch,
|
|
mock_week,
|
|
mock_generate,
|
|
mock_settings,
|
|
mock_put,
|
|
mock_slack,
|
|
):
|
|
s3 = MagicMock()
|
|
cloudfront = MagicMock()
|
|
mock_client.side_effect = _clients(s3, cloudfront)
|
|
order = []
|
|
mock_put.side_effect = lambda *args, **kwargs: order.append("menu")
|
|
s3.put_object.side_effect = lambda **kwargs: order.append(kwargs["Key"])
|
|
cloudfront.create_invalidation.side_effect = lambda **kwargs: order.append(
|
|
"invalidate"
|
|
)
|
|
mock_slack.side_effect = lambda *args, **kwargs: (
|
|
order.append("slack") or {"ok": True}
|
|
)
|
|
|
|
with patch.dict("os.environ", ENV, clear=False):
|
|
result = publish_menu.lambda_handler({}, None)
|
|
|
|
assert result == {"status": "published", "week": "2026-W38", "meal_count": 1}
|
|
mock_fetch.assert_called_once_with("https://www.redefinemeals.com/menu")
|
|
mock_week.assert_called_once()
|
|
mock_settings.assert_called_once()
|
|
mock_generate.assert_called_once()
|
|
assert mock_generate.call_args.kwargs["week"] == "2026-W38"
|
|
assert mock_generate.call_args.kwargs["google_client_id"] == ENV["GOOGLE_CLIENT_ID"]
|
|
assert mock_generate.call_args.kwargs["bulk_discount"] == 10
|
|
assert mock_generate.call_args.kwargs["company_subsidy"] == 50
|
|
mock_put.assert_called_once_with("2026-W38", MENU)
|
|
assert s3.put_object.call_count == 2
|
|
index = s3.put_object.call_args_list[0].kwargs
|
|
archive = s3.put_object.call_args_list[1].kwargs
|
|
assert index["Bucket"] == ENV["FORM_BUCKET"]
|
|
assert index["Key"] == "index.html"
|
|
assert index["CacheControl"] == "no-cache"
|
|
assert archive["Key"] == "archive/2026-W38.html"
|
|
invalidation = cloudfront.create_invalidation.call_args.kwargs
|
|
assert invalidation["DistributionId"] == "E123"
|
|
assert invalidation["InvalidationBatch"]["Paths"]["Items"] == ["/index.html"]
|
|
mock_slack.assert_called_once()
|
|
assert mock_slack.call_args.args[0] == (
|
|
"This week's meal order is open! Deadline: Thursday at 11:59 PM."
|
|
)
|
|
assert mock_slack.call_args.args[1][1]["text"]["text"] == (
|
|
f"*<{ENV['FORM_URL']}|Place your order>*\n\n*Deadline:* Thursday at 11:59 PM\n"
|
|
)
|
|
assert order == [
|
|
"menu",
|
|
"index.html",
|
|
"archive/2026-W38.html",
|
|
"invalidate",
|
|
"slack",
|
|
]
|
|
|
|
|
|
@patch("shared.slack.post_channel_message", return_value={"ok": True})
|
|
@patch("server.jobs.publish_menu.put_menu")
|
|
@patch("server.jobs.publish_menu.get_settings", return_value={})
|
|
@patch("server.jobs.publish_menu.generate_form", return_value="<html>form</html>")
|
|
@patch("server.jobs.publish_menu.current_week", return_value="2026-W38")
|
|
@patch("server.jobs.publish_menu.fetch_menu", return_value=MENU)
|
|
@patch("server.jobs.publish_menu.boto3.client")
|
|
def test_publish_menu_does_not_notify_when_upload_fails(
|
|
mock_client,
|
|
_fetch,
|
|
_week,
|
|
_generate,
|
|
_settings,
|
|
mock_put,
|
|
mock_slack,
|
|
):
|
|
s3 = MagicMock()
|
|
s3.put_object.side_effect = RuntimeError("s3 down")
|
|
mock_client.side_effect = _clients(s3, MagicMock())
|
|
|
|
with patch.dict("os.environ", ENV, clear=False):
|
|
with pytest.raises(RuntimeError, match="s3 down"):
|
|
publish_menu.lambda_handler({}, None)
|
|
|
|
mock_put.assert_called_once()
|
|
mock_slack.assert_not_called()
|
|
|
|
|
|
def test_run_job_dispatches_publish_menu():
|
|
with patch(
|
|
"server.jobs.publish_menu.lambda_handler", return_value={"status": "published"}
|
|
) as handler:
|
|
result = run_job({"event": "publish_menu"})
|
|
|
|
assert result == {"status": "published"}
|
|
handler.assert_called_once()
|