feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
75
backend/tests/unit/test_audit_service.py
Normal file
75
backend/tests/unit/test_audit_service.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user