feat: orchestrator package + alerts.send_admin_alert

This commit is contained in:
2026-05-07 06:24:55 -07:00
parent f8ab448cbd
commit 0135b74c03
3 changed files with 191 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
"""Tests for the Phase 5 weekly orchestration."""
import pytest
from datetime import date, datetime, timezone
from uuid import uuid4
pytestmark = pytest.mark.requires_postgres
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
WEEK = date(2026, 5, 8) # a Friday
@pytest.fixture()
def family(db):
from app.models import FamilyProfile
fp = FamilyProfile(
id=uuid4(),
name="Test Family",
household_size=4,
adult_count=2,
child_count=2,
pending_approval_policy="approve",
)
db.add(fp)
db.flush()
return fp
@pytest.fixture()
def member(db, family):
from app.models import FamilyMember, FamilyMemberRole
m = FamilyMember(
id=uuid4(),
family_profile_id=family.id,
name="Alice",
email="alice@example.com",
role=FamilyMemberRole.ADULT,
)
db.add(m)
db.flush()
return m
@pytest.fixture()
def weekly_run(db, family):
from app.models import WeeklyRun
r = WeeklyRun(family_id=family.id, week_start_date=WEEK, status="pending")
db.add(r)
db.flush()
return r
@pytest.fixture()
def weekly_run_scraped(db, family):
from app.models import WeeklyRun
r = WeeklyRun(
family_id=family.id,
week_start_date=WEEK,
status="running",
scraped_at=datetime.now(timezone.utc),
)
db.add(r)
db.flush()
return r
@pytest.fixture()
def weekly_run_generated(db, family):
from app.models import WeeklyRun
r = WeeklyRun(
family_id=family.id,
week_start_date=WEEK,
status="running",
scraped_at=datetime.now(timezone.utc),
generated_at=datetime.now(timezone.utc),
)
db.add(r)
db.flush()
return r
@pytest.fixture()
def meal_plan(db, family):
from app.models import MealPlan, MealPlanStatus
mp = MealPlan(
id=uuid4(),
family_profile_id=family.id,
week_start_date=WEEK,
status=MealPlanStatus.PENDING_APPROVAL,
)
db.add(mp)
db.flush()
return mp
@pytest.fixture()
def pending_item(db, meal_plan):
from app.models import MealPlanItem, MealPlanItemStatus, MealType, Recipe
recipe = Recipe(
id=uuid4(),
name="Test Pasta",
servings=4,
ingredients=[{"name": "pasta", "qty": "400", "unit": "g"}],
instructions=["boil water", "cook pasta"],
)
db.add(recipe)
db.flush()
item = MealPlanItem(
id=uuid4(),
meal_plan_id=meal_plan.id,
recipe_id=recipe.id,
day_of_week=5,
meal_type=MealType.DINNER,
approval_status=MealPlanItemStatus.PENDING,
)
db.add(item)
db.flush()
return item
@pytest.fixture()
def weekly_run_emailed(db, family):
from app.models import WeeklyRun
r = WeeklyRun(
family_id=family.id,
week_start_date=WEEK,
status="running",
scraped_at=datetime.now(timezone.utc),
generated_at=datetime.now(timezone.utc),
emailed_at=datetime.now(timezone.utc),
)
db.add(r)
db.flush()
return r
# ---------------------------------------------------------------------------
# alerts.py tests
# ---------------------------------------------------------------------------
def test_send_admin_alert_noop_when_email_unset(monkeypatch):
monkeypatch.setattr(
"app.services.orchestrator.alerts.settings",
type("S", (), {"ADMIN_EMAIL": ""})(),
)
from app.services.orchestrator.alerts import send_admin_alert
send_admin_alert("test subject", "test body") # must not raise
def test_send_admin_alert_calls_backend(monkeypatch):
sent = []
class FakeBackend:
def send(self, **kwargs):
sent.append(kwargs)
monkeypatch.setattr(
"app.services.orchestrator.alerts.settings",
type("S", (), {"ADMIN_EMAIL": "admin@example.com"})(),
)
monkeypatch.setattr(
"app.services.orchestrator.alerts.get_email_backend",
lambda: FakeBackend(),
)
from app.services.orchestrator.alerts import send_admin_alert
send_admin_alert("boom", "details here")
assert len(sent) == 1
assert "[MealPlanner ALERT] boom" in sent[0]["subject"]
assert sent[0]["to"] == "admin@example.com"