hub-core/tests/test_postgres_store.py
tegwick 8ab1d0c09a
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 2s
feat: add durable Core Hub absorption runtime
2026-08-21 16:16:42 +02:00

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"},
}