Files
resolutionflow/backend/tests/test_account_lifecycle.py
Michael Chihlas 8d6accaf60 feat: add account management, email verification, AI fixes, and user guides
- Profile settings, account transfer, delete/leave account flows
- Email verification with JWT tokens and Resend integration
- AI assistant/copilot fixes: markdown rendering, shared RAG helpers,
  token tracking, input refocus, model_validate usage
- User guides hub + detail pages with 13 topic guides
- Sidebar and top bar navigation for guides

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:18:06 -05:00

110 lines
4.0 KiB
Python

"""Tests for leave account and delete account endpoints."""
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
class TestLeaveAccount:
"""Test POST /accounts/me/leave."""
async def test_leave_as_non_owner(self, client: AsyncClient, test_db):
"""Non-owner can leave and gets a personal account."""
from sqlalchemy import select
from app.models.user import User
# Register owner
owner = await client.post("/api/v1/auth/register", json={
"email": "owner@example.com", "password": "TestPassword123!", "name": "Owner",
})
assert owner.status_code == 201
owner_data = owner.json()
# Login as owner
login = await client.post("/api/v1/auth/login/json", json={
"email": "owner@example.com", "password": "TestPassword123!",
})
owner_headers = {"Authorization": f"Bearer {login.json()['access_token']}"}
# Register member
member = await client.post("/api/v1/auth/register", json={
"email": "member@example.com", "password": "TestPassword123!", "name": "Member",
})
member_id = member.json()["id"]
# Move member to owner's account
result = await test_db.execute(select(User).where(User.id == member_id))
member_user = result.scalar_one()
member_user.account_id = owner_data["account_id"]
member_user.account_role = "engineer"
await test_db.commit()
# Login as member
login = await client.post("/api/v1/auth/login/json", json={
"email": "member@example.com", "password": "TestPassword123!",
})
member_headers = {"Authorization": f"Bearer {login.json()['access_token']}"}
# Leave
response = await client.post("/api/v1/accounts/me/leave", headers=member_headers)
assert response.status_code == 200
async def test_leave_as_owner_fails(self, client: AsyncClient, auth_headers: dict):
"""Owner cannot leave their own account."""
response = await client.post("/api/v1/accounts/me/leave", headers=auth_headers)
assert response.status_code == 400
@pytest.mark.asyncio
class TestDeleteAccount:
"""Test DELETE /accounts/me."""
async def test_delete_success(self, client: AsyncClient, auth_headers: dict):
"""Owner with no other members can delete account."""
response = await client.request(
"DELETE",
"/api/v1/accounts/me",
json={"current_password": "TestPassword123!"},
headers=auth_headers,
)
assert response.status_code == 200
async def test_delete_wrong_password(self, client: AsyncClient, auth_headers: dict):
"""Wrong password returns 401."""
response = await client.request(
"DELETE",
"/api/v1/accounts/me",
json={"current_password": "WrongPassword123!"},
headers=auth_headers,
)
assert response.status_code == 401
async def test_delete_with_members_fails(self, client: AsyncClient, auth_headers: dict, test_db):
"""Cannot delete account that has other members."""
from sqlalchemy import select
from app.models.user import User
# Get owner's account_id
me = await client.get("/api/v1/auth/me", headers=auth_headers)
account_id = me.json()["account_id"]
# Register and add member
member = await client.post("/api/v1/auth/register", json={
"email": "member2@example.com", "password": "TestPassword123!", "name": "Member",
})
member_id = member.json()["id"]
result = await test_db.execute(select(User).where(User.id == member_id))
member_user = result.scalar_one()
member_user.account_id = account_id
member_user.account_role = "engineer"
await test_db.commit()
response = await client.request(
"DELETE",
"/api/v1/accounts/me",
json={"current_password": "TestPassword123!"},
headers=auth_headers,
)
assert response.status_code == 400