Files
grateful-journal/backend/tests/test_users.py
2026-03-24 10:48:20 +05:30

237 lines
9.4 KiB
Python

"""Tests for user management endpoints (/api/users/*)."""
import pytest
# ---------------------------------------------------------------------------
# Shared fixtures
# ---------------------------------------------------------------------------
@pytest.fixture
def registered_user(client):
"""Register a test user and return the API response data."""
c, _ = client
response = c.post("/api/users/register", json={
"email": "test@example.com",
"displayName": "Test User",
"photoURL": "https://example.com/photo.jpg",
})
assert response.status_code == 200
return response.json()
# ---------------------------------------------------------------------------
# POST /api/users/register
# ---------------------------------------------------------------------------
class TestRegisterUser:
def test_register_new_user_returns_200(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "new@example.com", "displayName": "New User"})
assert response.status_code == 200
def test_register_returns_user_fields(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "new@example.com", "displayName": "New User"})
data = response.json()
assert data["email"] == "new@example.com"
assert data["displayName"] == "New User"
assert "id" in data
assert "createdAt" in data
assert "updatedAt" in data
def test_register_returns_registered_message(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "brand_new@example.com"})
assert response.json()["message"] == "User registered successfully"
def test_register_existing_user_is_idempotent(self, client):
c, _ = client
payload = {"email": "existing@example.com"}
c.post("/api/users/register", json=payload)
response = c.post("/api/users/register", json=payload)
assert response.status_code == 200
assert response.json()["message"] == "User already exists"
def test_register_idempotent_returns_same_id(self, client):
c, _ = client
payload = {"email": "same@example.com"}
r1 = c.post("/api/users/register", json=payload).json()
r2 = c.post("/api/users/register", json=payload).json()
assert r1["id"] == r2["id"]
def test_register_uses_email_prefix_as_default_display_name(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "johndoe@example.com"})
assert response.json()["displayName"] == "johndoe"
def test_register_default_theme_is_light(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "x@example.com"})
assert response.json()["theme"] == "light"
def test_register_missing_email_returns_422(self, client):
c, _ = client
response = c.post("/api/users/register", json={"displayName": "No Email"})
assert response.status_code == 422
def test_register_without_optional_fields(self, client):
c, _ = client
response = c.post("/api/users/register", json={"email": "minimal@example.com"})
assert response.status_code == 200
assert response.json()["photoURL"] is None
# ---------------------------------------------------------------------------
# GET /api/users/by-email/{email}
# ---------------------------------------------------------------------------
class TestGetUserByEmail:
def test_returns_existing_user(self, client, registered_user):
c, _ = client
email = registered_user["email"]
response = c.get(f"/api/users/by-email/{email}")
assert response.status_code == 200
assert response.json()["email"] == email
def test_returns_all_user_fields(self, client, registered_user):
c, _ = client
response = c.get(f"/api/users/by-email/{registered_user['email']}")
data = response.json()
for field in ("id", "email", "displayName", "theme", "createdAt", "updatedAt"):
assert field in data
def test_nonexistent_email_returns_404(self, client):
c, _ = client
response = c.get("/api/users/by-email/ghost@example.com")
assert response.status_code == 404
assert "User not found" in response.json()["detail"]
# ---------------------------------------------------------------------------
# GET /api/users/{user_id}
# ---------------------------------------------------------------------------
class TestGetUserById:
def test_returns_existing_user(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
response = c.get(f"/api/users/{user_id}")
assert response.status_code == 200
assert response.json()["id"] == user_id
def test_invalid_object_id_format_returns_400(self, client):
c, _ = client
response = c.get("/api/users/not-a-valid-objectid")
assert response.status_code == 400
def test_nonexistent_valid_id_returns_404(self, client):
c, _ = client
response = c.get("/api/users/507f1f77bcf86cd799439011")
assert response.status_code == 404
# ---------------------------------------------------------------------------
# PUT /api/users/{user_id}
# ---------------------------------------------------------------------------
class TestUpdateUser:
def test_update_display_name(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
response = c.put(f"/api/users/{user_id}", json={"displayName": "Updated Name"})
assert response.status_code == 200
assert response.json()["displayName"] == "Updated Name"
def test_update_theme_to_dark(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
response = c.put(f"/api/users/{user_id}", json={"theme": "dark"})
assert response.status_code == 200
assert response.json()["theme"] == "dark"
def test_update_photo_url(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
new_url = "https://new-photo.example.com/pic.jpg"
response = c.put(f"/api/users/{user_id}", json={"photoURL": new_url})
assert response.status_code == 200
assert response.json()["photoURL"] == new_url
def test_update_persists_to_database(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
c.put(f"/api/users/{user_id}", json={"displayName": "Persisted Name"})
response = c.get(f"/api/users/{user_id}")
assert response.json()["displayName"] == "Persisted Name"
def test_partial_update_does_not_clear_other_fields(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
# Update only theme
c.put(f"/api/users/{user_id}", json={"theme": "dark"})
response = c.get(f"/api/users/{user_id}")
data = response.json()
assert data["theme"] == "dark"
assert data["displayName"] == "Test User" # original value preserved
def test_update_nonexistent_user_returns_404(self, client):
c, _ = client
response = c.put("/api/users/507f1f77bcf86cd799439011", json={"displayName": "X"})
assert response.status_code == 404
def test_update_invalid_id_format_returns_400(self, client):
c, _ = client
response = c.put("/api/users/bad-id", json={"displayName": "X"})
assert response.status_code == 400
# ---------------------------------------------------------------------------
# DELETE /api/users/{user_id}
# ---------------------------------------------------------------------------
class TestDeleteUser:
def test_delete_user_returns_200(self, client, registered_user):
c, _ = client
response = c.delete(f"/api/users/{registered_user['id']}")
assert response.status_code == 200
def test_delete_user_returns_deletion_counts(self, client, registered_user):
c, _ = client
response = c.delete(f"/api/users/{registered_user['id']}")
data = response.json()
assert data["user_deleted"] == 1
assert "entries_deleted" in data
def test_delete_user_makes_them_unretrievable(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
c.delete(f"/api/users/{user_id}")
response = c.get(f"/api/users/{user_id}")
assert response.status_code == 404
def test_delete_user_also_deletes_their_entries(self, client, registered_user):
c, _ = client
user_id = registered_user["id"]
# Create 2 entries for this user
for _ in range(2):
c.post(f"/api/entries/{user_id}", json={
"encryption": {
"encrypted": True,
"ciphertext": "dGVzdA==",
"nonce": "bm9uY2U=",
"algorithm": "XSalsa20-Poly1305",
}
})
response = c.delete(f"/api/users/{user_id}")
assert response.json()["entries_deleted"] == 2
def test_delete_nonexistent_user_returns_404(self, client):
c, _ = client
response = c.delete("/api/users/507f1f77bcf86cd799439011")
assert response.status_code == 404
def test_delete_invalid_id_format_returns_400(self, client):
c, _ = client
response = c.delete("/api/users/bad-id")
assert response.status_code == 400