Co-authored-by: Michael Chihlas <michael@resolutionflow.com> Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
150 lines
4.8 KiB
Python
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
|