"""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