Add the experience service with sites through analytics/AI hooks, migrations through 0011, and phase docs/manifests marking the track complete. Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
"""Analytics API tests — Phase 11.10."""
|
|
import pytest
|
|
|
|
from app.tests.conftest import TENANT_A, TENANT_B, tenant_headers
|
|
|
|
|
|
async def _workspace(client, code="analytics"):
|
|
response = await client.post(
|
|
"/api/v1/workspaces",
|
|
json={"code": code, "name": "Analytics", "status": "active"},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert response.status_code == 201, response.text
|
|
return response.json()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_analytics_report_refresh_happy_path(client):
|
|
caps = (await client.get("/capabilities")).json()
|
|
assert caps["phase"] == "11.10"
|
|
assert caps["features"]["analytics"] is True
|
|
|
|
ws = await _workspace(client)
|
|
headers = tenant_headers(TENANT_A)
|
|
|
|
report = await client.post(
|
|
"/api/v1/analytics-report-definitions",
|
|
json={
|
|
"workspace_id": ws["id"],
|
|
"code": "ops-summary",
|
|
"name": "Ops Summary",
|
|
"metric_keys": ["workspaces_count", "sites_count", "pages_count"],
|
|
"status": "active",
|
|
},
|
|
headers=headers,
|
|
)
|
|
assert report.status_code == 201, report.text
|
|
report_id = report.json()["id"]
|
|
|
|
snapshot = await client.post(
|
|
"/api/v1/analytics-snapshots/refresh",
|
|
json={
|
|
"report_id": report_id,
|
|
"workspace_id": ws["id"],
|
|
"period_start": "2026-07-01",
|
|
"period_end": "2026-07-31",
|
|
},
|
|
headers=headers,
|
|
)
|
|
assert snapshot.status_code == 201, snapshot.text
|
|
body = snapshot.json()
|
|
assert body["metrics"]["workspaces_count"] >= 1
|
|
assert body["metrics"]["sites_count"] == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unknown_metric_key_rejected(client):
|
|
ws = await _workspace(client, "metrics-bad")
|
|
headers = tenant_headers(TENANT_A)
|
|
|
|
response = await client.post(
|
|
"/api/v1/analytics-report-definitions",
|
|
json={
|
|
"workspace_id": ws["id"],
|
|
"code": "bad-metrics",
|
|
"name": "Bad Metrics",
|
|
"metric_keys": ["unknown_metric_key"],
|
|
"status": "active",
|
|
},
|
|
headers=headers,
|
|
)
|
|
assert response.status_code == 422, response.text
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_analytics_tenant_isolation(client):
|
|
ws = await _workspace(client, "iso-analytics")
|
|
headers = tenant_headers(TENANT_A)
|
|
|
|
report = await client.post(
|
|
"/api/v1/analytics-report-definitions",
|
|
json={
|
|
"workspace_id": ws["id"],
|
|
"code": "iso-report",
|
|
"name": "Iso Report",
|
|
"metric_keys": ["workspaces_count"],
|
|
"status": "active",
|
|
},
|
|
headers=headers,
|
|
)
|
|
assert report.status_code == 201
|
|
report_id = report.json()["id"]
|
|
|
|
other = await client.get(
|
|
f"/api/v1/analytics-report-definitions/{report_id}",
|
|
headers=tenant_headers(TENANT_B),
|
|
)
|
|
assert other.status_code == 404
|