"""Tests for Audit Log API (Phase 3.4).""" import pytest from tests.conftest import auth_header pytestmark = pytest.mark.skip(reason="Skip: awaiting Go backend alignment") class TestAuditLogAPI: @pytest.mark.asyncio async def test_list_audit_logs_admin(self, client, admin_token): """Admin can list audit logs.""" resp = await client.get( "/api/v1/audit/logs", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert "items" in data assert "total" in data assert "page" in data @pytest.mark.asyncio async def test_list_audit_logs_dba(self, client, dba_token): """DBA can also list audit logs.""" resp = await client.get( "/api/v1/audit/logs", headers=auth_header(dba_token), ) assert resp.status_code == 200 @pytest.mark.asyncio async def test_list_audit_logs_developer_forbidden(self, client, developer_token): """Developer cannot access audit logs.""" resp = await client.get( "/api/v1/audit/logs", headers=auth_header(developer_token), ) assert resp.status_code == 403 @pytest.mark.asyncio async def test_create_instance_logs_audit(self, client, db, admin_token): """Creating an instance produces an audit log entry.""" from app.models.audit_log import AuditLog from sqlalchemy import select resp = await client.post( "/api/v1/instances", json={ "name": "审计测试实例", "db_type": "mysql", "host": "10.0.0.100", "port": 3306, "username": "root", "password": "test123", "database": "test", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 # Check audit log was created result = await db.execute( select(AuditLog).where(AuditLog.action == "CREATE_INSTANCE") ) log = result.scalar_one_or_none() assert log is not None assert "审计测试实例" in log.detail @pytest.mark.asyncio async def test_create_user_logs_audit(self, client, admin_token): """Creating a user produces an audit log entry.""" resp = await client.post( "/api/v1/users", json={ "username": "audit_test_user", "display_name": "审计测试", "password": "test123456", "role": "developer", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 # Audit middleware writes to its own session; verified via API in integration tests # Here we just confirm the create succeeded @pytest.mark.asyncio async def test_create_ticket_logs_audit(self, client, admin_token, test_instance): """Creating a ticket produces an audit log entry.""" resp = await client.post( "/api/v1/tickets", json={ "title": "审计日志工单测试", "db_type": "mysql", "instance_id": test_instance.id, "target_database": "test_db", "sql_content": "SELECT id FROM users LIMIT 10;", "reason": "测试审计日志", }, headers=auth_header(admin_token), ) assert resp.status_code == 201 # Audit middleware writes to its own session; verified via API in integration tests @pytest.mark.asyncio async def test_audit_log_pagination(self, client, admin_token): """Audit log pagination works.""" resp = await client.get( "/api/v1/audit/logs?page=1&page_size=5", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() assert data["page_size"] == 5 @pytest.mark.asyncio async def test_audit_log_filter_by_action(self, client, admin_token, db, test_instance): """Can filter audit logs by action.""" # Create a ticket first await client.post( "/api/v1/tickets", json={ "title": "过滤测试工单", "db_type": "mysql", "instance_id": test_instance.id, "target_database": "test_db", "sql_content": "SELECT 1;", "reason": "过滤测试用", }, headers=auth_header(admin_token), ) resp = await client.get( "/api/v1/audit/logs?action=CREATE_TICKET", headers=auth_header(admin_token), ) assert resp.status_code == 200 data = resp.json() for item in data["items"]: assert item["action"] == "CREATE_TICKET" @pytest.mark.asyncio async def test_review_ticket_logs_audit(self, client, db, admin_token, dba_token, test_instance): """Reviewing a ticket creates an audit log.""" from app.models.audit_log import AuditLog from sqlalchemy import select # Create ticket as admin resp = await client.post( "/api/v1/tickets", json={ "title": "审核日志测试", "db_type": "mysql", "instance_id": test_instance.id, "target_database": "test_db", "sql_content": "SELECT 1;", "reason": "审核日志测试", }, headers=auth_header(admin_token), ) ticket_id = resp.json()["id"] # Review as DBA resp = await client.post( f"/api/v1/tickets/{ticket_id}/review", json={"action": "approve", "comment": "批准"}, headers=auth_header(dba_token), ) assert resp.status_code == 200 result = await db.execute( select(AuditLog).where(AuditLog.action == "REVIEW_TICKET") ) log = result.scalar_one_or_none() assert log is not None assert "approve" in log.detail