mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-09-30 08:53:13 +00:00
Monday publish parses the catalog embedded in the Redefine menu page and runs on the Fargate worker, so the GitHub Actions scrape cron can go away.
142 lines
4.8 KiB
Python
142 lines
4.8 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 (
|
|
"https://orders.seahaven.com" in mock_slack.call_args.args[1][1]["text"]["text"]
|
|
)
|
|
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()
|