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>
154 lines
5.4 KiB
Python
154 lines
5.4 KiB
Python
"""API foundation flow — Experience Phase 11.0."""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.events.publisher import get_event_publisher
|
|
from app.events.types import ExperienceEventType
|
|
from app.tests.conftest import TENANT_A, TENANT_B, tenant_headers
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health(client):
|
|
resp = await client.get("/health")
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert body["service"] == "experience-service"
|
|
assert body["status"] == "ok"
|
|
assert body["version"] == "0.11.10.0"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_capabilities_and_metrics(client):
|
|
caps = await client.get("/capabilities")
|
|
assert caps.status_code == 200
|
|
body = caps.json()
|
|
assert body["phase"] == "11.10"
|
|
assert body["features"]["widgets"] is True
|
|
assert body["features"]["consumer_connectors"] is True
|
|
assert body["features"]["analytics"] is True
|
|
assert body["features"]["ai_content"] is True
|
|
assert body["commercial_product"] == "Torbat Pages"
|
|
assert body["features"]["foundation"] is True
|
|
assert body["features"]["sites"] is True
|
|
assert body["features"]["components"] is True
|
|
assert body["features"]["themes"] is True
|
|
assert body["features"]["layouts"] is True
|
|
assert body["features"]["page_builder"] is False
|
|
assert body["features"]["templates"] is True
|
|
assert body["features"]["template_versions"] is True
|
|
assert body["features"]["template_instantiation"] is True
|
|
assert body["features"]["site_locale_bindings"] is True
|
|
assert body["features"]["localizations"] is True
|
|
assert body["features"]["media_refs"] is True
|
|
assert body["features"]["forms"] is True
|
|
assert body["features"]["surveys"] is True
|
|
assert body["features"]["appointment_pages"] is True
|
|
assert body["features"]["seo_engine"] is True
|
|
assert body["independence"]["integration_mode"] == "api_and_events_only"
|
|
|
|
metrics = await client.get("/metrics")
|
|
assert metrics.status_code == 200
|
|
assert metrics.json()["metrics"]["sites"] == "tenant_scoped"
|
|
assert metrics.json()["metrics"]["components"] == "tenant_scoped"
|
|
assert metrics.json()["metrics"]["themes"] == "tenant_scoped"
|
|
assert metrics.json()["metrics"]["layouts"] == "tenant_scoped"
|
|
assert metrics.json()["metrics"]["media_refs"] == "tenant_scoped"
|
|
assert metrics.json()["metrics"]["forms"] == "tenant_scoped"
|
|
assert metrics.json()["phase"] == "11.10"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_foundation_flow_and_events(client):
|
|
ws = await client.post(
|
|
"/api/v1/workspaces",
|
|
json={"code": "main", "name": "Main Workspace", "status": "active"},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert ws.status_code == 201, ws.text
|
|
workspace_id = ws.json()["id"]
|
|
assert ws.json()["code"] == "main"
|
|
|
|
locale = await client.post(
|
|
"/api/v1/locale-profiles",
|
|
json={
|
|
"workspace_id": workspace_id,
|
|
"code": "fa",
|
|
"name": "Persian",
|
|
"language": "fa",
|
|
"text_direction": "rtl",
|
|
},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert locale.status_code == 201, locale.text
|
|
assert locale.json()["text_direction"] == "rtl"
|
|
|
|
provider = await client.post(
|
|
"/api/v1/external-providers",
|
|
json={
|
|
"workspace_id": workspace_id,
|
|
"code": "cdn1",
|
|
"name": "CDN",
|
|
"provider_kind": "cdn",
|
|
"adapter_key": "cdn.generic",
|
|
},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert provider.status_code == 201, provider.text
|
|
|
|
engine = await client.post(
|
|
"/api/v1/render-engines",
|
|
json={
|
|
"workspace_id": workspace_id,
|
|
"code": "ssg",
|
|
"name": "SSG Ready",
|
|
"adapter_key": "render.ssg",
|
|
},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert engine.status_code == 201, engine.text
|
|
|
|
cfg = await client.post(
|
|
"/api/v1/configurations",
|
|
json={
|
|
"workspace_id": workspace_id,
|
|
"code": "default",
|
|
"timezone": "Asia/Tehran",
|
|
"language": "fa",
|
|
},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert cfg.status_code == 201, cfg.text
|
|
|
|
setting = await client.put(
|
|
"/api/v1/settings",
|
|
json={"workspace_id": workspace_id, "key": "default_locale", "value": {"code": "fa"}},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert setting.status_code == 200, setting.text
|
|
|
|
events = get_event_publisher().published
|
|
types = {e.event_type for e in events}
|
|
assert ExperienceEventType.WORKSPACE_CREATED.value in types
|
|
assert ExperienceEventType.LOCALE_PROFILE_CREATED.value in types
|
|
assert ExperienceEventType.RENDER_ENGINE_REGISTERED.value in types
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tenant_isolation(client):
|
|
created = await client.post(
|
|
"/api/v1/workspaces",
|
|
json={"code": "iso", "name": "Iso"},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert created.status_code == 201
|
|
workspace_id = created.json()["id"]
|
|
|
|
other = await client.get(
|
|
f"/api/v1/workspaces/{workspace_id}",
|
|
headers=tenant_headers(TENANT_B),
|
|
)
|
|
assert other.status_code == 404
|
|
|
|
missing = await client.get("/api/v1/workspaces")
|
|
assert missing.status_code in (400, 422)
|