Files
resolutionflow/backend/tests/test_onboarding_step.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

150 lines
4.8 KiB
Python

"""Tests for welcome-wizard onboarding-step endpoints (Phase 2)."""
import pytest
from sqlalchemy import select
from app.models.account import Account
from app.models.user import User
@pytest.mark.asyncio
async def test_onboarding_step1_complete_writes_account_name_and_team_size_and_role(
client, auth_headers, test_db, test_user
):
"""Step 1 + complete writes account.name + team_size_bucket + user.role_at_signup
and advances onboarding_step_completed to 1."""
response = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={
"step": 1,
"action": "complete",
"data": {
"company_name": "Acme MSP",
"team_size_bucket": "3-5",
"role_at_signup": "owner",
},
},
)
assert response.status_code == 200, response.text
data = response.json()
assert data["onboarding_step_completed"] == 1
assert data["onboarding_dismissed"] is False
# Verify persisted writes
account_id = test_user["user_data"]["account_id"]
user_email = test_user["email"]
acct = (
await test_db.execute(select(Account).where(Account.id == account_id))
).scalar_one()
assert acct.name == "Acme MSP"
assert acct.team_size_bucket == "3-5"
user = (
await test_db.execute(select(User).where(User.email == user_email))
).scalar_one()
assert user.role_at_signup == "owner"
assert user.onboarding_step_completed == 1
@pytest.mark.asyncio
async def test_onboarding_step2_skip_advances_without_psa(
client, auth_headers, test_db, test_user
):
"""Step 2 + skip ignores data entirely and only advances the step counter
(no primary_psa write)."""
# Capture original account.primary_psa so we can assert it's untouched.
account_id = test_user["user_data"]["account_id"]
acct_before = (
await test_db.execute(select(Account).where(Account.id == account_id))
).scalar_one()
psa_before = acct_before.primary_psa # likely None
# Advance step 1 first so step 2 is allowed.
r1 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={"step": 1, "action": "skip"},
)
assert r1.status_code == 200, r1.text
# Skip step 2 — even if data is present it must be ignored.
r2 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={
"step": 2,
"action": "skip",
"data": {"primary_psa": "connectwise"},
},
)
assert r2.status_code == 200, r2.text
assert r2.json()["onboarding_step_completed"] == 2
# Re-fetch account: primary_psa must NOT have been written.
test_db.expire_all()
acct_after = (
await test_db.execute(select(Account).where(Account.id == account_id))
).scalar_one()
assert acct_after.primary_psa == psa_before
@pytest.mark.asyncio
async def test_onboarding_step_cannot_decrease(client, auth_headers):
"""A step=2 PATCH followed by step=1 must return 400."""
# Advance to step 2.
r1 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={"step": 1, "action": "skip"},
)
assert r1.status_code == 200, r1.text
r2 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={"step": 2, "action": "skip"},
)
assert r2.status_code == 200, r2.text
assert r2.json()["onboarding_step_completed"] == 2
# Try to go back to step 1 — must fail.
r3 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={"step": 1, "action": "skip"},
)
assert r3.status_code == 400, r3.text
# Idempotent re-PATCH of same step succeeds.
r4 = await client.patch(
"/api/v1/users/me/onboarding-step",
headers=auth_headers,
json={"step": 2, "action": "skip"},
)
assert r4.status_code == 200, r4.text
assert r4.json()["onboarding_step_completed"] == 2
@pytest.mark.asyncio
async def test_onboarding_dismiss_rest_sets_flag(
client, auth_headers, test_db, test_user
):
"""POST /users/me/onboarding-dismiss-rest sets users.onboarding_dismissed=TRUE."""
response = await client.post(
"/api/v1/users/me/onboarding-dismiss-rest",
headers=auth_headers,
)
assert response.status_code == 200, response.text
data = response.json()
assert data["onboarding_dismissed"] is True
# step counter is whatever it was (None for a fresh user).
assert "onboarding_step_completed" in data
# Verify persisted.
user_email = test_user["email"]
user = (
await test_db.execute(select(User).where(User.email == user_email))
).scalar_one()
assert user.onboarding_dismissed is True