2025-05-24 12:27:32 +02:00

549 lines
16 KiB
Python

import pytest
from fastapi.testclient import TestClient
from bson import ObjectId
from src.models.user import UserModel
from src.models.team import TeamModel
from src.db.repositories.user_repository import user_repository
from src.db.repositories.team_repository import team_repository
@pytest.mark.asyncio
async def test_create_user(client: TestClient, admin_api_key: tuple, test_team: TeamModel):
"""Test creating a new user (admin only)"""
raw_key, _ = admin_api_key
# Set up the headers with the admin API key
headers = {"X-API-Key": raw_key}
# Create a new user
response = client.post(
"/api/v1/users",
headers=headers,
json={
"email": "newuser@example.com",
"name": "New User",
"team_id": str(test_team.id),
"is_admin": False
}
)
# Check response
assert response.status_code == 201
data = response.json()
assert "id" in data
assert data["email"] == "newuser@example.com"
assert data["name"] == "New User"
assert data["team_id"] == str(test_team.id)
assert data["is_admin"] is False
assert "created_at" in data
@pytest.mark.asyncio
async def test_create_user_non_admin(client: TestClient, user_api_key: tuple, test_team: TeamModel):
"""Test that non-admin users cannot create users"""
raw_key, _ = user_api_key
# Set up the headers with a non-admin API key
headers = {"X-API-Key": raw_key}
# Try to create a new user
response = client.post(
"/api/v1/users",
headers=headers,
json={
"email": "unauthorized@example.com",
"name": "Unauthorized User",
"team_id": str(test_team.id),
"is_admin": False
}
)
# Check that the request is forbidden
assert response.status_code == 403
assert "detail" in response.json()
assert "admin" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_list_users_in_team(client: TestClient, admin_api_key: tuple, test_team: TeamModel):
"""Test listing users in a team"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# List users in the team
response = client.get(
f"/api/v1/users?team_id={test_team.id}",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert "users" in data
assert "total" in data
assert data["total"] >= 1 # Should include at least the admin user
@pytest.mark.asyncio
async def test_list_all_users_admin_only(client: TestClient, admin_api_key: tuple):
"""Test that only admins can list all users across teams"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# List all users (no team filter)
response = client.get(
"/api/v1/users",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert "users" in data
assert "total" in data
@pytest.mark.asyncio
async def test_list_users_non_admin_restricted(client: TestClient, user_api_key: tuple):
"""Test that non-admin users can only see their own team"""
raw_key, _ = user_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to list all users
response = client.get(
"/api/v1/users",
headers=headers
)
# Should only return users from their own team
assert response.status_code == 200
data = response.json()
assert "users" in data
# All returned users should be from the same team
if data["users"]:
user_team_ids = set(user["team_id"] for user in data["users"])
assert len(user_team_ids) == 1 # Only one team represented
@pytest.mark.asyncio
async def test_get_user_by_id(client: TestClient, admin_api_key: tuple, admin_user: UserModel):
"""Test getting a specific user by ID"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Get the user
response = client.get(
f"/api/v1/users/{admin_user.id}",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["id"] == str(admin_user.id)
assert data["email"] == admin_user.email
assert data["name"] == admin_user.name
assert data["team_id"] == str(admin_user.team_id)
assert data["is_admin"] == admin_user.is_admin
@pytest.mark.asyncio
async def test_get_user_self(client: TestClient, user_api_key: tuple, regular_user: UserModel):
"""Test that users can get their own information"""
raw_key, _ = user_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Get own user information
response = client.get(
f"/api/v1/users/{regular_user.id}",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["id"] == str(regular_user.id)
assert data["email"] == regular_user.email
@pytest.mark.asyncio
async def test_get_user_other_team_forbidden(client: TestClient, user_api_key: tuple):
"""Test that users cannot access users from other teams"""
raw_key, _ = user_api_key
# Create a user in another team
other_team = TeamModel(
name="Other Team",
description="Another team for testing"
)
created_team = await team_repository.create(other_team)
other_user = UserModel(
email="other@example.com",
name="Other User",
team_id=created_team.id,
is_admin=False
)
created_user = await user_repository.create(other_user)
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to get the other user
response = client.get(
f"/api/v1/users/{created_user.id}",
headers=headers
)
# Check that the request is forbidden
assert response.status_code == 403
assert "detail" in response.json()
@pytest.mark.asyncio
async def test_update_user(client: TestClient, admin_api_key: tuple, regular_user: UserModel):
"""Test updating a user (admin only)"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Update the user
response = client.put(
f"/api/v1/users/{regular_user.id}",
headers=headers,
json={
"name": "Updated User Name",
"email": "updated@example.com",
"is_admin": True
}
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["id"] == str(regular_user.id)
assert data["name"] == "Updated User Name"
assert data["email"] == "updated@example.com"
assert data["is_admin"] is True
assert "updated_at" in data
@pytest.mark.asyncio
async def test_update_user_self(client: TestClient, user_api_key: tuple, regular_user: UserModel):
"""Test that users can update their own basic information"""
raw_key, _ = user_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Update own information (limited fields)
response = client.put(
f"/api/v1/users/{regular_user.id}",
headers=headers,
json={
"name": "Self Updated Name",
"email": "self_updated@example.com"
# Note: is_admin should not be updatable by self
}
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["name"] == "Self Updated Name"
assert data["email"] == "self_updated@example.com"
# Admin status should remain unchanged
assert data["is_admin"] == regular_user.is_admin
@pytest.mark.asyncio
async def test_update_user_non_admin_forbidden(client: TestClient, user_api_key: tuple, admin_user: UserModel):
"""Test that non-admin users cannot update other users"""
raw_key, _ = user_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to update another user
response = client.put(
f"/api/v1/users/{admin_user.id}",
headers=headers,
json={
"name": "Unauthorized Update",
"is_admin": False
}
)
# Check that the request is forbidden
assert response.status_code == 403
assert "detail" in response.json()
@pytest.mark.asyncio
async def test_delete_user(client: TestClient, admin_api_key: tuple):
"""Test deleting a user (admin only)"""
raw_key, _ = admin_api_key
# Create a user to delete
user_to_delete = UserModel(
email="delete@example.com",
name="User to Delete",
team_id=ObjectId(),
is_admin=False
)
created_user = await user_repository.create(user_to_delete)
# Set up the headers
headers = {"X-API-Key": raw_key}
# Delete the user
response = client.delete(
f"/api/v1/users/{created_user.id}",
headers=headers
)
# Check response
assert response.status_code == 204
# Verify the user has been deleted
deleted_user = await user_repository.get_by_id(created_user.id)
assert deleted_user is None
@pytest.mark.asyncio
async def test_delete_user_non_admin(client: TestClient, user_api_key: tuple, admin_user: UserModel):
"""Test that non-admin users cannot delete users"""
raw_key, _ = user_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to delete a user
response = client.delete(
f"/api/v1/users/{admin_user.id}",
headers=headers
)
# Check that the request is forbidden
assert response.status_code == 403
assert "detail" in response.json()
assert "admin" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_get_user_activity(client: TestClient, admin_api_key: tuple, regular_user: UserModel):
"""Test getting user activity/statistics"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Get user activity
response = client.get(
f"/api/v1/users/{regular_user.id}/activity",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert "user_id" in data
assert "images_uploaded" in data
assert "last_login" in data
assert "api_key_usage" in data
@pytest.mark.asyncio
async def test_change_user_team(client: TestClient, admin_api_key: tuple, regular_user: UserModel):
"""Test moving a user to a different team (admin only)"""
raw_key, _ = admin_api_key
# Create another team
new_team = TeamModel(
name="New Team",
description="A new team for testing"
)
created_team = await team_repository.create(new_team)
# Set up the headers
headers = {"X-API-Key": raw_key}
# Move user to new team
response = client.put(
f"/api/v1/users/{regular_user.id}/team",
headers=headers,
json={
"team_id": str(created_team.id)
}
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["team_id"] == str(created_team.id)
@pytest.mark.asyncio
async def test_user_search(client: TestClient, admin_api_key: tuple):
"""Test searching for users by email or name"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Search for users
response = client.get(
"/api/v1/users/search?query=admin",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert "users" in data
assert "total" in data
# Results should contain users matching the query
for user in data["users"]:
assert "admin" in user["email"].lower() or "admin" in user["name"].lower()
@pytest.mark.asyncio
async def test_user_pagination(client: TestClient, admin_api_key: tuple):
"""Test user listing with pagination"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Test pagination
response = client.get(
"/api/v1/users?page=1&limit=10",
headers=headers
)
# Check response
assert response.status_code == 200
data = response.json()
assert "users" in data
assert "pagination" in data
assert "total" in data["pagination"]
assert "page" in data["pagination"]
assert "pages" in data["pagination"]
assert len(data["users"]) <= 10
@pytest.mark.asyncio
async def test_invalid_user_id(client: TestClient, admin_api_key: tuple):
"""Test handling of invalid user IDs"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to get user with invalid ID
response = client.get(
"/api/v1/users/invalid-id",
headers=headers
)
# Check that the request returns 400 Bad Request
assert response.status_code == 400
assert "detail" in response.json()
assert "invalid" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_nonexistent_user(client: TestClient, admin_api_key: tuple):
"""Test handling of nonexistent user IDs"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to get nonexistent user
nonexistent_id = str(ObjectId())
response = client.get(
f"/api/v1/users/{nonexistent_id}",
headers=headers
)
# Check that the request returns 404 Not Found
assert response.status_code == 404
assert "detail" in response.json()
assert "not found" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_duplicate_email_validation(client: TestClient, admin_api_key: tuple, regular_user: UserModel):
"""Test that duplicate emails are not allowed"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Try to create user with existing email
response = client.post(
"/api/v1/users",
headers=headers,
json={
"email": regular_user.email, # Duplicate email
"name": "Duplicate Email User",
"team_id": str(regular_user.team_id),
"is_admin": False
}
)
# Check that the request is rejected
assert response.status_code == 400
assert "detail" in response.json()
assert "email" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_user_role_management(client: TestClient, admin_api_key: tuple, regular_user: UserModel):
"""Test managing user roles and permissions"""
raw_key, _ = admin_api_key
# Set up the headers
headers = {"X-API-Key": raw_key}
# Promote user to admin
response = client.put(
f"/api/v1/users/{regular_user.id}/role",
headers=headers,
json={
"is_admin": True
}
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["is_admin"] is True
# Demote user back to regular
response = client.put(
f"/api/v1/users/{regular_user.id}/role",
headers=headers,
json={
"is_admin": False
}
)
# Check response
assert response.status_code == 200
data = response.json()
assert data["is_admin"] is False