test(l1): T9 — flywheel capture + engineer notification tests
Add test_resolve_ai_build_creates_outcome_validated_proposal and test_escalate_notifies_engineers to cover the already-committed Task 9 implementation (flywheel FlowProposal creation on resolve, notify() call on escalate). Adapts fixture pattern to test_db + _make_internal_ticket as required by the T9 spec. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1074,3 +1074,67 @@ async def test_escalate_without_walk_writes_audit_log(test_db: AsyncSession):
|
||||
row = result.scalar_one()
|
||||
assert row.account_id == account.id
|
||||
assert row.details["escalation_reason_category"] == "no_kb_content"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# T9: flywheel capture on resolve + engineer notification on escalate
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_ai_build_creates_outcome_validated_proposal(test_db: AsyncSession, monkeypatch):
|
||||
"""resolve(helpful=True) on an ai_build session creates a FlowProposal with
|
||||
source='ai_realtime_l1' and validated_by_outcome=True."""
|
||||
from app.services import l1_session_service as svc
|
||||
|
||||
account = await _make_account(test_db)
|
||||
l1_user = await _make_user(test_db, account_id=account.id)
|
||||
ticket = await _make_internal_ticket(test_db, account_id=account.id, user_id=l1_user.id)
|
||||
|
||||
s = await svc.start_ai_build_session(
|
||||
test_db, account_id=account.id, user=l1_user,
|
||||
ticket_id=str(ticket.id), ticket_kind="internal")
|
||||
|
||||
# Simulate a walked path
|
||||
s.walked_path = [
|
||||
{"node_type": "question", "id": "n1", "text": "On?", "answer": "no"},
|
||||
{"node_type": "resolved", "id": "n2", "text": "Fixed."},
|
||||
]
|
||||
await test_db.flush()
|
||||
|
||||
await svc.resolve(test_db, session_id=s.id, helpful=True, resolution_notes="ok")
|
||||
|
||||
props = (await test_db.execute(
|
||||
select(FlowProposal).where(FlowProposal.l1_session_id == s.id))).scalars().all()
|
||||
assert len(props) == 1
|
||||
assert props[0].source == "ai_realtime_l1"
|
||||
assert props[0].validated_by_outcome is True
|
||||
assert props[0].source_session_id is None
|
||||
assert props[0].proposed_flow_data["tree_structure"]["id"] == "n1"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_escalate_notifies_engineers(test_db: AsyncSession, monkeypatch):
|
||||
"""escalate() sends a notification to engineer-or-above users in the account."""
|
||||
from app.services import l1_session_service as svc
|
||||
|
||||
calls: dict = {}
|
||||
|
||||
async def fake_notify(event, account_id, payload, db, target_user_ids=None):
|
||||
calls["event"] = event
|
||||
calls["target_user_ids"] = target_user_ids
|
||||
|
||||
monkeypatch.setattr(svc, "notify", fake_notify)
|
||||
|
||||
account = await _make_account(test_db)
|
||||
l1_user = await _make_user(test_db, account_id=account.id)
|
||||
ticket = await _make_internal_ticket(test_db, account_id=account.id, user_id=l1_user.id)
|
||||
|
||||
s = await svc.start_ai_build_session(
|
||||
test_db, account_id=account.id, user=l1_user,
|
||||
ticket_id=str(ticket.id), ticket_kind="internal")
|
||||
|
||||
await svc.escalate(test_db, session_id=s.id, reason="stuck",
|
||||
reason_category="exhausted_safe_steps")
|
||||
|
||||
assert calls["event"] == "l1.session.escalated"
|
||||
assert calls["target_user_ids"] is not None # explicit engineer recipients
|
||||
|
||||
Reference in New Issue
Block a user