test(P14.1): add comprehensive backend API test suite
Add 134 tests as independent test project: - test_auth.py (5): Login, JWT, protected endpoints - test_users.py (8): User CRUD, permissions - test_projects.py (8): Project CRUD, ownership - test_milestones.py (7): Milestone CRUD, filtering - test_tasks.py (8): Task CRUD, filtering - test_comments.py (5): Comment CRUD, permissions - test_roles.py (9): Role/permission management - test_milestone_actions.py (17): Milestone state machine - test_task_transitions.py (34): Task state machine - test_propose.py (19): Propose CRUD, lifecycle - test_misc.py (14): Notifications, activity, API keys, dashboard Setup: - conftest.py: SQLite in-memory DB, fixtures - requirements.txt: Dependencies - pyproject.toml: Pytest config - README.md: Documentation
This commit is contained in:
108
tests/test_projects.py
Normal file
108
tests/test_projects.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""P14.1 — Projects API tests.
|
||||
|
||||
Covers:
|
||||
- List projects
|
||||
- Get project by ID
|
||||
- Create project
|
||||
- Update project
|
||||
- Delete project
|
||||
- Project ownership and permissions
|
||||
"""
|
||||
import pytest
|
||||
|
||||
|
||||
class TestProjects:
|
||||
"""Project management endpoints."""
|
||||
|
||||
def test_list_projects(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions):
|
||||
"""User can list projects they have access to."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
project1 = make_project(name="Project 1")
|
||||
project2 = make_project(name="Project 2")
|
||||
|
||||
resp = client.get("/projects", headers=auth_header(user))
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
def test_get_project_by_id(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions):
|
||||
"""Get specific project details."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
project = make_project(name="Test Project", owner_id=user.id)
|
||||
|
||||
resp = client.get(f"/projects/{project.id}", headers=auth_header(user))
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["id"] == project.id
|
||||
assert data["name"] == "Test Project"
|
||||
|
||||
def test_create_project(self, client, db, make_user, auth_header, seed_roles_and_permissions):
|
||||
"""User can create project."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
|
||||
resp = client.post(
|
||||
"/projects",
|
||||
json={
|
||||
"name": "New Project",
|
||||
"description": "Test description",
|
||||
"project_code": "TEST"
|
||||
},
|
||||
headers=auth_header(user)
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["name"] == "New Project"
|
||||
assert data["project_code"] == "TEST"
|
||||
assert "id" in data
|
||||
|
||||
def test_update_project(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions):
|
||||
"""Project owner can update project."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
project = make_project(name="Old Name", owner_id=user.id)
|
||||
|
||||
resp = client.patch(
|
||||
f"/projects/{project.id}",
|
||||
json={"name": "Updated Name"},
|
||||
headers=auth_header(user)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["name"] == "Updated Name"
|
||||
|
||||
def test_delete_project(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions):
|
||||
"""Project owner can delete project."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
project = make_project(owner_id=user.id)
|
||||
|
||||
resp = client.delete(f"/projects/{project.id}", headers=auth_header(user))
|
||||
assert resp.status_code == 204
|
||||
|
||||
def test_non_owner_cannot_delete_project(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member):
|
||||
"""Non-owner cannot delete project."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
owner = make_user(username="owner")
|
||||
other = make_user(username="other")
|
||||
project = make_project(owner_id=owner.id)
|
||||
make_member(project.id, other.id, dev_role.id)
|
||||
|
||||
resp = client.delete(f"/projects/{project.id}", headers=auth_header(other))
|
||||
assert resp.status_code == 403
|
||||
|
||||
def test_project_code_generation(self, client, db, make_user, auth_header, seed_roles_and_permissions):
|
||||
"""Project code is auto-generated if not provided."""
|
||||
admin_role, mgr_role, dev_role = seed_roles_and_permissions
|
||||
user = make_user()
|
||||
|
||||
resp = client.post(
|
||||
"/projects",
|
||||
json={"name": "Auto Code Project"},
|
||||
headers=auth_header(user)
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["project_code"].startswith("P")
|
||||
Reference in New Issue
Block a user