perf(organizations): cache and instrument API responses
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
"""Tests for core middleware"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from io import StringIO
|
||||
|
||||
from apps.core.middleware import (
|
||||
ApiCsrfExemptMiddleware,
|
||||
ApiSlashlessRouteMiddleware,
|
||||
OrganizationApiMetricsMiddleware,
|
||||
RequestIDMiddleware,
|
||||
RequestLoggingMiddleware,
|
||||
get_request_id,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
from organizations.models import Organization
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
|
||||
@@ -126,3 +130,70 @@ class ApiSlashlessRouteMiddlewareTest(APITestCase):
|
||||
self.middleware.process_request(request)
|
||||
|
||||
self.assertEqual(request.path_info, "/admin/login")
|
||||
|
||||
|
||||
class OrganizationApiMetricsMiddlewareTest(APITestCase):
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_middleware_is_enabled_for_organization_endpoint_metrics(self):
|
||||
self.assertIn(
|
||||
"apps.core.middleware.OrganizationApiMetricsMiddleware",
|
||||
settings.MIDDLEWARE,
|
||||
)
|
||||
|
||||
def test_logs_organization_endpoint_metrics_without_query_values(self):
|
||||
Organization.objects.create(name='ООО "Метрика"', inn="7707083893")
|
||||
|
||||
def get_response(request):
|
||||
Organization.objects.count()
|
||||
response = HttpResponse("ok", status=200)
|
||||
response["X-Cache"] = "MISS"
|
||||
return response
|
||||
|
||||
middleware = OrganizationApiMetricsMiddleware(get_response)
|
||||
request = self.factory.get(
|
||||
"/api/v2/organizations/",
|
||||
{"page": "1", "page_size": "20", "search": "7707083893"},
|
||||
)
|
||||
request.request_id = "metrics-request"
|
||||
request.user = type("AnonymousUser", (), {"is_authenticated": False})()
|
||||
|
||||
with self.assertLogs("organizations.api.metrics", level="INFO") as captured:
|
||||
response = middleware(request)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(captured.output), 1)
|
||||
_, payload = captured.output[0].split("organization_api_metrics ", 1)
|
||||
metrics = json.loads(payload)
|
||||
|
||||
self.assertEqual(metrics["request_id"], "metrics-request")
|
||||
self.assertEqual(metrics["method"], "GET")
|
||||
self.assertEqual(metrics["path"], "/api/v2/organizations/")
|
||||
self.assertEqual(metrics["status_code"], 200)
|
||||
self.assertEqual(metrics["cache"], "MISS")
|
||||
self.assertEqual(metrics["query_keys"], ["page", "page_size", "search"])
|
||||
self.assertGreaterEqual(metrics["db_query_count"], 1)
|
||||
self.assertGreater(metrics["duration_ms"], 0)
|
||||
self.assertGreater(metrics["response_size_bytes"], 0)
|
||||
self.assertNotIn("7707083893", captured.output[0])
|
||||
|
||||
def test_ignores_non_organization_api_paths(self):
|
||||
middleware = OrganizationApiMetricsMiddleware(
|
||||
lambda request: HttpResponse("ok", status=200)
|
||||
)
|
||||
request = self.factory.get("/api/v2/sources/")
|
||||
request.request_id = "metrics-request"
|
||||
request.user = type("AnonymousUser", (), {"is_authenticated": False})()
|
||||
|
||||
logger = logging.getLogger("organizations.api.metrics")
|
||||
stream = StringIO()
|
||||
handler = logging.StreamHandler(stream)
|
||||
logger.addHandler(handler)
|
||||
try:
|
||||
response = middleware(request)
|
||||
finally:
|
||||
logger.removeHandler(handler)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(stream.getvalue(), "")
|
||||
|
||||
Reference in New Issue
Block a user