import pytest from rest_framework import status from infrasynth.audit.models import APIInteractionLog, ModelChangeLog, SecurityEvent @pytest.fixture def change_log(db, user, tenant): return ModelChangeLog.objects.create( tenant=tenant, model_label="infrasynth_security.Role", object_id="1", action="create", changes={"name": [None, "Test"]}, actor=user, request_id="req-1", ) @pytest.fixture def api_log(db, user, tenant): return APIInteractionLog.objects.create( tenant=tenant, method="GET", path="/api/v1/features/", status_code=200, actor=user, duration_ms=12, request_id="req-2", ) @pytest.fixture def security_event(db, user, tenant): return SecurityEvent.objects.create( tenant=tenant, event_type="login_failed", actor=user, ip_address="127.0.0.1", metadata={"reason": "bad_password"}, request_id="req-3", ) class TestModelChangeLogEndpoints: def test_list_changes(self, authenticated_client, change_log): resp = authenticated_client.get("/api/v1/audit/changes/", {"request_id": "req-1"}) assert resp.status_code == status.HTTP_200_OK assert resp.json()["count"] == 1 assert resp.json()["results"][0]["action"] == "create" def test_retrieve_change(self, authenticated_client, change_log): resp = authenticated_client.get(f"/api/v1/audit/changes/{change_log.pk}/") assert resp.status_code == status.HTTP_200_OK assert resp.json()["changes"] == {"name": [None, "Test"]} def test_requires_auth(self, api_client, change_log): resp = api_client.get("/api/v1/audit/changes/") assert resp.status_code == status.HTTP_401_UNAUTHORIZED def test_list_does_not_allow_create(self, authenticated_client): resp = authenticated_client.post("/api/v1/audit/changes/", {"model_label": "x"}, format="json") assert resp.status_code in ( status.HTTP_405_METHOD_NOT_ALLOWED, status.HTTP_403_FORBIDDEN, ) class TestAPIInteractionLogEndpoints: def test_list_api_logs(self, authenticated_client, api_log): resp = authenticated_client.get("/api/v1/audit/api-logs/") assert resp.status_code == status.HTTP_200_OK assert resp.json()["count"] == 1 assert resp.json()["results"][0]["method"] == "GET" def test_retrieve_api_log(self, authenticated_client, api_log): resp = authenticated_client.get(f"/api/v1/audit/api-logs/{api_log.pk}/") assert resp.status_code == status.HTTP_200_OK assert resp.json()["path"] == "/api/v1/features/" def test_requires_auth(self, api_client, api_log): resp = api_client.get("/api/v1/audit/api-logs/") assert resp.status_code == status.HTTP_401_UNAUTHORIZED class TestSecurityEventEndpoints: def test_list_security_events(self, authenticated_client, security_event): resp = authenticated_client.get("/api/v1/audit/security-events/") assert resp.status_code == status.HTTP_200_OK assert resp.json()["count"] == 1 assert resp.json()["results"][0]["event_type"] == "login_failed" def test_retrieve_security_event(self, authenticated_client, security_event): resp = authenticated_client.get(f"/api/v1/audit/security-events/{security_event.pk}/") assert resp.status_code == status.HTTP_200_OK assert resp.json()["metadata"] == {"reason": "bad_password"} def test_requires_auth(self, api_client, security_event): resp = api_client.get("/api/v1/audit/security-events/") assert resp.status_code == status.HTTP_401_UNAUTHORIZED class TestFiltering: def test_filter_by_model_label(self, authenticated_client, db, tenant): ModelChangeLog.objects.create( tenant=tenant, model_label="infrasynth_security.Role", object_id="10", action="create", changes={}, request_id="x1", ) ModelChangeLog.objects.create( tenant=tenant, model_label="infrasynth_security.Grant", object_id="99", action="create", changes={}, request_id="x2", ) resp = authenticated_client.get("/api/v1/audit/changes/", {"model_label": "infrasynth_security.Role"}) assert resp.json()["count"] == 1 def test_filter_by_action(self, authenticated_client, change_log, tenant): ModelChangeLog.objects.create( tenant=tenant, model_label="infrasynth_security.Role", object_id="77", action="delete", changes={}, request_id="req-4", ) resp = authenticated_client.get("/api/v1/audit/changes/", {"action": "create"}) assert all(r["action"] == "create" for r in resp.json()["results"]) resp = authenticated_client.get("/api/v1/audit/changes/", {"action": "delete"}) assert all(r["action"] == "delete" for r in resp.json()["results"])