"""Tests for enhanced audit trail (IP, user-agent, integrity hash).""" from datetime import datetime, timedelta, timezone import pytest from app.middleware.request_context import request_ip, request_user_agent from app.models.audit import AuditLog from app.services.audit_service import ( compute_integrity_hash, log_action, verify_audit_integrity, ) from app.jobs.retention_job import apply_retention_policies class TestAuditIntegrity: def test_integrity_hash_set_on_log(self, db): entry = log_action( db, user_id=None, action="test_action", entity_type="test", entity_id="abc", ip_address="10.0.0.1", user_agent="pytest", ) db.commit() db.refresh(entry) assert entry.integrity_hash assert len(entry.integrity_hash) == 64 assert verify_audit_integrity(entry) def test_tampered_entry_fails_integrity(self, db): entry = log_action( db, user_id=None, action="test_action", entity_type="test", entity_id="abc", ) db.commit() entry.entity_id = "tampered" assert not verify_audit_integrity(entry) def test_recomputed_hash_matches_stored(self, db): entry = log_action(db, None, "update", "user", "1") db.commit() assert entry.integrity_hash == compute_integrity_hash(entry) class TestRequestContext: def test_context_vars_used_by_log_action(self, db): token_ip = request_ip.set("203.0.113.42") token_ua = request_user_agent.set("AegisTestClient/1.0") try: entry = log_action(db, None, "ctx_action", "system", None) db.commit() assert entry.ip_address == "203.0.113.42" assert entry.user_agent == "AegisTestClient/1.0" finally: request_ip.reset(token_ip) request_user_agent.reset(token_ua) class TestRetentionJob: def test_deletes_old_audit_logs(self, db): old = AuditLog( action="old", entity_type="system", timestamp=datetime.now(timezone.utc) - timedelta(days=800), ) recent = AuditLog( action="recent", entity_type="system", timestamp=datetime.now(timezone.utc) - timedelta(days=1), ) db.add_all([old, recent]) db.commit() summary = apply_retention_policies(db) assert summary["audit_logs_deleted"] >= 1 remaining = db.query(AuditLog).all() assert all(log.action != "old" for log in remaining)