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>
84 lines
3.2 KiB
Python
84 lines
3.2 KiB
Python
"""Bundles, licensing refs and feature-toggle API tests — Phase 11.8."""
|
|
import pytest
|
|
|
|
from app.tests.conftest import TENANT_A, TENANT_B, tenant_headers
|
|
|
|
|
|
async def _workspace(client, code="bundle"):
|
|
response = await client.post(
|
|
"/api/v1/workspaces",
|
|
json={"code": code, "name": "Bundles", "status": "active"},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert response.status_code == 201, response.text
|
|
return response.json()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bundle_activation_license_and_toggle(client):
|
|
caps = (await client.get("/capabilities")).json()
|
|
assert caps["phase"] == "11.10"
|
|
assert caps["features"]["bundle_licensing_shell"] is True
|
|
ws = await _workspace(client)
|
|
headers = tenant_headers(TENANT_A)
|
|
|
|
definition = await client.post(
|
|
"/api/v1/bundle-definitions",
|
|
json={"workspace_id": ws["id"], "code": "mini", "bundle_key": "mini_site",
|
|
"name": "Mini Site", "status": "available", "feature_keys": ["pages.mini"]},
|
|
headers=headers,
|
|
)
|
|
assert definition.status_code == 201, definition.text
|
|
bundle = await client.post(
|
|
"/api/v1/tenant-bundles",
|
|
json={"workspace_id": ws["id"], "bundle_definition_id": definition.json()["id"],
|
|
"status": "active", "core_entitlement_ref": "core-entitlement-1"},
|
|
headers=headers,
|
|
)
|
|
assert bundle.status_code == 201, bundle.text
|
|
assert bundle.json()["bundle_key"] == "mini_site"
|
|
|
|
license_binding = await client.post(
|
|
"/api/v1/license-bindings",
|
|
json={"workspace_id": ws["id"], "tenant_bundle_id": bundle.json()["id"],
|
|
"core_entitlement_ref": "core-entitlement-1", "status": "active"},
|
|
headers=headers,
|
|
)
|
|
assert license_binding.status_code == 201, license_binding.text
|
|
toggle = await client.post(
|
|
"/api/v1/feature-toggles",
|
|
json={"workspace_id": ws["id"], "feature_key": "pages.mini",
|
|
"enabled": True, "bundle_key": "mini_site"},
|
|
headers=headers,
|
|
)
|
|
assert toggle.status_code == 201, toggle.text
|
|
assert (await client.get(
|
|
f"/api/v1/feature-toggles/{toggle.json()['id']}", headers=tenant_headers(TENANT_B)
|
|
)).status_code == 404
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_rejects_core_plan_metadata_and_inactive_activation(client):
|
|
ws = await _workspace(client, "bundle-rules")
|
|
headers = tenant_headers(TENANT_A)
|
|
rejected = await client.post(
|
|
"/api/v1/bundle-definitions",
|
|
json={"workspace_id": ws["id"], "code": "bad", "bundle_key": "custom",
|
|
"name": "Bad", "metadata_json": {"plan_engine": "local"}},
|
|
headers=headers,
|
|
)
|
|
assert rejected.status_code == 422
|
|
|
|
draft = await client.post(
|
|
"/api/v1/bundle-definitions",
|
|
json={"workspace_id": ws["id"], "code": "draft", "bundle_key": "forms", "name": "Draft"},
|
|
headers=headers,
|
|
)
|
|
assert draft.status_code == 201, draft.text
|
|
activation = await client.post(
|
|
"/api/v1/tenant-bundles",
|
|
json={"workspace_id": ws["id"], "bundle_definition_id": draft.json()["id"], "status": "active"},
|
|
headers=headers,
|
|
)
|
|
assert activation.status_code == 409
|