refactor: implement user deletion functionality and enhance settings management
This commit is contained in:
98
backend/tests/test_users_delete_api.py
Normal file
98
backend/tests/test_users_delete_api.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# ruff: noqa: S101
|
||||
"""Tests for user self-delete API behavior."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.api import users
|
||||
from app.core.auth import AuthContext
|
||||
from app.models.users import User
|
||||
|
||||
|
||||
@dataclass
|
||||
class _FakeSession:
|
||||
committed: int = 0
|
||||
|
||||
async def commit(self) -> None:
|
||||
self.committed += 1
|
||||
|
||||
|
||||
class _EmptyMembershipQuery:
|
||||
async def all(self, _session: Any) -> list[Any]:
|
||||
return []
|
||||
|
||||
|
||||
class _FakeOrganizationMemberModel:
|
||||
class objects:
|
||||
@staticmethod
|
||||
def filter_by(**_kwargs: Any) -> _EmptyMembershipQuery:
|
||||
return _EmptyMembershipQuery()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_me_aborts_when_clerk_delete_fails(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Local deletion should not run if Clerk account deletion fails."""
|
||||
session = _FakeSession()
|
||||
user = User(id=uuid4(), clerk_user_id="user_123")
|
||||
auth = AuthContext(actor_type="user", user=user)
|
||||
|
||||
async def _fail_delete(_clerk_user_id: str) -> None:
|
||||
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="clerk failure")
|
||||
|
||||
async def _unexpected_update(*_args: Any, **_kwargs: Any) -> int:
|
||||
raise AssertionError("crud.update_where should not be called on Clerk failure")
|
||||
|
||||
async def _unexpected_delete(*_args: Any, **_kwargs: Any) -> int:
|
||||
raise AssertionError("crud.delete_where should not be called on Clerk failure")
|
||||
|
||||
monkeypatch.setattr(users, "delete_clerk_user", _fail_delete)
|
||||
monkeypatch.setattr(users.crud, "update_where", _unexpected_update)
|
||||
monkeypatch.setattr(users.crud, "delete_where", _unexpected_delete)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await users.delete_me(session=session, auth=auth)
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_502_BAD_GATEWAY
|
||||
assert session.committed == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_me_deletes_local_user_after_clerk_success(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""User delete should invoke Clerk deletion, then remove local account."""
|
||||
session = _FakeSession()
|
||||
user = User(id=uuid4(), clerk_user_id="user_456")
|
||||
auth = AuthContext(actor_type="user", user=user)
|
||||
calls: dict[str, int] = {"clerk": 0, "update": 0, "delete": 0}
|
||||
|
||||
async def _delete_from_clerk(clerk_user_id: str) -> None:
|
||||
assert clerk_user_id == "user_456"
|
||||
calls["clerk"] += 1
|
||||
|
||||
async def _update_where(*_args: Any, **_kwargs: Any) -> int:
|
||||
calls["update"] += 1
|
||||
return 0
|
||||
|
||||
async def _delete_where(*_args: Any, **_kwargs: Any) -> int:
|
||||
calls["delete"] += 1
|
||||
return 1
|
||||
|
||||
monkeypatch.setattr(users, "delete_clerk_user", _delete_from_clerk)
|
||||
monkeypatch.setattr(users, "OrganizationMember", _FakeOrganizationMemberModel)
|
||||
monkeypatch.setattr(users.crud, "update_where", _update_where)
|
||||
monkeypatch.setattr(users.crud, "delete_where", _delete_where)
|
||||
|
||||
response = await users.delete_me(session=session, auth=auth)
|
||||
|
||||
assert response.ok is True
|
||||
assert calls["clerk"] == 1
|
||||
assert calls["update"] == 3
|
||||
assert calls["delete"] == 1
|
||||
assert session.committed == 1
|
||||
Reference in New Issue
Block a user