Files
resolutionflow/backend/tests/test_sales_leads.py
Michael Chihlas f1be3abcc5
Some checks failed
CI / e2e (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / backend (push) Has been cancelled
Mirror to GitHub / mirror (push) Has been cancelled
feat: self-serve signup Phase 2 (frontend cutover) (#162)
Co-authored-by: Michael Chihlas <michael@resolutionflow.com>
Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
2026-05-07 18:42:20 +00:00

135 lines
4.5 KiB
Python

"""Integration tests for the public Talk-to-Sales endpoint.
POST /api/v1/sales-leads — no auth, rate-limited 5/hour per IP.
"""
from unittest.mock import AsyncMock, patch
import pytest
import sqlalchemy as sa
@pytest.mark.asyncio
async def test_sales_lead_creates_row_and_sends_notification_email(client, test_db):
"""Happy path: row inserted, notification email fired, 201 returned."""
payload = {
"email": "buyer@acme.example",
"name": "Pat Buyer",
"company": "Acme MSP",
"team_size": "11-50",
"message": "We're evaluating ResolutionFlow for our NOC team.",
"source": "pricing_page",
"posthog_distinct_id": "ph_distinct_123",
}
with patch(
"app.api.endpoints.sales_leads.EmailService.send_sales_lead_notification",
new=AsyncMock(return_value=True),
) as mock_email:
response = await client.post("/api/v1/sales-leads", json=payload)
assert response.status_code == 201, response.text
body = response.json()
assert body["status"] == "received"
assert "id" in body
# Notification email was attempted (asyncio.create_task — give it a tick).
import asyncio
await asyncio.sleep(0)
await asyncio.sleep(0)
assert mock_email.await_count == 1
kwargs = mock_email.await_args.kwargs
assert kwargs["to_email"] # default placeholder until cutover
assert kwargs["lead"].email == "buyer@acme.example"
assert kwargs["lead"].source == "pricing_page"
# Row was inserted with normalized email + all fields preserved.
result = await test_db.execute(
sa.text("SELECT email, name, company, team_size, message, source, posthog_distinct_id, status FROM sales_leads")
)
rows = result.all()
assert len(rows) == 1
row = rows[0]
assert row.email == "buyer@acme.example"
assert row.name == "Pat Buyer"
assert row.company == "Acme MSP"
assert row.team_size == "11-50"
assert row.message == "We're evaluating ResolutionFlow for our NOC team."
assert row.source == "pricing_page"
assert row.posthog_distinct_id == "ph_distinct_123"
assert row.status == "new"
@pytest.mark.asyncio
async def test_sales_lead_email_failure_does_not_fail_request(client, test_db):
"""If the email send raises, the API still returns 201 and the row persists."""
payload = {
"email": "buyer2@acme.example",
"name": "Sam Lead",
"company": "Acme MSP",
"source": "register_footer",
}
with patch(
"app.api.endpoints.sales_leads.EmailService.send_sales_lead_notification",
new=AsyncMock(side_effect=RuntimeError("resend exploded")),
):
response = await client.post("/api/v1/sales-leads", json=payload)
assert response.status_code == 201, response.text
# Row must still be persisted even though email failed.
import asyncio
await asyncio.sleep(0)
result = await test_db.execute(
sa.text("SELECT count(*) FROM sales_leads WHERE email = 'buyer2@acme.example'")
)
assert result.scalar() == 1
@pytest.mark.asyncio
async def test_sales_lead_rate_limited_after_5_per_hour(client):
"""The 6th submission within an hour from the same IP returns 429.
The default `limiter` is disabled in tests (DEBUG=true). We re-enable it
for this test, then reset its state on teardown so other tests aren't
affected.
"""
from app.core.rate_limit import limiter
was_enabled = limiter.enabled
limiter.enabled = True
try:
limiter.reset()
with patch(
"app.api.endpoints.sales_leads.EmailService.send_sales_lead_notification",
new=AsyncMock(return_value=True),
):
for i in range(5):
payload = {
"email": f"lead{i}@acme.example",
"name": f"Lead {i}",
"company": "Acme MSP",
"source": "landing_page",
}
resp = await client.post("/api/v1/sales-leads", json=payload)
assert resp.status_code == 201, f"submission {i}: {resp.text}"
# 6th should be rate-limited.
resp = await client.post(
"/api/v1/sales-leads",
json={
"email": "lead6@acme.example",
"name": "Lead 6",
"company": "Acme MSP",
"source": "landing_page",
},
)
assert resp.status_code == 429, resp.text
finally:
limiter.reset()
limiter.enabled = was_enabled