from datetime import UTC, datetime def test_health(client): response = client.get("/health") assert response.status_code == 200 data = response.json() assert data["status"] == "ok" assert data["database"] == "connected" def test_metrics(client): response = client.get("/metrics") assert response.status_code == 200 assert "aoc_request_duration_seconds" in response.text def test_list_events_empty(client): response = client.get("/api/events") assert response.status_code == 200 data = response.json() assert data["items"] == [] assert data["next_cursor"] is None def test_list_events_cursor_pagination(client, mock_events_collection): for i in range(5): mock_events_collection.insert_one({ "id": f"evt-{i}", "timestamp": datetime.now(UTC).isoformat(), "service": "Directory", "operation": "Add user", "result": "success", "actor_display": f"Actor {i}", "raw_text": "", }) response = client.get("/api/events?page_size=2") assert response.status_code == 200 data = response.json() assert len(data["items"]) == 2 assert data["next_cursor"] is not None # Follow cursor response2 = client.get(f"/api/events?page_size=2&cursor={data['next_cursor']}") assert response2.status_code == 200 data2 = response2.json() assert len(data2["items"]) == 2 assert data2["next_cursor"] is not None def test_list_events_filter_by_service(client, mock_events_collection): mock_events_collection.insert_one({ "id": "evt-1", "timestamp": datetime.now(UTC).isoformat(), "service": "Exchange", "operation": "Update", "result": "success", "actor_display": "Alice", "raw_text": "", }) mock_events_collection.insert_one({ "id": "evt-2", "timestamp": datetime.now(UTC).isoformat(), "service": "Directory", "operation": "Add", "result": "success", "actor_display": "Bob", "raw_text": "", }) response = client.get("/api/events?service=Exchange") assert response.status_code == 200 data = response.json() assert len(data["items"]) == 1 assert data["items"][0]["service"] == "Exchange" def test_list_events_page_size_validation(client): response = client.get("/api/events?page_size=0") assert response.status_code == 422 response = client.get("/api/events?page_size=501") assert response.status_code == 422 def test_filter_options(client, mock_events_collection): mock_events_collection.insert_one({ "id": "evt-1", "timestamp": datetime.now(UTC).isoformat(), "service": "Intune", "operation": "Assign", "result": "failure", "actor_display": "Charlie", "actor_upn": "charlie@example.com", "raw_text": "", }) response = client.get("/api/filter-options") assert response.status_code == 200 data = response.json() assert "Intune" in data["services"] assert "Assign" in data["operations"] assert "failure" in data["results"] assert "Charlie" in data["actors"] assert "charlie@example.com" in data["actor_upns"] def test_fetch_audit_logs_validation(client): response = client.get("/api/fetch-audit-logs?hours=0") assert response.status_code == 422 response = client.get("/api/fetch-audit-logs?hours=721") assert response.status_code == 422 def test_graph_webhook_validation(client): token = "test-validation-token-123" response = client.post("/api/webhooks/graph?validationToken=" + token) assert response.status_code == 200 assert response.text == token assert response.headers["content-type"] == "text/plain; charset=utf-8" def test_graph_webhook_notification(client): payload = { "value": [ { "changeType": "updated", "resource": "auditLogs/directoryAudits", "clientState": "secret", } ] } response = client.post("/api/webhooks/graph", json=payload) assert response.status_code == 200 assert response.json()["status"] == "accepted"