ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
76 lines
2.3 KiB
Python
76 lines
2.3 KiB
Python
"""Unit tests for the audit service and model.
|
|
|
|
Tests cover:
|
|
- AuditLog model can be imported
|
|
- log_action function signature is correct
|
|
- Audit logs router is importable with expected endpoints
|
|
- CSV export endpoint exists
|
|
"""
|
|
|
|
import uuid
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
class TestAuditLogModel:
|
|
"""Tests for the AuditLog ORM model."""
|
|
|
|
def test_model_importable(self):
|
|
from app.models.audit_log import AuditLog
|
|
assert AuditLog.__tablename__ == "audit_logs"
|
|
|
|
def test_model_has_required_columns(self):
|
|
from app.models.audit_log import AuditLog
|
|
mapper = AuditLog.__table__.columns
|
|
expected_columns = {
|
|
"id", "tenant_id", "user_id", "action",
|
|
"resource_type", "resource_id", "device_id",
|
|
"details", "ip_address", "created_at",
|
|
}
|
|
actual_columns = {c.name for c in mapper}
|
|
assert expected_columns.issubset(actual_columns), (
|
|
f"Missing columns: {expected_columns - actual_columns}"
|
|
)
|
|
|
|
def test_model_exported_from_init(self):
|
|
from app.models import AuditLog
|
|
assert AuditLog.__tablename__ == "audit_logs"
|
|
|
|
|
|
class TestAuditService:
|
|
"""Tests for the audit service log_action function."""
|
|
|
|
def test_log_action_importable(self):
|
|
from app.services.audit_service import log_action
|
|
assert callable(log_action)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_log_action_does_not_raise_on_db_error(self):
|
|
"""log_action must swallow exceptions so it never breaks the caller."""
|
|
from app.services.audit_service import log_action
|
|
|
|
mock_db = AsyncMock()
|
|
mock_db.execute = AsyncMock(side_effect=Exception("DB down"))
|
|
|
|
# Should NOT raise even though the DB call fails
|
|
await log_action(
|
|
db=mock_db,
|
|
tenant_id=uuid.uuid4(),
|
|
user_id=uuid.uuid4(),
|
|
action="test_action",
|
|
)
|
|
|
|
|
|
class TestAuditRouter:
|
|
"""Tests for the audit logs router."""
|
|
|
|
def test_router_importable(self):
|
|
from app.routers.audit_logs import router
|
|
assert router is not None
|
|
|
|
def test_router_has_audit_logs_endpoint(self):
|
|
from app.routers.audit_logs import router
|
|
paths = [route.path for route in router.routes]
|
|
assert "/audit-logs" in paths or any("/audit-logs" in p for p in paths)
|