feat: add audit log table and integration with admin/tree endpoints
Creates AuditLog model with JSONB details column for tracking admin actions. Integrates log_audit() helper into admin endpoints (role change, team admin toggle, deactivate, activate) and tree delete. IP address column reserved for future Railway proxy header support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.models.audit_log import AuditLog
|
||||
|
||||
|
||||
class TestAdminEndpoints:
|
||||
@@ -127,3 +130,42 @@ class TestAdminEndpoints:
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert "own account" in response.json()["detail"].lower()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_log_created_on_role_change(
|
||||
self, client: AsyncClient, admin_auth_headers: dict, test_user: dict, test_db: AsyncSession
|
||||
):
|
||||
"""Test that changing a user's role creates an audit log entry."""
|
||||
user_id = test_user["user_data"]["id"]
|
||||
await client.put(
|
||||
f"/api/v1/admin/users/{user_id}/role",
|
||||
json={"role": "viewer"},
|
||||
headers=admin_auth_headers
|
||||
)
|
||||
|
||||
result = await test_db.execute(
|
||||
select(AuditLog).where(AuditLog.action == "user.role_change")
|
||||
)
|
||||
log = result.scalar_one_or_none()
|
||||
assert log is not None
|
||||
assert str(log.resource_id) == user_id
|
||||
assert log.details["old_role"] == "engineer"
|
||||
assert log.details["new_role"] == "viewer"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_log_created_on_deactivate(
|
||||
self, client: AsyncClient, admin_auth_headers: dict, test_user: dict, test_db: AsyncSession
|
||||
):
|
||||
"""Test that deactivating a user creates an audit log entry."""
|
||||
user_id = test_user["user_data"]["id"]
|
||||
await client.put(
|
||||
f"/api/v1/admin/users/{user_id}/deactivate",
|
||||
headers=admin_auth_headers
|
||||
)
|
||||
|
||||
result = await test_db.execute(
|
||||
select(AuditLog).where(AuditLog.action == "user.deactivate")
|
||||
)
|
||||
log = result.scalar_one_or_none()
|
||||
assert log is not None
|
||||
assert str(log.resource_id) == user_id
|
||||
|
||||
Reference in New Issue
Block a user