100 lines
3.7 KiB
Python
100 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from uuid import uuid4
|
|
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
|
|
from hub_core.contracts import extension_contract_root
|
|
from hub_core.runtime.app import create_app
|
|
from hub_core.runtime.config import RuntimeSettings
|
|
from hub_core.runtime.postgres_store import PostgresPortStore
|
|
from hub_core.runtime.tables import runtime_audit_ledger, runtime_metadata
|
|
|
|
|
|
def test_durable_store_survives_reopen_and_keeps_event_families_separate(tmp_path) -> None:
|
|
database_url = f"sqlite+aiosqlite:///{tmp_path / 'runtime.db'}"
|
|
|
|
async def create_schema() -> None:
|
|
engine = create_async_engine(database_url)
|
|
async with engine.begin() as connection:
|
|
await connection.run_sync(runtime_metadata.create_all)
|
|
await engine.dispose()
|
|
|
|
asyncio.run(create_schema())
|
|
package = json.loads(
|
|
extension_contract_root()
|
|
.joinpath("fixtures", "ops-hub.extension.json")
|
|
.read_text(encoding="utf-8")
|
|
)
|
|
settings = RuntimeSettings(
|
|
environment="production",
|
|
backend="postgresql",
|
|
allow_ephemeral=False,
|
|
database_url=database_url,
|
|
)
|
|
first_store = PostgresPortStore.from_url(database_url)
|
|
first = TestClient(create_app(settings=settings, port_store=first_store))
|
|
|
|
correlation_id = str(uuid4())
|
|
assert first.get("/readyz").status_code == 200
|
|
assert first.post(
|
|
"/ports/registry/registrations",
|
|
headers={"X-Correlation-ID": correlation_id},
|
|
json=package,
|
|
).status_code == 202
|
|
assert first.post(
|
|
"/ports/events/progress", json=_event("hub.progress.recorded")
|
|
).status_code == 202
|
|
assert first.post(
|
|
"/ports/events/interaction", json=_event("hub.interaction.recorded")
|
|
).status_code == 202
|
|
asyncio.run(first_store.aclose())
|
|
|
|
second_store = PostgresPortStore.from_url(database_url)
|
|
second = TestClient(create_app(settings=settings, port_store=second_store))
|
|
registry = second.get("/ports/projections/hub_registry").json()
|
|
progress = second.get("/ports/projections/progress_events").json()
|
|
interaction = second.get("/ports/projections/interaction_events").json()
|
|
|
|
assert registry["data"]["items"][0]["descriptor"]["hub_slug"] == "ops-hub"
|
|
assert {item["family"] for item in progress["data"]["items"]} == {"progress"}
|
|
assert {item["family"] for item in interaction["data"]["items"]} == {"interaction"}
|
|
assert progress["provenance"]["source_system"] == "hub-core-postgresql"
|
|
|
|
async def audit_count() -> int:
|
|
async with second_store.sessions() as session:
|
|
return len((await session.execute(runtime_audit_ledger.select())).all())
|
|
|
|
assert asyncio.run(audit_count()) == 3
|
|
asyncio.run(second_store.aclose())
|
|
|
|
|
|
def test_postgresql_readiness_fails_when_database_is_unavailable() -> None:
|
|
database_url = "sqlite+aiosqlite:////definitely-missing-parent/runtime.db"
|
|
store = PostgresPortStore.from_url(database_url)
|
|
settings = RuntimeSettings(
|
|
environment="production",
|
|
backend="postgresql",
|
|
allow_ephemeral=False,
|
|
database_url=database_url,
|
|
)
|
|
response = TestClient(create_app(settings=settings, port_store=store)).get("/readyz")
|
|
|
|
assert response.status_code == 503
|
|
assert response.json()["checks"]["database"] == "unavailable"
|
|
asyncio.run(store.aclose())
|
|
|
|
|
|
def _event(event_type: str) -> dict:
|
|
return {
|
|
"schema_version": "0.1.0",
|
|
"correlation_id": str(uuid4()),
|
|
"event_type": event_type,
|
|
"occurred_at": datetime.now(timezone.utc).isoformat(),
|
|
"subject_refs": {"hub": "ops-hub"},
|
|
"payload": {"result": "ok"},
|
|
}
|