generated from coulomb/repo-seed
Implement ops-hub bootstrap compatibility
This commit is contained in:
@@ -1,14 +1,33 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
||||
|
||||
import core_hub.models # noqa: F401
|
||||
from core_hub.app import create_app
|
||||
from core_hub.db import Base
|
||||
from core_hub.config import get_settings
|
||||
from core_hub.db import Base, get_engine, get_sessionmaker
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
return create_app()
|
||||
def app(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("CORE_HUB_DATABASE_URL", f"sqlite+aiosqlite:///{tmp_path / 'core-hub.db'}")
|
||||
monkeypatch.setenv("CORE_HUB_API_TOKEN", "operator-token")
|
||||
monkeypatch.setenv("CORE_HUB_AUTO_CREATE_TABLES", "1")
|
||||
get_settings.cache_clear()
|
||||
get_engine.cache_clear()
|
||||
get_sessionmaker.cache_clear()
|
||||
try:
|
||||
yield create_app()
|
||||
finally:
|
||||
get_settings.cache_clear()
|
||||
get_engine.cache_clear()
|
||||
get_sessionmaker.cache_clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app):
|
||||
with TestClient(app) as test_client:
|
||||
yield test_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
PUBLIC_ENDPOINTS = [
|
||||
"/api/v2/hubs",
|
||||
"/api/v2/hub-capability-manifests",
|
||||
CATALOG_ENDPOINTS = [
|
||||
"/api/v2/widget-types",
|
||||
"/api/v2/event-types",
|
||||
"/api/v2/annotation-categories",
|
||||
@@ -12,6 +8,8 @@ PUBLIC_ENDPOINTS = [
|
||||
]
|
||||
|
||||
PROTECTED_ENDPOINTS = [
|
||||
"/api/v2/hubs",
|
||||
"/api/v2/hub-capability-manifests",
|
||||
"/api/v2/api-consumers",
|
||||
"/api/v2/widgets",
|
||||
"/api/v2/interaction-events",
|
||||
@@ -23,33 +21,127 @@ PROTECTED_ENDPOINTS = [
|
||||
"/api/v2/hub-registry",
|
||||
]
|
||||
|
||||
OPERATOR_HEADERS = {"Authorization": "Bearer operator-token"}
|
||||
|
||||
def test_public_api_v2_endpoints_respond(app):
|
||||
client = TestClient(app)
|
||||
|
||||
for endpoint in PUBLIC_ENDPOINTS:
|
||||
def test_catalog_endpoints_respond_without_auth(client):
|
||||
for endpoint in CATALOG_ENDPOINTS:
|
||||
response = client.get(endpoint)
|
||||
assert response.status_code == 200, endpoint
|
||||
|
||||
|
||||
def test_hubs_seed_core_hub(app):
|
||||
response = TestClient(app).get("/api/v2/hubs")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()[0]["slug"] == "core-hub"
|
||||
|
||||
|
||||
def test_protected_api_v2_endpoints_fail_before_business_logic(app):
|
||||
client = TestClient(app)
|
||||
|
||||
def test_protected_api_v2_endpoints_fail_before_business_logic(client):
|
||||
for endpoint in PROTECTED_ENDPOINTS:
|
||||
response = client.get(endpoint)
|
||||
assert response.status_code == 401, endpoint
|
||||
assert response.json()["detail"]["code"] == "unauthorized"
|
||||
|
||||
|
||||
def test_openapi_contains_compatibility_routes(app):
|
||||
payload = TestClient(app).get("/api/v2/openapi.json").json()
|
||||
def test_openapi_contains_ops_hub_gate_paths(client):
|
||||
payload = client.get("/api/v2/openapi.json").json()
|
||||
|
||||
assert "/api/v2/hubs" in payload["paths"]
|
||||
assert "/api/v2/widgets" in payload["paths"]
|
||||
assert "/hubs" in payload["paths"]
|
||||
assert "/hub-capability-manifests" in payload["paths"]
|
||||
assert "/api-consumers" in payload["paths"]
|
||||
assert "/policy-scopes" in payload["paths"]
|
||||
|
||||
|
||||
def test_ops_hub_bootstrap_sequence(client):
|
||||
hub = client.post(
|
||||
"/api/v2/hubs",
|
||||
headers=OPERATOR_HEADERS,
|
||||
json={
|
||||
"slug": "ops-hub",
|
||||
"name": "Ops Hub",
|
||||
"domain": "ops.coulomb.social",
|
||||
"hubKind": "domain",
|
||||
"hubFamily": "vsm",
|
||||
"vsmFunction": "OPS",
|
||||
"vsmSystem": "1",
|
||||
},
|
||||
)
|
||||
assert hub.status_code == 201
|
||||
hub_record = hub.json()
|
||||
assert hub_record["slug"] == "ops-hub"
|
||||
|
||||
listed_hubs = client.get("/api/v2/hubs", headers=OPERATOR_HEADERS).json()
|
||||
assert listed_hubs["data"][0]["id"] == hub_record["id"]
|
||||
|
||||
manifest = client.post(
|
||||
"/api/v2/hub-capability-manifests",
|
||||
headers=OPERATOR_HEADERS,
|
||||
json={
|
||||
"hubId": hub_record["id"],
|
||||
"manifestVersion": "1.0",
|
||||
"declaredWidgetTypes": ["ops-readiness-gate"],
|
||||
"declaredEventTypes": ["ops-endpoint-verified"],
|
||||
"declaredAnnotationCategories": ["ops-readiness-blocker"],
|
||||
"declaredPolicyScopes": ["ops-registry"],
|
||||
},
|
||||
)
|
||||
assert manifest.status_code == 201
|
||||
manifest_record = manifest.json()
|
||||
|
||||
activated = client.post(
|
||||
f"/api/v2/hub-capability-manifests/{manifest_record['id']}/activate",
|
||||
headers=OPERATOR_HEADERS,
|
||||
)
|
||||
assert activated.status_code == 200
|
||||
assert activated.json()["status"] == "active"
|
||||
|
||||
consumer = client.post(
|
||||
"/api/v2/api-consumers",
|
||||
headers=OPERATOR_HEADERS,
|
||||
json={
|
||||
"name": "ops-hub",
|
||||
"description": "API consumer for the VSM Operations hub",
|
||||
"hubCapabilityManifestId": manifest_record["id"],
|
||||
"rateLimitPerMinute": 120,
|
||||
"quotaPerDay": 50000,
|
||||
},
|
||||
)
|
||||
assert consumer.status_code == 201
|
||||
consumer_record = consumer.json()
|
||||
|
||||
key_response = client.post(
|
||||
f"/api/v2/api-consumers/{consumer_record['id']}/api-keys",
|
||||
headers=OPERATOR_HEADERS,
|
||||
json={"scopes": "framework:read hub:ops-hub:read hub:ops-hub:write"},
|
||||
)
|
||||
assert key_response.status_code == 201
|
||||
runtime_key = key_response.json()["fullKey"]
|
||||
runtime_headers = {"Authorization": f"Bearer {runtime_key}"}
|
||||
|
||||
widget = client.post(
|
||||
"/api/v2/widgets",
|
||||
headers=runtime_headers,
|
||||
json={
|
||||
"hubId": hub_record["id"],
|
||||
"status": "active",
|
||||
"name": "Gitea Registry Readiness",
|
||||
"widgetType": "ops-readiness-gate",
|
||||
"capabilityRef": "ops:readiness:gitea-registry",
|
||||
"viewContext": "ops-hub/readiness/gitea-registry",
|
||||
"policyScope": "ops-registry",
|
||||
},
|
||||
)
|
||||
assert widget.status_code == 201
|
||||
widget_record = widget.json()
|
||||
|
||||
event = client.post(
|
||||
"/api/v2/interaction-events",
|
||||
headers=runtime_headers,
|
||||
json={
|
||||
"widgetId": widget_record["id"],
|
||||
"eventType": "ops-endpoint-verified",
|
||||
"viewContext": "railiance-apps/workplans/RAIL-AP-WP-0001",
|
||||
"metadata": {"expectedStatus": 401},
|
||||
},
|
||||
)
|
||||
assert event.status_code == 201
|
||||
assert event.json()["eventType"] == "ops-endpoint-verified"
|
||||
|
||||
registry = client.get("/api/v2/hub-registry", headers=runtime_headers)
|
||||
assert registry.status_code == 200
|
||||
assert registry.json()["data"]["hubs"][0]["slug"] == "ops-hub"
|
||||
|
||||
@@ -3,9 +3,18 @@ from sqlalchemy import inspect
|
||||
import core_hub.models # noqa: F401
|
||||
from core_hub.db import Base
|
||||
|
||||
FOUNDATION_TABLES = {
|
||||
"hubs",
|
||||
"hub_capability_manifests",
|
||||
"api_consumers",
|
||||
"api_keys",
|
||||
"widgets",
|
||||
"interaction_events",
|
||||
}
|
||||
|
||||
|
||||
def test_metadata_contains_foundation_tables():
|
||||
assert {"hubs", "hub_capability_manifests", "api_consumers"}.issubset(Base.metadata.tables)
|
||||
assert FOUNDATION_TABLES.issubset(Base.metadata.tables)
|
||||
|
||||
|
||||
async def test_sqlite_fixture_creates_foundation_tables(sqlite_engine):
|
||||
@@ -14,4 +23,4 @@ async def test_sqlite_fixture_creates_foundation_tables(sqlite_engine):
|
||||
lambda sync_conn: inspect(sync_conn).get_table_names()
|
||||
)
|
||||
|
||||
assert {"hubs", "hub_capability_manifests", "api_consumers"}.issubset(table_names)
|
||||
assert FOUNDATION_TABLES.issubset(table_names)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_healthz(app):
|
||||
response = TestClient(app).get("/healthz")
|
||||
def test_healthz(client):
|
||||
response = client.get("/healthz")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "ok"
|
||||
assert response.json()["service"] == "core-hub"
|
||||
|
||||
|
||||
def test_readyz(app):
|
||||
response = TestClient(app).get("/readyz")
|
||||
def test_readyz(client):
|
||||
response = client.get("/readyz")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["checks"]["database_url"] == "configured"
|
||||
|
||||
Reference in New Issue
Block a user