from __future__ import annotations from fastapi.testclient import TestClient def test_metrics_endpoint_available(client: TestClient): """Verify that the /metrics endpoint returns HTTP 200 and Prometheus text format.""" res = client.get("/metrics") assert res.status_code == 200 assert "text/plain" in res.headers.get("content-type", "") content = res.text # Check that golden triangle labels are present assert "deployment_environment=" in content assert "service_version=" in content assert "service_name=" in content def test_http_request_metrics_recorded(client: TestClient, auth_headers: dict): """Verify that requests to API endpoints update request counters and latency histograms.""" # Execute a few requests res = client.get("/api/v1/items", headers=auth_headers) assert res.status_code == 200 metrics_res = client.get("/metrics") assert metrics_res.status_code == 200 text = metrics_res.text assert "app_http_requests_total" in text assert 'http_route="/api/v1/items"' in text assert 'http_status_code="200"' in text assert "app_http_server_duration_milliseconds" in text def test_lifecycle_items_metrics_lifecycle(client: TestClient, auth_headers: dict): """Verify creation, completion, and deletion of lifecycle items update domain metrics.""" # 1. Create renewal item create_payload = { "name": "Passport Renewal Test", "lifecycleType": "renewal", "dueDate": "2027-01-15", } create_res = client.post("/api/v1/items", json=create_payload, headers=auth_headers) assert create_res.status_code == 201 item = create_res.json() item_id = item["id"] # 2. Check items created metric metrics_res = client.get("/metrics") text = metrics_res.text assert "app_items_created_total" in text assert 'lifecycle_type="renewal"' in text # 3. Complete item complete_res = client.post(f"/api/v1/items/{item_id}/complete", headers=auth_headers) assert complete_res.status_code == 200 metrics_res2 = client.get("/metrics") text2 = metrics_res2.text assert "app_items_completed_total" in text2 assert 'lifecycle_type="renewal"' in text2 # 4. Delete item del_res = client.delete(f"/api/v1/items/{item_id}", headers=auth_headers) assert del_res.status_code == 204 metrics_res3 = client.get("/metrics") text3 = metrics_res3.text assert "app_items_deleted_total" in text3 def test_auth_and_reminders_metrics(client: TestClient, auth_headers: dict): """Verify auth and reminder metrics tracking.""" # 1. Signup metric new_user_payload = { "email": "metricstest@example.com", "name": "Metrics Test User", "password": "password123", } signup_res = client.post("/api/v1/auth/signup", json=new_user_payload) assert signup_res.status_code == 201 # 2. Failed login metric client.post("/api/v1/auth/login", json={"email": "metricstest@example.com", "password": "wrongpassword"}) # 3. Successful login metric client.post("/api/v1/auth/login", json={"email": "metricstest@example.com", "password": "password123"}) # 4. Reminders summary summary_res = client.get("/api/v1/reminders/summary", headers=auth_headers) assert summary_res.status_code == 200 # 5. Daily digest generation digest_res = client.post("/api/v1/reminders/digest/generate", headers=auth_headers) assert digest_res.status_code == 200 # Inspect metrics output metrics_res = client.get("/metrics") text = metrics_res.text assert "app_user_signups_total" in text assert 'app_user_logins_total{deployment_environment="development",service_name="api-backend",service_version="dev-latest",status="success"}' in text assert 'app_user_logins_total{deployment_environment="development",service_name="api-backend",service_version="dev-latest",status="failure"}' in text assert "app_reminders_summary_requests_total" in text assert 'app_digests_generated_total{deployment_environment="development",service_name="api-backend",service_version="dev-latest",status="success"}' in text def test_route_normalization_avoids_cardinality_explosion(client: TestClient, auth_headers: dict): """Verify that route paths with dynamic UUIDs and member emails are normalized to avoid high cardinality.""" # Access an item by ID client.get("/api/v1/items/8f51a704-5853-41bb-9466-9e1234567890", headers=auth_headers) metrics_res = client.get("/metrics") text = metrics_res.text # The route should be normalized to /api/v1/items/{id} rather than raw UUID assert 'http_route="/api/v1/items/{id}"' in text assert "8f51a704-5853-41bb-9466-9e1234567890" not in text # Access a member route by email client.delete("/api/v1/groups/group_home/members/testuser@example.com", headers=auth_headers) metrics_res2 = client.get("/metrics") text2 = metrics_res2.text assert "testuser@example.com" not in text2 assert "{memberEmail}" in text2 def test_item_creation_failure_metrics(client: TestClient, auth_headers: dict): """Verify that failed item creation attempts increment app_item_creation_failures_total.""" # Trigger an item creation failure (invalid lifecycle type or empty name) fail_res = client.post( "/api/v1/items", json={"name": " ", "lifecycleType": "renewal"}, headers=auth_headers, ) # The store/endpoint rejects empty names with 400 assert fail_res.status_code == 400 metrics_res = client.get("/metrics") assert metrics_res.status_code == 200 text = metrics_res.text assert "app_item_creation_failures_total" in text assert 'app_item_creation_failures_total{deployment_environment="development",lifecycle_type="renewal",service_name="api-backend",service_version="dev-latest"}' in text