4a93bd1c44
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
109 lines
3.5 KiB
Python
109 lines
3.5 KiB
Python
import unittest
|
|
from datetime import datetime, timezone
|
|
from types import SimpleNamespace
|
|
|
|
from fastapi import HTTPException
|
|
|
|
from app.services.audit_log_service import AuditLogService
|
|
|
|
|
|
class StubAuditLogRepository:
|
|
def __init__(self):
|
|
self.items = []
|
|
|
|
def create(self, audit_log):
|
|
if audit_log.created_at is None:
|
|
audit_log.created_at = datetime.now(timezone.utc)
|
|
self.items.append(audit_log)
|
|
return audit_log
|
|
|
|
def list_paginated(self, tenant_id, page, page_size, action=None):
|
|
items = [item for item in self.items if item.tenant_id == tenant_id]
|
|
if action:
|
|
items = [item for item in items if item.action == action]
|
|
items.sort(key=lambda item: item.created_at, reverse=True)
|
|
start = (page - 1) * page_size
|
|
return items[start : start + page_size]
|
|
|
|
def count(self, tenant_id, action=None):
|
|
return len(self.list_paginated(tenant_id, 1, 10000, action))
|
|
|
|
|
|
class StubUserRepository:
|
|
def __init__(self):
|
|
self.users = {
|
|
"user-admin": SimpleNamespace(id="user-admin", display_name="管理员"),
|
|
"user-member": SimpleNamespace(id="user-member", display_name="成员"),
|
|
}
|
|
|
|
def get_by_id(self, user_id):
|
|
return self.users.get(user_id)
|
|
|
|
|
|
class AuditLogServiceTestCase(unittest.TestCase):
|
|
def setUp(self):
|
|
self.service = AuditLogService(db=None)
|
|
self.service.repository = StubAuditLogRepository()
|
|
self.service.user_repository = StubUserRepository()
|
|
|
|
def test_record_event_creates_audit_log(self):
|
|
created = self.service.record_event(
|
|
tenant_id="tenant-demo-001",
|
|
user_id="user-admin",
|
|
action="login",
|
|
resource_type="user",
|
|
resource_id="user-admin",
|
|
detail_json={"username": "admin@example.com"},
|
|
)
|
|
|
|
self.assertEqual(created["action"], "login")
|
|
self.assertEqual(created["resource_type"], "user")
|
|
self.assertEqual(created["resource_id"], "user-admin")
|
|
self.assertEqual(created["operator"]["display_name"], "管理员")
|
|
|
|
def test_list_logs_requires_admin_role(self):
|
|
with self.assertRaises(HTTPException) as context:
|
|
self.service.list_logs(
|
|
{"id": "user-member", "tenant_id": "tenant-demo-001", "role": "member"}
|
|
)
|
|
|
|
self.assertEqual(context.exception.status_code, 403)
|
|
|
|
def test_list_logs_filters_by_tenant_and_action(self):
|
|
self.service.record_event(
|
|
tenant_id="tenant-demo-001",
|
|
user_id="user-admin",
|
|
action="login",
|
|
resource_type="user",
|
|
resource_id="user-admin",
|
|
)
|
|
self.service.record_event(
|
|
tenant_id="tenant-demo-001",
|
|
user_id="user-admin",
|
|
action="download_report",
|
|
resource_type="review",
|
|
resource_id="review-1",
|
|
)
|
|
self.service.record_event(
|
|
tenant_id="tenant-other",
|
|
user_id="user-admin",
|
|
action="download_report",
|
|
resource_type="review",
|
|
resource_id="review-2",
|
|
)
|
|
|
|
result = self.service.list_logs(
|
|
{"id": "user-admin", "tenant_id": "tenant-demo-001", "role": "admin"},
|
|
page=1,
|
|
page_size=20,
|
|
action="download_report",
|
|
)
|
|
|
|
self.assertEqual(result["total"], 1)
|
|
self.assertEqual(len(result["items"]), 1)
|
|
self.assertEqual(result["items"][0]["resource_id"], "review-1")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|