import json import pytest from django.conf import settings from django.http import JsonResponse from django.test import RequestFactory, override_settings from rest_framework.response import Response from infrasynth.audit.middleware import AuditAPIMiddleware from infrasynth.audit.models import APIInteractionLog def audit_config(**overrides): return {**settings.INFRASYNTH_AUDIT, **overrides} @pytest.fixture def middleware(): return AuditAPIMiddleware(get_response=lambda request: None) def run_request(middleware, request, response): middleware.process_request(request) return middleware.process_response(request, response) class TestAPILogging: def test_logs_api_interaction(self, middleware, db): request = RequestFactory().get("/api/features/active/") response = JsonResponse({"ok": True}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/features/active/") assert log.method == "GET" assert log.status_code == 200 assert log.request_id assert log.duration_ms >= 0 def test_logs_request_body(self, middleware, db): request = RequestFactory().post( "/api/auth/login/", data=json.dumps({"username": "a@b.com"}), content_type="application/json", ) response = JsonResponse({"detail": "ok"}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/auth/login/") assert log.request_body == {"username": "a@b.com"} def test_logs_response_body(self, middleware, db): request = RequestFactory().get("/api/audit/changes/") response = Response({"count": 3, "results": []}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/audit/changes/") assert log.response_body == {"count": 3, "results": []} def test_captures_ip_and_user_agent(self, middleware, db): request = RequestFactory().get("/api/audit/changes/", HTTP_USER_AGENT="pytest-agent", REMOTE_ADDR="10.0.0.5") response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/audit/changes/") assert log.ip_address == "10.0.0.5" assert log.user_agent == "pytest-agent" class TestSensitiveData: def test_sensitive_request_body_not_stored(self, middleware, db): request = RequestFactory().post( "/api/auth/login/", data=json.dumps({"username": "a@b.com", "password": "supersecret"}), content_type="application/json", ) response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/auth/login/") assert log.request_body is None @override_settings(INFRASYNTH_AUDIT=audit_config(SENSITIVE_KEYS=["password", "my_secret"])) def test_custom_sensitive_keys(self, db): middleware = AuditAPIMiddleware(get_response=lambda request: None) request = RequestFactory().post( "/api/test/", data=json.dumps({"my_secret": "abc", "other": 1}), content_type="application/json", ) response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/test/") assert log.request_body is None class TestConfiguration: def test_admin_path_not_logged(self, middleware, db): request = RequestFactory().get("/admin/auth/user/") response = JsonResponse({}) run_request(middleware, request, response) assert not APIInteractionLog.objects.filter(path="/admin/auth/user/").exists() @override_settings(INFRASYNTH_AUDIT=audit_config(ENABLE_API_LOGGING=False)) def test_api_logging_disabled(self, db): middleware = AuditAPIMiddleware(get_response=lambda request: None) request = RequestFactory().get("/api/features/active/") response = JsonResponse({}) run_request(middleware, request, response) assert not APIInteractionLog.objects.exists() @override_settings(INFRASYNTH_AUDIT=audit_config(MAX_BODY_SIZE_BYTES=10)) def test_large_body_truncated(self, db): middleware = AuditAPIMiddleware(get_response=lambda request: None) big_payload = {"data": "x" * 100} request = RequestFactory().post( "/api/test/", data=json.dumps(big_payload), content_type="application/json", ) response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/test/") assert log.request_body is None class TestActorCapture: def test_captures_authenticated_actor(self, middleware, db, user): request = RequestFactory().get("/api/audit/changes/") request.user = user response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/audit/changes/") assert log.actor == user def test_anonymous_actor_null(self, middleware, db, user): request = RequestFactory().get("/api/audit/changes/") request.user = type("AnonUser", (), {"is_authenticated": False})() response = JsonResponse({}) run_request(middleware, request, response) log = APIInteractionLog.objects.get(path="/api/audit/changes/") assert log.actor is None