"""P14.1 — Roles and Permissions API tests. Covers: - List roles - Get role by ID - Create role - Update role - Delete role - Assign role to user - Check permissions """ import pytest class TestRoles: """Role management endpoints.""" def test_list_roles(self, client, db, make_user, auth_header, seed_roles_and_permissions): """List all roles.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() resp = client.get("/roles", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert len(data) >= 3 # admin, mgr, dev at minimum def test_get_role_by_id(self, client, db, make_user, auth_header, seed_roles_and_permissions): """Get specific role.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() resp = client.get(f"/roles/{admin_role.id}", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert data["id"] == admin_role.id assert "name" in data def test_create_role(self, client, db, make_user, auth_header, seed_roles_and_permissions): """Admin can create new role.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) resp = client.post( "/roles", json={ "name": "tester", "description": "Test role", "is_global": False }, headers=auth_header(admin) ) assert resp.status_code == 201 data = resp.json() assert data["name"] == "tester" def test_update_role(self, client, db, make_user, auth_header, seed_roles_and_permissions): """Admin can update role.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) resp = client.patch( f"/roles/{dev_role.id}", json={"description": "Updated description"}, headers=auth_header(admin) ) assert resp.status_code == 200 data = resp.json() assert data["description"] == "Updated description" def test_delete_role(self, client, db, make_user, auth_header, seed_roles_and_permissions): """Admin can delete non-default role.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) # Create a role to delete resp = client.post( "/roles", json={"name": "temp-role", "description": "To delete"}, headers=auth_header(admin) ) role_id = resp.json()["id"] resp = client.delete(f"/roles/{role_id}", headers=auth_header(admin)) assert resp.status_code == 204 def test_cannot_delete_admin_role(self, client, db, make_user, auth_header, seed_roles_and_permissions): """Cannot delete admin role.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) resp = client.delete(f"/roles/{admin_role.id}", headers=auth_header(admin)) assert resp.status_code == 400 class TestPermissions: """Permission checking endpoints.""" def test_check_permission_true(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Check permission returns true when granted.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) # Dev should have view permission resp = client.get( f"/projects/{project.id}/check-permission?permission=view", headers=auth_header(user) ) assert resp.status_code == 200 data = resp.json() assert data["has_permission"] is True def test_check_permission_false(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Check permission returns false when not granted.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() # Add as guest (viewer role) from app.models.role_permission import Role guest_role = db.query(Role).filter(Role.name == "guest").first() if not guest_role: guest_role = Role(name="guest", description="Guest", is_global=False) db.add(guest_role) db.commit() make_member(project.id, user.id, guest_role.id) resp = client.get( f"/projects/{project.id}/check-permission?permission=admin", headers=auth_header(user) ) assert resp.status_code == 200 data = resp.json() assert data["has_permission"] is False class TestRoleAssignments: """Role assignment endpoints.""" def test_assign_role_to_user(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Assign role to project member.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) user = make_user(username="member") project = make_project() resp = client.post( f"/projects/{project.id}/members", json={"user_id": user.id, "role_id": dev_role.id}, headers=auth_header(admin) ) assert resp.status_code == 201 def test_change_user_role(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Change user's role in project.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) user = make_user(username="member") project = make_project() make_member(project.id, user.id, dev_role.id) resp = client.patch( f"/projects/{project.id}/members/{user.id}", json={"role_id": mgr_role.id}, headers=auth_header(admin) ) assert resp.status_code == 200 def test_remove_user_from_project(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Remove user from project.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions admin = make_user(username="admin", is_admin=True) user = make_user(username="member") project = make_project() make_member(project.id, user.id, dev_role.id) resp = client.delete( f"/projects/{project.id}/members/{user.id}", headers=auth_header(admin) ) assert resp.status_code == 204