Files
main_backend/tests/integration/test_projects_endpoints.py
2026-02-04 02:19:50 +03:00

255 lines
8.2 KiB
Python

"""
Tests for project management endpoints.
"""
from __future__ import annotations
import uuid
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession
from cpv3.modules.projects.models import Project
from cpv3.modules.users.models import User
@pytest.fixture
async def test_project(test_db_session: AsyncSession, test_user: User) -> Project:
"""Create a test project owned by test_user."""
project = Project(
id=uuid.uuid4(),
owner_id=test_user.id,
name="Test Project",
description="A test project",
language="en",
status="DRAFT",
is_active=True,
)
test_db_session.add(project)
await test_db_session.commit()
await test_db_session.refresh(project)
return project
@pytest.fixture
async def other_project(test_db_session: AsyncSession, other_user: User) -> Project:
"""Create a project owned by another user."""
project = Project(
id=uuid.uuid4(),
owner_id=other_user.id,
name="Other Project",
description="Another user's project",
language="en",
status="DRAFT",
is_active=True,
)
test_db_session.add(project)
await test_db_session.commit()
await test_db_session.refresh(project)
return project
class TestListProjectsEndpoint:
"""Tests for GET /api/projects/."""
async def test_list_projects_authenticated(
self, auth_client: AsyncClient, test_project: Project
):
"""Test listing projects as authenticated user."""
response = await auth_client.get("/api/projects/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
async def test_list_projects_unauthenticated(self, async_client: AsyncClient):
"""Test listing projects without auth returns 401."""
response = await async_client.get("/api/projects/")
assert response.status_code == 401
class TestCreateProjectEndpoint:
"""Tests for POST /api/projects/."""
async def test_create_project_success(self, auth_client: AsyncClient):
"""Test creating a new project."""
response = await auth_client.post(
"/api/projects/",
json={
"name": "New Project",
"description": "A new project",
"language": "en",
},
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "New Project"
assert data["description"] == "A new project"
assert data["language"] == "en"
assert data["status"] == "DRAFT"
async def test_create_project_minimal(self, auth_client: AsyncClient):
"""Test creating a project with minimal fields."""
response = await auth_client.post(
"/api/projects/",
json={"name": "Minimal Project"},
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Minimal Project"
async def test_create_project_unauthenticated(self, async_client: AsyncClient):
"""Test creating project without auth returns 401."""
response = await async_client.post(
"/api/projects/",
json={"name": "Unauthorized Project"},
)
assert response.status_code == 401
class TestRetrieveProjectEndpoint:
"""Tests for GET /api/projects/{project_id}/."""
async def test_retrieve_own_project(
self, auth_client: AsyncClient, test_project: Project
):
"""Test retrieving own project."""
response = await auth_client.get(f"/api/projects/{test_project.id}/")
assert response.status_code == 200
data = response.json()
assert data["id"] == str(test_project.id)
assert data["name"] == test_project.name
async def test_retrieve_other_project_as_staff(
self, staff_client: AsyncClient, test_project: Project
):
"""Test staff can retrieve any project."""
response = await staff_client.get(f"/api/projects/{test_project.id}/")
assert response.status_code == 200
data = response.json()
assert data["id"] == str(test_project.id)
async def test_retrieve_nonexistent_project(self, auth_client: AsyncClient):
"""Test retrieving nonexistent project returns 404."""
fake_id = uuid.uuid4()
response = await auth_client.get(f"/api/projects/{fake_id}/")
assert response.status_code == 404
assert response.json()["detail"] == "Not found"
async def test_retrieve_other_project_forbidden(
self, auth_client: AsyncClient, other_project: Project
):
"""Test regular user cannot retrieve other user's project."""
response = await auth_client.get(f"/api/projects/{other_project.id}/")
assert response.status_code == 403
assert response.json()["detail"] == "Forbidden"
class TestPatchProjectEndpoint:
"""Tests for PATCH /api/projects/{project_id}/."""
async def test_patch_own_project(
self, auth_client: AsyncClient, test_project: Project
):
"""Test updating own project."""
response = await auth_client.patch(
f"/api/projects/{test_project.id}/",
json={"name": "Updated Project", "description": "Updated description"},
)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Updated Project"
assert data["description"] == "Updated description"
async def test_patch_project_status(
self, auth_client: AsyncClient, test_project: Project
):
"""Test updating project status."""
response = await auth_client.patch(
f"/api/projects/{test_project.id}/",
json={"status": "PROCESSING"},
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "PROCESSING"
async def test_patch_other_project_as_staff(
self, staff_client: AsyncClient, test_project: Project
):
"""Test staff can update any project."""
response = await staff_client.patch(
f"/api/projects/{test_project.id}/",
json={"name": "Staff Updated"},
)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Staff Updated"
async def test_patch_nonexistent_project(self, auth_client: AsyncClient):
"""Test patching nonexistent project returns 404."""
fake_id = uuid.uuid4()
response = await auth_client.patch(
f"/api/projects/{fake_id}/",
json={"name": "Test"},
)
assert response.status_code == 404
async def test_patch_other_project_forbidden(
self, auth_client: AsyncClient, other_project: Project
):
"""Test regular user cannot update other user's project."""
response = await auth_client.patch(
f"/api/projects/{other_project.id}/",
json={"name": "Hacked"},
)
assert response.status_code == 403
class TestDeleteProjectEndpoint:
"""Tests for DELETE /api/projects/{project_id}/."""
async def test_delete_own_project(
self, auth_client: AsyncClient, test_project: Project
):
"""Test deleting (deactivating) own project."""
response = await auth_client.delete(f"/api/projects/{test_project.id}/")
assert response.status_code == 204
async def test_delete_other_project_as_staff(
self, staff_client: AsyncClient, test_project: Project
):
"""Test staff can delete any project."""
response = await staff_client.delete(f"/api/projects/{test_project.id}/")
assert response.status_code == 204
async def test_delete_nonexistent_project(self, auth_client: AsyncClient):
"""Test deleting nonexistent project returns 404."""
fake_id = uuid.uuid4()
response = await auth_client.delete(f"/api/projects/{fake_id}/")
assert response.status_code == 404
async def test_delete_other_project_forbidden(
self, auth_client: AsyncClient, other_project: Project
):
"""Test regular user cannot delete other user's project."""
response = await auth_client.delete(f"/api/projects/{other_project.id}/")
assert response.status_code == 403