Apply ruff formatting

This commit is contained in:
Adam Moussa 2026-05-13 13:25:40 -04:00
parent 10667c4362
commit fa8f19660d
8 changed files with 510 additions and 184 deletions

View file

@ -87,7 +87,11 @@ def handle_orders_aggregated(event):
totals_text = (
f"*${grand_total:.2f}* order total (bulk rate)\n"
f"*${employee_total:.2f}* payroll deductions"
+ (f"\n*${grand_total - employee_total:.2f}* company subsidy" if has_subsidy else "")
+ (
f"\n*${grand_total - employee_total:.2f}* company subsidy"
if has_subsidy
else ""
)
)
text = f"Meal orders closed for {week}. {total_employees} employees, {total_meals} meals, ${grand_total:.2f} total."
@ -137,7 +141,9 @@ def handle_order_confirmed(event):
for item in items:
qty = int(item.get("quantity", 0))
price = float(item.get("price", 0))
item_lines.append(f"{_escape_mrkdwn(item['name'])} x{qty} — your cost: ${price * qty:.2f}")
item_lines.append(
f"{_escape_mrkdwn(item['name'])} x{qty} — your cost: ${price * qty:.2f}"
)
item_list = "\n".join(item_lines)
send_dm(

View file

@ -161,7 +161,9 @@ def handle_submit(event):
return response(403, {"error": "Google authentication is required"})
user_info, verify_status = _verify_google_token(google_token)
if verify_status == "unavailable":
return response(503, {"error": "Authentication service temporarily unavailable"})
return response(
503, {"error": "Authentication service temporarily unavailable"}
)
if user_info is None:
return response(403, {"error": "Invalid or unauthorized Google account"})
name = user_info["name"]
@ -170,7 +172,9 @@ def handle_submit(event):
# Google auth not configured but token provided — verify it anyway
user_info, verify_status = _verify_google_token(google_token)
if verify_status == "unavailable":
return response(503, {"error": "Authentication service temporarily unavailable"})
return response(
503, {"error": "Authentication service temporarily unavailable"}
)
if user_info is None:
return response(403, {"error": "Invalid or unauthorized Google account"})
name = user_info["name"]
@ -216,7 +220,9 @@ def handle_submit(event):
# Step 1: apply bulk discount and round
bulk_price = (retail * bulk_mult).quantize(TWO_PLACES, rounding=ROUND_HALF_UP)
# Step 2: apply company subsidy and round
emp_price = (bulk_price * subsidy_mult).quantize(TWO_PLACES, rounding=ROUND_HALF_UP)
emp_price = (bulk_price * subsidy_mult).quantize(
TWO_PLACES, rounding=ROUND_HALF_UP
)
subtotal = (emp_price * qty).quantize(TWO_PLACES, rounding=ROUND_HALF_UP)
# Convert back to float for JSON serialization
item["retail_price"] = float(retail)
@ -224,7 +230,11 @@ def handle_submit(event):
item["price"] = float(emp_price)
item["subtotal"] = float(subtotal)
total = float(sum(Decimal(str(i["subtotal"])) for i in filtered_items).quantize(TWO_PLACES, rounding=ROUND_HALF_UP))
total = float(
sum(Decimal(str(i["subtotal"])) for i in filtered_items).quantize(
TWO_PLACES, rounding=ROUND_HALF_UP
)
)
slug = email.lower()
@ -244,14 +254,17 @@ def handle_submit(event):
_lambda.invoke(
FunctionName=os.environ["SLACK_NOTIFIER_ARN"],
InvocationType="Event",
Payload=json.dumps({
"event": "order_confirmed",
"employee_name": name,
"employee_email": email,
"items": filtered_items,
"total": order_data["total"],
"week": week,
}, default=float),
Payload=json.dumps(
{
"event": "order_confirmed",
"employee_name": name,
"employee_email": email,
"items": filtered_items,
"total": order_data["total"],
"week": week,
},
default=float,
),
)
except Exception as exc:
logger.error("Slack notifier invocation failed (order already saved): %s", exc)

View file

@ -161,7 +161,10 @@ def main():
print(f"{meal['meal']:<45} {meal['quantity']:>4} ${meal['line_total']:>7.2f}")
print("-" * 60)
print(f"{'TOTAL':<45} {summary['total_meals']:>4} ${summary['grand_total']:>7.2f}")
if summary.get("employee_total") is not None and summary["employee_total"] != summary["grand_total"]:
if (
summary.get("employee_total") is not None
and summary["employee_total"] != summary["grand_total"]
):
print(f"{'PAYROLL DEDUCTIONS':<45} ${summary['employee_total']:>7.2f}")
subsidy = round(summary["grand_total"] - summary["employee_total"], 2)
print(f"{'COMPANY SUBSIDY':<45} ${subsidy:>7.2f}")

View file

@ -49,7 +49,9 @@ def generate_form(
week = datetime.now().strftime("%Y-W%U")
scraped_at = menu.get("scraped_at", "unknown")
submit_url = f"{api_url}/api/submit-order" if api_url else "/api/submit-order"
status_url = f"{api_url}/api/form-status/{week}" if api_url else f"/api/form-status/{week}"
status_url = (
f"{api_url}/api/form-status/{week}" if api_url else f"/api/form-status/{week}"
)
roster_url = f"{api_url}/api/roster" if api_url else "/api/roster"
api_key_json = json.dumps(api_key).replace("</", "<\\/")
has_discount = bulk_discount > 0 or company_subsidy > 0
@ -299,7 +301,11 @@ body {{ padding-bottom: 80px; }}
.countdown {{ font-size: 2rem; font-weight: 700; color: #1a1a2e; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }}
.countdown-label {{ font-size: 0.75rem; color: #9ca3af; margin-top: 4px; }}
</style>
{'<script src="https://accounts.google.com/gsi/client" async defer></script>' if use_google_auth else ''}
{
'<script src="https://accounts.google.com/gsi/client" async defer></script>'
if use_google_auth
else ""
}
</head>
<body>
<div class="closed-overlay" id="closed-overlay">
@ -311,8 +317,12 @@ body {{ padding-bottom: 80px; }}
<div class="countdown-label">until orders open</div>
</div>
</div>
{'<div class="auth-overlay" id="auth-overlay"><div class="auth-card"><div class="logo">&#127869;</div><h1>Sea Haven Meal Order</h1><p>Sign in with your company Google account to place your order.</p><div id="g-signin-btn" style="display:flex;justify-content:center;"></div></div></div>' if use_google_auth else ''}
<div class="container" id="app" {'style="display:none;"' if use_google_auth else ''}>
{
'<div class="auth-overlay" id="auth-overlay"><div class="auth-card"><div class="logo">&#127869;</div><h1>Sea Haven Meal Order</h1><p>Sign in with your company Google account to place your order.</p><div id="g-signin-btn" style="display:flex;justify-content:center;"></div></div></div>'
if use_google_auth
else ""
}
<div class="container" id="app" {'style="display:none;"' if use_google_auth else ""}>
<header>
<h1>Sea Haven Meal Order</h1>
<p>Week of {week} &middot; Menu scraped {scraped_at[:10]}</p>
@ -320,7 +330,15 @@ body {{ padding-bottom: 80px; }}
<div class="deadline">Order deadline: {deadline}</div>
<div class="duplicate-warning" id="duplicate-warning" style="display:none;">You've already submitted an order this week. Submitting again will replace your previous order.</div>
{'<div class="discount-banner">Prices reflect employee cost after ' + (f"{bulk_discount:g}% bulk discount" if bulk_discount > 0 else "") + (" + " if bulk_discount > 0 and company_subsidy > 0 else "") + (f"{company_subsidy:g}% company subsidy" if company_subsidy > 0 else "") + "</div>" if has_discount else ""}
{
'<div class="discount-banner">Prices reflect employee cost after '
+ (f"{bulk_discount:g}% bulk discount" if bulk_discount > 0 else "")
+ (" + " if bulk_discount > 0 and company_subsidy > 0 else "")
+ (f"{company_subsidy:g}% company subsidy" if company_subsidy > 0 else "")
+ "</div>"
if has_discount
else ""
}
{auth_section_html}
@ -330,7 +348,9 @@ body {{ padding-bottom: 80px; }}
<div id="meals-list"></div>
<div class="sticky-footer" {'style="display:none;" id="sticky-footer"' if use_google_auth else ''}>
<div class="sticky-footer" {
'style="display:none;" id="sticky-footer"' if use_google_auth else ""
}>
<div class="inner">
<div>
<span class="total" id="total-display">$0.00</span>
@ -359,7 +379,13 @@ const BULK_DISCOUNT = {bulk_discount};
const COMPANY_SUBSIDY = {company_subsidy};
const quantities = {{}};
let formClosed = false;
{'const GOOGLE_CLIENT_ID = ' + google_client_id_json + ';\nlet googleCredential = null;\nlet googleUser = null;' if use_google_auth else ''}
{
"const GOOGLE_CLIENT_ID = "
+ google_client_id_json
+ ";\nlet googleCredential = null;\nlet googleUser = null;"
if use_google_auth
else ""
}
function escapeHtml(str) {{
if (!str) return '';
@ -388,7 +414,11 @@ function checkDuplicateOrder() {{
function init() {{
checkFormStatus();
{'waitForGoogleAuth();' if use_google_auth else 'loadRoster(); checkDuplicateOrder();'}
{
"waitForGoogleAuth();"
if use_google_auth
else "loadRoster(); checkDuplicateOrder();"
}
// Build filter buttons
const tags = new Set();
MEALS.forEach(m => (m.dietary_tags || []).forEach(t => tags.add(t)));
@ -474,12 +504,17 @@ function updateTotal() {{
}});
document.getElementById('total-display').textContent = `$${{total.toFixed(2)}}`;
document.getElementById('count-display').textContent = `${{count}} meal${{count !== 1 ? 's' : ''}}`;
document.getElementById('submit-btn').disabled = count === 0{' || !googleCredential' if use_google_auth else ''};
document.getElementById('submit-btn').disabled = count === 0{
" || !googleCredential" if use_google_auth else ""
};
}}
{submit_order_js}
{"" if use_google_auth else """async function loadRoster() {{
{
""
if use_google_auth
else '''async function loadRoster() {{
try {{
const res = await fetch(ROSTER_URL);
const data = await res.json();
@ -508,7 +543,8 @@ function updateTotal() {{
}});
}}
}}
"""}
'''
}
async function checkFormStatus() {{
if (!STATUS_URL) return;
try {{
@ -616,6 +652,7 @@ def main():
if not google_client_id:
try:
import boto3
ssm = boto3.client("ssm")
resp = ssm.get_parameter(Name="/meal-order-manager/google-client-id")
google_client_id = resp["Parameter"]["Value"]

View file

@ -5,7 +5,5 @@ import sys
# Add the shared layer source directory so `from shared.db import ...` works
# without requiring a real Lambda layer or .aws-sam build.
_shared_layer_dir = os.path.join(
os.path.dirname(__file__), os.pardir, "src", "shared"
)
_shared_layer_dir = os.path.join(os.path.dirname(__file__), os.pardir, "src", "shared")
sys.path.insert(0, os.path.abspath(_shared_layer_dir))

View file

@ -14,11 +14,16 @@ import pytest
# Helpers — reusable order builders
# ---------------------------------------------------------------------------
def _make_order(name: str, email: str, items: list[dict], total: float | None = None) -> dict:
def _make_order(
name: str, email: str, items: list[dict], total: float | None = None
) -> dict:
"""Build a minimal order dict matching DynamoDB shape."""
if total is None:
total = sum(
float(i.get("subtotal", float(i.get("price", 0)) * int(i.get("quantity", 0))))
float(
i.get("subtotal", float(i.get("price", 0)) * int(i.get("quantity", 0)))
)
for i in items
)
return {
@ -50,11 +55,15 @@ def _make_item(
# don't try to hit real AWS.
# ---------------------------------------------------------------------------
@pytest.fixture(autouse=True)
def _patch_env(monkeypatch):
monkeypatch.setenv("TABLE_NAME", "test-table")
monkeypatch.setenv("REPORTS_BUCKET", "test-bucket")
monkeypatch.setenv("SLACK_NOTIFIER_ARN", "arn:aws:lambda:us-east-1:123456789012:function:test-notifier")
monkeypatch.setenv(
"SLACK_NOTIFIER_ARN",
"arn:aws:lambda:us-east-1:123456789012:function:test-notifier",
)
@pytest.fixture()
@ -63,10 +72,14 @@ def handler_module():
with patch("boto3.client") as mock_client, patch("boto3.resource"):
mock_s3 = MagicMock()
mock_lambda = MagicMock()
mock_client.side_effect = lambda svc, **kw: {"s3": mock_s3, "lambda": mock_lambda}[svc]
mock_client.side_effect = lambda svc, **kw: {
"s3": mock_s3,
"lambda": mock_lambda,
}[svc]
import importlib
import functions.aggregate_orders.handler as mod
importlib.reload(mod)
# Inject mocked clients so tests can assert on them
@ -85,10 +98,16 @@ class TestBuildSummarySingleOrder:
def test_build_summary_single_order(self, handler_module):
orders = [
_make_order("Alice Smith", "alice@example.com", [
_make_item("Chicken Parm", quantity=1, price=12.00, bulk_price=10.00),
_make_item("Caesar Salad", quantity=1, price=8.00, bulk_price=6.50),
]),
_make_order(
"Alice Smith",
"alice@example.com",
[
_make_item(
"Chicken Parm", quantity=1, price=12.00, bulk_price=10.00
),
_make_item("Caesar Salad", quantity=1, price=8.00, bulk_price=6.50),
],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
@ -103,12 +122,20 @@ class TestBuildSummarySingleOrder:
chicken = meals_by_name["Chicken Parm"]
assert chicken["quantity"] == 1
assert chicken["unit_price"] == 10.00, "unit_price should use bulk_price"
assert chicken["employee_unit_price"] == 12.00, "employee_unit_price should use price"
assert chicken["employee_unit_price"] == 12.00, (
"employee_unit_price should use price"
)
assert chicken["line_total"] == 10.00, "line_total = bulk_price * qty"
assert chicken["employee_line_total"] == 12.00, "employee_line_total = price * qty"
assert chicken["employee_line_total"] == 12.00, (
"employee_line_total = price * qty"
)
assert summary["grand_total"] == 16.50, "grand_total should sum bulk line totals"
assert summary["employee_total"] == 20.00, "employee_total should sum employee line totals"
assert summary["grand_total"] == 16.50, (
"grand_total should sum bulk line totals"
)
assert summary["employee_total"] == 20.00, (
"employee_total should sum employee line totals"
)
class TestBuildSummaryMultipleOrdersSameMeal:
@ -116,13 +143,27 @@ class TestBuildSummaryMultipleOrdersSameMeal:
def test_build_summary_multiple_orders_same_meal(self, handler_module):
orders = [
_make_order("Alice", "a@x.com", [_make_item("Burger", quantity=1, price=10.00, bulk_price=8.00)]),
_make_order("Bob", "b@x.com", [_make_item("Burger", quantity=2, price=10.00, bulk_price=8.00)]),
_make_order("Carol", "c@x.com", [_make_item("Burger", quantity=1, price=10.00, bulk_price=8.00)]),
_make_order(
"Alice",
"a@x.com",
[_make_item("Burger", quantity=1, price=10.00, bulk_price=8.00)],
),
_make_order(
"Bob",
"b@x.com",
[_make_item("Burger", quantity=2, price=10.00, bulk_price=8.00)],
),
_make_order(
"Carol",
"c@x.com",
[_make_item("Burger", quantity=1, price=10.00, bulk_price=8.00)],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
assert len(summary["meals"]) == 1, "All three ordered the same meal — should aggregate to 1 entry"
assert len(summary["meals"]) == 1, (
"All three ordered the same meal — should aggregate to 1 entry"
)
burger = summary["meals"][0]
assert burger["quantity"] == 4, "Total quantity should be 1 + 2 + 1 = 4"
assert burger["line_total"] == 32.00, "line_total = 8.00 * 4"
@ -134,11 +175,15 @@ class TestBuildSummarySortedAlphabetically:
def test_build_summary_sorted_alphabetically(self, handler_module):
orders = [
_make_order("Alice", "a@x.com", [
_make_item("Ziti", quantity=1, price=10.00),
_make_item("Apple Pie", quantity=1, price=5.00),
_make_item("Meatloaf", quantity=1, price=12.00),
]),
_make_order(
"Alice",
"a@x.com",
[
_make_item("Ziti", quantity=1, price=10.00),
_make_item("Apple Pie", quantity=1, price=5.00),
_make_item("Meatloaf", quantity=1, price=12.00),
],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
@ -153,19 +198,31 @@ class TestBuildSummaryGrandTotalVsEmployeeTotal:
def test_build_summary_grand_total_vs_employee_total(self, handler_module):
orders = [
_make_order("Alice", "a@x.com", [
_make_item("Steak", quantity=2, price=15.00, bulk_price=11.00),
]),
_make_order("Bob", "b@x.com", [
_make_item("Pasta", quantity=1, price=9.00, bulk_price=7.00),
]),
_make_order(
"Alice",
"a@x.com",
[
_make_item("Steak", quantity=2, price=15.00, bulk_price=11.00),
],
),
_make_order(
"Bob",
"b@x.com",
[
_make_item("Pasta", quantity=1, price=9.00, bulk_price=7.00),
],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
# Steak: bulk 11*2=22, employee 15*2=30
# Pasta: bulk 7*1=7, employee 9*1=9
assert summary["grand_total"] == 29.00, "grand_total should use bulk_price (22 + 7)"
assert summary["employee_total"] == 39.00, "employee_total should use employee_price (30 + 9)"
assert summary["grand_total"] == 29.00, (
"grand_total should use bulk_price (22 + 7)"
)
assert summary["employee_total"] == 39.00, (
"employee_total should use employee_price (30 + 9)"
)
assert summary["grand_total"] != summary["employee_total"], (
"grand_total and employee_total must differ when bulk != employee price"
)
@ -177,9 +234,13 @@ class TestBuildSummaryRounding:
def test_build_summary_rounding(self, handler_module):
# Use prices that produce repeating decimals when multiplied
orders = [
_make_order("Alice", "a@x.com", [
_make_item("Soup", quantity=3, price=3.33, bulk_price=2.77),
]),
_make_order(
"Alice",
"a@x.com",
[
_make_item("Soup", quantity=3, price=3.33, bulk_price=2.77),
],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
@ -187,7 +248,9 @@ class TestBuildSummaryRounding:
# 2.77 * 3 = 8.31 (rounded)
assert soup["line_total"] == 8.31, "line_total should be rounded to 2 decimals"
# 3.33 * 3 = 9.99
assert soup["employee_line_total"] == 9.99, "employee_line_total should be rounded to 2 decimals"
assert soup["employee_line_total"] == 9.99, (
"employee_line_total should be rounded to 2 decimals"
)
assert summary["grand_total"] == 8.31
assert summary["employee_total"] == 9.99
@ -206,10 +269,14 @@ class TestBuildOrderSummaryCsv:
def test_build_order_summary_csv(self, handler_module):
orders = [
_make_order("Alice", "a@x.com", [
_make_item("Burger", quantity=2, price=10.00, bulk_price=8.00),
_make_item("Fries", quantity=1, price=5.00, bulk_price=4.00),
]),
_make_order(
"Alice",
"a@x.com",
[
_make_item("Burger", quantity=2, price=10.00, bulk_price=8.00),
_make_item("Fries", quantity=1, price=5.00, bulk_price=4.00),
],
),
]
summary = handler_module.build_summary(orders, "2026-W20")
csv_str = handler_module.build_order_summary_csv(summary)
@ -245,13 +312,23 @@ class TestBuildPayrollCsv:
def test_build_payroll_csv(self, handler_module):
orders = [
_make_order("Zara Adams", "zara@x.com", [
_make_item("Pasta", quantity=2, price=9.00, subtotal=18.00),
], total=18.00),
_make_order("Alice Brown", "alice@x.com", [
_make_item("Burger", quantity=1, price=10.00, subtotal=10.00),
_make_item("Fries", quantity=2, price=5.00, subtotal=10.00),
], total=20.00),
_make_order(
"Zara Adams",
"zara@x.com",
[
_make_item("Pasta", quantity=2, price=9.00, subtotal=18.00),
],
total=18.00,
),
_make_order(
"Alice Brown",
"alice@x.com",
[
_make_item("Burger", quantity=1, price=10.00, subtotal=10.00),
_make_item("Fries", quantity=2, price=5.00, subtotal=10.00),
],
total=20.00,
),
]
csv_str = handler_module.build_payroll_csv(orders)
@ -259,7 +336,12 @@ class TestBuildPayrollCsv:
rows = list(reader)
# Header
assert rows[0] == ["Employee Name", "Employee Email", "Items Ordered", "Total Deduction"]
assert rows[0] == [
"Employee Name",
"Employee Email",
"Items Ordered",
"Total Deduction",
]
# Sorted alphabetically by employee_name: Alice Brown before Zara Adams
assert rows[1][0] == "Alice Brown", "Employees should be sorted by name"
@ -280,9 +362,14 @@ class TestBuildPayrollCsvSubtotalFallback:
def test_build_payroll_csv_subtotal_fallback(self, handler_module):
orders = [
_make_order("Alice Brown", "alice@x.com", [
_make_item("Burger", quantity=3, price=10.00), # no subtotal key
], total=30.00),
_make_order(
"Alice Brown",
"alice@x.com",
[
_make_item("Burger", quantity=3, price=10.00), # no subtotal key
],
total=30.00,
),
]
csv_str = handler_module.build_payroll_csv(orders)
@ -313,7 +400,9 @@ class TestAggregateAlreadyAggregated:
result = handler_module.lambda_handler({}, None)
assert result["status"] == "already_aggregated", "Should return already_aggregated status"
assert result["status"] == "already_aggregated", (
"Should return already_aggregated status"
)
assert result["week"] == "2026-W20"
handler_module._s3.put_object.assert_not_called()
mock_orders.assert_not_called()
@ -333,7 +422,9 @@ class TestAggregateNoOrders:
result = handler_module.lambda_handler({}, None)
assert result["status"] == "no_orders", "Should return no_orders when order list is empty"
assert result["status"] == "no_orders", (
"Should return no_orders when order list is empty"
)
assert result["week"] == "2026-W20"
handler_module._s3.put_object.assert_not_called()
@ -350,12 +441,30 @@ class TestAggregateHappyPath:
):
mock_get_summary.return_value = None
mock_orders.return_value = [
_make_order("Alice", "alice@x.com", [
_make_item("Burger", quantity=1, price=10.00, bulk_price=8.00, subtotal=10.00),
], total=10.00),
_make_order("Bob", "bob@x.com", [
_make_item("Pasta", quantity=2, price=9.00, bulk_price=7.00, subtotal=18.00),
], total=18.00),
_make_order(
"Alice",
"alice@x.com",
[
_make_item(
"Burger",
quantity=1,
price=10.00,
bulk_price=8.00,
subtotal=10.00,
),
],
total=10.00,
),
_make_order(
"Bob",
"bob@x.com",
[
_make_item(
"Pasta", quantity=2, price=9.00, bulk_price=7.00, subtotal=18.00
),
],
total=18.00,
),
]
result = handler_module.lambda_handler({}, None)

View file

@ -29,6 +29,7 @@ ET = ZoneInfo("America/New_York")
# Helpers
# ────────────────────────────────────────────────────────────────────────────
def _make_datetime(year, month, day, hour, minute=0):
"""Return a timezone-aware datetime in America/New_York."""
return datetime(year, month, day, hour, minute, tzinfo=ET)
@ -52,8 +53,8 @@ def _wednesday_10am():
# Reminder Dedup Guard (Critical)
# ────────────────────────────────────────────────────────────────────────────
class TestReminderDedupGuard:
class TestReminderDedupGuard:
@patch("slack_notifier_handler.datetime")
def test_reminder_skipped_wrong_hour(self, mock_dt):
"""Invoked at 11am Thursday ET -> returns skipped."""
@ -106,8 +107,8 @@ class TestReminderDedupGuard:
# Reminder DMs (High)
# ────────────────────────────────────────────────────────────────────────────
class TestReminderDMs:
class TestReminderDMs:
@patch("slack_notifier_handler.send_dm")
@patch("slack_notifier_handler.get_orders")
@patch("slack_notifier_handler.get_roster")
@ -187,8 +188,8 @@ class TestReminderDMs:
# Order Confirmed (High)
# ────────────────────────────────────────────────────────────────────────────
class TestOrderConfirmed:
class TestOrderConfirmed:
@patch("slack_notifier_handler.send_dm")
@patch("slack_notifier_handler.get_roster")
@patch("slack_notifier_handler.current_week", return_value="2026-W19")
@ -217,12 +218,8 @@ class TestOrderConfirmed:
blocks = call_kwargs.kwargs.get("blocks") or call_kwargs[1].get("blocks")
body_text = blocks[1]["text"]["text"]
assert "your cost: $17.00" in body_text, (
"Should show Grilled Chicken x2 cost"
)
assert "your cost: $6.00" in body_text, (
"Should show Caesar Salad x1 cost"
)
assert "your cost: $17.00" in body_text, "Should show Grilled Chicken x2 cost"
assert "your cost: $6.00" in body_text, "Should show Caesar Salad x1 cost"
assert "$23.00" in body_text, "Should show payroll deduction total"
assert "payroll deduction" in body_text.lower()
@ -267,23 +264,25 @@ class TestOrderConfirmed:
assert result["status"] == "confirmed", "Should not crash on empty name"
blocks = mock_dm.call_args.kwargs.get("blocks") or mock_dm.call_args[1].get("blocks")
blocks = mock_dm.call_args.kwargs.get("blocks") or mock_dm.call_args[1].get(
"blocks"
)
body_text = blocks[1]["text"]["text"]
assert "Hey there!" in body_text, "Should greet with 'Hey there!' when name is empty"
assert "Hey there!" in body_text, (
"Should greet with 'Hey there!' when name is empty"
)
# ────────────────────────────────────────────────────────────────────────────
# Orders Aggregated (High)
# ────────────────────────────────────────────────────────────────────────────
class TestOrdersAggregated:
class TestOrdersAggregated:
@patch("slack_notifier_handler.post_channel_message")
@patch("slack_notifier_handler.get_summary")
@patch("slack_notifier_handler.current_week", return_value="2026-W19")
def test_orders_aggregated_with_subsidy(
self, mock_week, mock_summary, mock_post
):
def test_orders_aggregated_with_subsidy(self, mock_week, mock_summary, mock_post):
"""employee_total < grand_total -> message includes company subsidy line."""
mock_summary.return_value = {
"total_employees": 5,
@ -339,9 +338,7 @@ class TestOrdersAggregated:
@patch("slack_notifier_handler.post_channel_message")
@patch("slack_notifier_handler.get_summary")
@patch("slack_notifier_handler.current_week", return_value="2026-W19")
def test_orders_aggregated_no_summary(
self, mock_week, mock_summary, mock_post
):
def test_orders_aggregated_no_summary(self, mock_week, mock_summary, mock_post):
"""No summary in DB -> returns {'status': 'no_summary'}."""
mock_summary.return_value = None
@ -355,8 +352,8 @@ class TestOrdersAggregated:
# Escaping (Low)
# ────────────────────────────────────────────────────────────────────────────
class TestEscaping:
class TestEscaping:
def test_escape_mrkdwn(self):
"""'A & B <C>' -> 'A &amp; B &lt;C&gt;'."""
assert handler._escape_mrkdwn("A & B <C>") == "A &amp; B &lt;C&gt;"
@ -366,8 +363,8 @@ class TestEscaping:
# Routing
# ────────────────────────────────────────────────────────────────────────────
class TestRouting:
class TestRouting:
def test_unknown_event_type(self):
"""Unknown event type returns error message."""
result = handler.lambda_handler({"event": "bogus_event"}, None)

View file

@ -19,13 +19,20 @@ import pytest
# ---------------------------------------------------------------------------
os.environ.setdefault("TABLE_NAME", "test-orders-table")
os.environ.setdefault("FORM_API_KEY_SECRET", "test/form-api-key")
os.environ.setdefault("SLACK_NOTIFIER_ARN", "arn:aws:lambda:us-east-1:000000000000:function:test-slack-notifier")
os.environ.setdefault(
"SLACK_NOTIFIER_ARN",
"arn:aws:lambda:us-east-1:000000000000:function:test-slack-notifier",
)
os.environ.setdefault("GOOGLE_CLIENT_ID_PARAM", "")
import importlib.util
_handler_path = os.path.join(os.path.dirname(__file__), os.pardir, "functions", "submit_order", "handler.py")
_spec = importlib.util.spec_from_file_location("submit_order_handler", os.path.abspath(_handler_path))
_handler_path = os.path.join(
os.path.dirname(__file__), os.pardir, "functions", "submit_order", "handler.py"
)
_spec = importlib.util.spec_from_file_location(
"submit_order_handler", os.path.abspath(_handler_path)
)
submit_order_handler = importlib.util.module_from_spec(_spec)
sys.modules["submit_order_handler"] = submit_order_handler
_spec.loader.exec_module(submit_order_handler)
@ -62,8 +69,13 @@ def _make_event(
return event
def _submit_event(items, api_key=TEST_API_KEY, employee_name="Test User",
employee_email="test.user@seahavenind.com", extra_body=None):
def _submit_event(
items,
api_key=TEST_API_KEY,
employee_name="Test User",
employee_email="test.user@seahavenind.com",
extra_body=None,
):
"""Shortcut for a typical POST /submit event with items."""
body = {
"employee_name": employee_name,
@ -89,11 +101,13 @@ def _make_items(retail_prices_and_qtys):
"""Build item list from [(retail_price, quantity), ...]."""
items = []
for i, (price, qty) in enumerate(retail_prices_and_qtys):
items.append({
"name": f"Meal {i + 1}",
"retail_price": price,
"quantity": qty,
})
items.append(
{
"name": f"Meal {i + 1}",
"retail_price": price,
"quantity": qty,
}
)
return items
@ -123,6 +137,7 @@ def _reset_handler_caches():
def _reset_shared_caches():
"""Reset shared.secrets cache before each test."""
from shared import secrets
secrets._cache = {}
yield
@ -131,11 +146,15 @@ def _reset_shared_caches():
# PRICING PIPELINE (Critical)
# ===========================================================================
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 10, "company_subsidy_percent": 50})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 10, "company_subsidy_percent": 50},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_discount_two_step_rounding(
@ -174,7 +193,10 @@ def test_discount_two_step_rounding(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 50, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 50, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_discount_rounding_half_up_boundary(
@ -208,7 +230,10 @@ def test_discount_rounding_half_up_boundary(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": -5, "company_subsidy_percent": 150})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": -5, "company_subsidy_percent": 150},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_discount_clamping(
@ -242,7 +267,10 @@ def test_discount_clamping(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_discount_both_zero(
@ -261,7 +289,9 @@ def test_discount_both_zero(
saved_order = mock_put.call_args[0][2]
item = saved_order["items"][0]
assert item["retail_price"] == 15.99, f"retail_price mismatch: {item['retail_price']}"
assert item["retail_price"] == 15.99, (
f"retail_price mismatch: {item['retail_price']}"
)
assert item["bulk_price"] == 15.99, (
f"bulk_price should equal retail when bulk discount is 0%, got {item['bulk_price']}"
)
@ -277,7 +307,10 @@ def test_discount_both_zero(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 10, "company_subsidy_percent": 25})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 10, "company_subsidy_percent": 25},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_total_summation_multiple_items(
@ -301,10 +334,14 @@ def test_total_summation_multiple_items(
saved_order = mock_put.call_args[0][2]
item_a = saved_order["items"][0]
assert item_a["subtotal"] == 13.50, f"Item A subtotal expected 13.50, got {item_a['subtotal']}"
assert item_a["subtotal"] == 13.50, (
f"Item A subtotal expected 13.50, got {item_a['subtotal']}"
)
item_b = saved_order["items"][1]
assert item_b["subtotal"] == 4.95, f"Item B subtotal expected 4.95, got {item_b['subtotal']}"
assert item_b["subtotal"] == 4.95, (
f"Item B subtotal expected 4.95, got {item_b['subtotal']}"
)
assert saved_order["total"] == 18.45, (
f"total should be sum of rounded subtotals (13.50 + 4.95 = 18.45), got {saved_order['total']}"
@ -315,13 +352,19 @@ def test_total_summation_multiple_items(
# AUTHENTICATION (Critical + High)
# ===========================================================================
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
def test_google_auth_required_when_configured(
mock_gcid, mock_secret, mock_settings, mock_week, mock_status, mock_put, mock_lam
):
@ -345,9 +388,14 @@ def test_google_auth_required_when_configured(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
def test_google_auth_bypass_prevention(
mock_gcid, mock_secret, mock_settings, mock_week, mock_status, mock_put, mock_lam
):
@ -361,8 +409,10 @@ def test_google_auth_bypass_prevention(
# No google_id_token — trying to bypass with manual name/email
}
event = _make_event(
method="POST", path="/submit",
body=body, headers={"x-api-key": TEST_API_KEY},
method="POST",
path="/submit",
body=body,
headers={"x-api-key": TEST_API_KEY},
)
result = lambda_handler(event, None)
status, body_resp = _parse_response(result)
@ -375,25 +425,38 @@ def test_google_auth_bypass_prevention(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
@patch("submit_order_handler.urllib.request.urlopen")
def test_google_token_audience_mismatch(
mock_urlopen, mock_gcid, mock_secret, mock_settings,
mock_week, mock_status, mock_put, mock_lam
mock_urlopen,
mock_gcid,
mock_secret,
mock_settings,
mock_week,
mock_status,
mock_put,
mock_lam,
):
"""Token with wrong audience -> 403."""
from submit_order_handler import lambda_handler
# Simulate Google returning token info with wrong audience
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({
"aud": "wrong-client-id.apps.googleusercontent.com",
"hd": "seahavenind.com",
"name": "Test User",
"email": "test@seahavenind.com",
}).encode()
mock_resp.read.return_value = json.dumps(
{
"aud": "wrong-client-id.apps.googleusercontent.com",
"hd": "seahavenind.com",
"name": "Test User",
"email": "test@seahavenind.com",
}
).encode()
mock_resp.__enter__ = MagicMock(return_value=mock_resp)
mock_resp.__exit__ = MagicMock(return_value=False)
mock_urlopen.return_value = mock_resp
@ -411,24 +474,37 @@ def test_google_token_audience_mismatch(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
@patch("submit_order_handler.urllib.request.urlopen")
def test_google_token_domain_mismatch(
mock_urlopen, mock_gcid, mock_secret, mock_settings,
mock_week, mock_status, mock_put, mock_lam
mock_urlopen,
mock_gcid,
mock_secret,
mock_settings,
mock_week,
mock_status,
mock_put,
mock_lam,
):
"""Token with wrong hosted domain -> 403."""
from submit_order_handler import lambda_handler
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({
"aud": VALID_GOOGLE_CLIENT_ID,
"hd": "evil-corp.com",
"name": "Evil User",
"email": "evil@evil-corp.com",
}).encode()
mock_resp.read.return_value = json.dumps(
{
"aud": VALID_GOOGLE_CLIENT_ID,
"hd": "evil-corp.com",
"name": "Evil User",
"email": "evil@evil-corp.com",
}
).encode()
mock_resp.__enter__ = MagicMock(return_value=mock_resp)
mock_resp.__exit__ = MagicMock(return_value=False)
mock_urlopen.return_value = mock_resp
@ -446,13 +522,27 @@ def test_google_token_domain_mismatch(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch("submit_order_handler.urllib.request.urlopen", side_effect=urllib.error.URLError("Connection refused"))
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
@patch(
"submit_order_handler.urllib.request.urlopen",
side_effect=urllib.error.URLError("Connection refused"),
)
def test_google_token_service_unavailable(
mock_urlopen, mock_gcid, mock_secret, mock_settings,
mock_week, mock_status, mock_put, mock_lam
mock_urlopen,
mock_gcid,
mock_secret,
mock_settings,
mock_week,
mock_status,
mock_put,
mock_lam,
):
"""URLError from Google tokeninfo -> 503."""
from submit_order_handler import lambda_handler
@ -473,24 +563,37 @@ def test_google_token_service_unavailable(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID)
@patch(
"submit_order_handler._get_google_client_id", return_value=VALID_GOOGLE_CLIENT_ID
)
@patch("submit_order_handler.urllib.request.urlopen")
def test_google_token_valid_success(
mock_urlopen, mock_gcid, mock_secret, mock_settings,
mock_week, mock_status, mock_put, mock_lam
mock_urlopen,
mock_gcid,
mock_secret,
mock_settings,
mock_week,
mock_status,
mock_put,
mock_lam,
):
"""Valid Google token -> order saved with token's name/email."""
from submit_order_handler import lambda_handler
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({
"aud": VALID_GOOGLE_CLIENT_ID,
"hd": "seahavenind.com",
"name": "Adam Moussa",
"email": "adam.moussa@seahavenind.com",
}).encode()
mock_resp.read.return_value = json.dumps(
{
"aud": VALID_GOOGLE_CLIENT_ID,
"hd": "seahavenind.com",
"name": "Adam Moussa",
"email": "adam.moussa@seahavenind.com",
}
).encode()
mock_resp.__enter__ = MagicMock(return_value=mock_resp)
mock_resp.__exit__ = MagicMock(return_value=False)
mock_urlopen.return_value = mock_resp
@ -521,7 +624,10 @@ def test_google_token_valid_success(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_manual_fallback_when_google_not_configured(
@ -531,7 +637,9 @@ def test_manual_fallback_when_google_not_configured(
from submit_order_handler import lambda_handler
items = _make_items([(12.00, 1)])
event = _submit_event(items, employee_name="Manual User", employee_email="manual@seahavenind.com")
event = _submit_event(
items, employee_name="Manual User", employee_email="manual@seahavenind.com"
)
result = lambda_handler(event, None)
status, body = _parse_response(result)
@ -550,7 +658,10 @@ def test_manual_fallback_when_google_not_configured(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_invalid_api_key(
@ -575,11 +686,15 @@ def test_submit_invalid_api_key(
# SLUG GENERATION (Critical)
# ===========================================================================
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_slug_from_email(
@ -589,7 +704,9 @@ def test_slug_from_email(
from submit_order_handler import lambda_handler
items = _make_items([(10.00, 1)])
event = _submit_event(items, employee_name="Adam Moussa", employee_email="Adam.Moussa@seahavenind.com")
event = _submit_event(
items, employee_name="Adam Moussa", employee_email="Adam.Moussa@seahavenind.com"
)
result = lambda_handler(event, None)
status, _ = _parse_response(result)
@ -597,14 +714,19 @@ def test_slug_from_email(
# put_order is called with (week, slug, order_data)
slug = mock_put.call_args[0][1]
assert slug == "adam.moussa@seahavenind.com", f"Slug should be 'adam.moussa@seahavenind.com', got '{slug}'"
assert slug == "adam.moussa@seahavenind.com", (
f"Slug should be 'adam.moussa@seahavenind.com', got '{slug}'"
)
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_slug_edge_cases(
@ -615,7 +737,11 @@ def test_slug_edge_cases(
# Test 1: full email preserved
items = _make_items([(10.00, 1)])
event = _submit_event(items, employee_name="First Middle Last", employee_email="First.Middle.Last@x.com")
event = _submit_event(
items,
employee_name="First Middle Last",
employee_email="First.Middle.Last@x.com",
)
lambda_handler(event, None)
slug_1 = mock_put.call_args[0][1]
assert slug_1 == "first.middle.last@x.com", (
@ -624,7 +750,9 @@ def test_slug_edge_cases(
# Test 2: different domains produce different slugs (no collision)
mock_put.reset_mock()
event = _submit_event(items, employee_name="Bob Smith", employee_email="Bob@other.com")
event = _submit_event(
items, employee_name="Bob Smith", employee_email="Bob@other.com"
)
lambda_handler(event, None)
slug_2 = mock_put.call_args[0][1]
assert slug_2 == "bob@other.com", (
@ -637,13 +765,16 @@ def test_slug_edge_cases(
# FORM STATUS (Critical + High)
# ===========================================================================
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
def test_form_status_open(mock_week, mock_status):
"""Status 'open' — response has week and status, no reopen_at."""
from submit_order_handler import lambda_handler
event = _make_event(method="GET", path="/form-status/2026-W20", path_parameters={"week": "2026-W20"})
event = _make_event(
method="GET", path="/form-status/2026-W20", path_parameters={"week": "2026-W20"}
)
result = lambda_handler(event, None)
status, body = _parse_response(result)
@ -665,13 +796,19 @@ def test_form_status_closed_reopen_at(mock_week, mock_status):
mock_dt.now.return_value = thursday
mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
event = _make_event(method="GET", path="/form-status/2026-W20", path_parameters={"week": "2026-W20"})
event = _make_event(
method="GET",
path="/form-status/2026-W20",
path_parameters={"week": "2026-W20"},
)
result = lambda_handler(event, None)
status, body = _parse_response(result)
assert status == 200, f"Expected 200, got {status}: {body}"
assert body["status"] == "closed", f"Expected status='closed', got '{body['status']}'"
assert body["status"] == "closed", (
f"Expected status='closed', got '{body['status']}'"
)
assert "reopen_at" in body, "reopen_at should be present when form is closed"
# Next Monday from Thursday 2026-05-14 is Monday 2026-05-18
@ -694,7 +831,11 @@ def test_form_status_monday_before_8am(mock_week, mock_status):
mock_dt.now.return_value = monday_early
mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
event = _make_event(method="GET", path="/form-status/2026-W20", path_parameters={"week": "2026-W20"})
event = _make_event(
method="GET",
path="/form-status/2026-W20",
path_parameters={"week": "2026-W20"},
)
result = lambda_handler(event, None)
status, body = _parse_response(result)
@ -715,11 +856,15 @@ def test_form_status_monday_before_8am(mock_week, mock_status):
# VALIDATION (High)
# ===========================================================================
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_missing_name(
@ -729,7 +874,9 @@ def test_submit_missing_name(
from submit_order_handler import lambda_handler
items = _make_items([(10.00, 1)])
event = _submit_event(items, employee_name="", employee_email="test@seahavenind.com")
event = _submit_event(
items, employee_name="", employee_email="test@seahavenind.com"
)
result = lambda_handler(event, None)
status, body = _parse_response(result)
@ -744,7 +891,10 @@ def test_submit_missing_name(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_missing_email(
@ -769,7 +919,10 @@ def test_submit_missing_email(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_zero_quantity_only(
@ -794,7 +947,10 @@ def test_submit_zero_quantity_only(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="closed")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_form_closed(
@ -819,7 +975,10 @@ def test_submit_form_closed(
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="not_found")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_submit_no_menu(
@ -844,11 +1003,15 @@ def test_submit_no_menu(
# RELIABILITY (High)
# ===========================================================================
@patch("submit_order_handler._lambda")
@patch("submit_order_handler.put_order")
@patch("submit_order_handler.get_form_status", return_value="open")
@patch("submit_order_handler.current_week", return_value="2026-W20")
@patch("submit_order_handler.get_settings", return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0})
@patch(
"submit_order_handler.get_settings",
return_value={"bulk_discount_percent": 0, "company_subsidy_percent": 0},
)
@patch("submit_order_handler.get_secret", return_value=TEST_API_KEY)
@patch("submit_order_handler._get_google_client_id", return_value="")
def test_slack_failure_does_not_fail_order(