generated from coulomb/repo-seed
Add Inter-Hub migration bundle importer
This commit is contained in:
@@ -10,6 +10,7 @@ FOUNDATION_TABLES = {
|
||||
"api_keys",
|
||||
"widgets",
|
||||
"interaction_events",
|
||||
"migration_runs",
|
||||
}
|
||||
|
||||
|
||||
|
||||
116
tests/test_migration.py
Normal file
116
tests/test_migration.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import json
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
|
||||
from core_hub.migration import import_bundle, validate_bundle
|
||||
from core_hub.models import Hub, MigrationRun, Widget
|
||||
|
||||
|
||||
def minimal_bundle():
|
||||
return {
|
||||
"schemaVersion": "core-hub.migration.v1",
|
||||
"source": "inter-hub-haskell",
|
||||
"exportedAt": "2026-06-27T00:00:00Z",
|
||||
"records": {
|
||||
"hubs": [
|
||||
{
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"slug": "ops-hub",
|
||||
"name": "Ops Hub",
|
||||
"domain": "ops.coulomb.social",
|
||||
"hubKind": "domain",
|
||||
"hubFamily": "vsm",
|
||||
"vsmFunction": "OPS",
|
||||
"vsmSystem": "1",
|
||||
}
|
||||
],
|
||||
"hubCapabilityManifests": [
|
||||
{
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"hubSlug": "ops-hub",
|
||||
"manifestVersion": "1.0",
|
||||
"status": "active",
|
||||
}
|
||||
],
|
||||
"apiConsumers": [
|
||||
{
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"slug": "ops-hub",
|
||||
"name": "ops-hub",
|
||||
"hubCapabilityManifestId": "22222222-2222-4222-8222-222222222222",
|
||||
"keyPrefix": "ch_prefixonly",
|
||||
}
|
||||
],
|
||||
"apiKeys": [
|
||||
{
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"apiConsumerSlug": "ops-hub",
|
||||
"keyPrefix": "ch_prefixonly",
|
||||
"keyHash": "a" * 64,
|
||||
"scopes": "framework:read hub:ops-hub:write",
|
||||
}
|
||||
],
|
||||
"widgets": [
|
||||
{
|
||||
"id": "55555555-5555-4555-8555-555555555555",
|
||||
"hubSlug": "ops-hub",
|
||||
"name": "Readiness",
|
||||
"widgetType": "ops-readiness-gate",
|
||||
"capabilityRef": "ops:readiness",
|
||||
"viewContext": "ops-hub/readiness",
|
||||
"policyScope": "ops-registry",
|
||||
}
|
||||
],
|
||||
"interactionEvents": [
|
||||
{
|
||||
"id": "66666666-6666-4666-8666-666666666666",
|
||||
"widgetId": "55555555-5555-4555-8555-555555555555",
|
||||
"eventType": "ops-endpoint-verified",
|
||||
"metadata": {"expectedStatus": 401},
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_validate_bundle_rejects_secret_key_material():
|
||||
bundle = minimal_bundle()
|
||||
bundle["records"]["apiKeys"][0]["fullKey"] = "do-not-import"
|
||||
|
||||
report = validate_bundle(bundle)
|
||||
|
||||
assert report["ok"] is False
|
||||
assert any("secret-like" in error for error in report["errors"])
|
||||
|
||||
|
||||
async def test_dry_run_reports_counts_without_writing(sqlite_engine):
|
||||
sessionmaker = async_sessionmaker(sqlite_engine, expire_on_commit=False)
|
||||
async with sessionmaker() as session:
|
||||
report = await import_bundle(session, minimal_bundle(), dry_run=True)
|
||||
|
||||
assert report["ok"] is True
|
||||
assert report["counts"]["hubs"]["created"] == 1
|
||||
assert (await session.execute(select(Hub))).scalars().first() is None
|
||||
|
||||
|
||||
async def test_import_bundle_persists_records_and_run_summary(sqlite_engine):
|
||||
sessionmaker = async_sessionmaker(sqlite_engine, expire_on_commit=False)
|
||||
async with sessionmaker() as session:
|
||||
report = await import_bundle(session, minimal_bundle())
|
||||
|
||||
assert report["ok"] is True
|
||||
assert report["migrationRunId"]
|
||||
assert report["counts"]["widgets"]["created"] == 1
|
||||
|
||||
hub = (await session.execute(select(Hub))).scalars().one()
|
||||
widget = (await session.execute(select(Widget))).scalars().one()
|
||||
run = (await session.execute(select(MigrationRun))).scalars().one()
|
||||
|
||||
assert hub.slug == "ops-hub"
|
||||
assert widget.hub_id == hub.id
|
||||
assert json.loads(run.counts_json)["interactionEvents"]["created"] == 1
|
||||
|
||||
second = await import_bundle(session, minimal_bundle())
|
||||
assert second["counts"]["hubs"]["updated"] == 1
|
||||
assert second["counts"]["interactionEvents"]["updated"] == 1
|
||||
Reference in New Issue
Block a user