feat: add durable store conformance harness
This commit is contained in:
parent
2ceecf6463
commit
886874d0f6
10 changed files with 937 additions and 7 deletions
124
src/user_engine/migrations.py
Normal file
124
src/user_engine/migrations.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""Migration manifest for durable user-engine store adapters."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import PurePosixPath
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MigrationStep:
|
||||
"""One ordered durable-store migration known to user-engine."""
|
||||
|
||||
version: str
|
||||
name: str
|
||||
description: str
|
||||
record_types: tuple[str, ...]
|
||||
sql_path: str | None = None
|
||||
|
||||
|
||||
USER_ENGINE_STORE_RECORD_TYPES = (
|
||||
"users",
|
||||
"accounts",
|
||||
"external_identities",
|
||||
"tenant_accounts",
|
||||
"memberships",
|
||||
"applications",
|
||||
"application_bindings",
|
||||
"catalogs",
|
||||
"family_invitations",
|
||||
"registration_sessions",
|
||||
"identity_factors",
|
||||
"prepared_accounts",
|
||||
"access_profiles",
|
||||
"active_access_contexts",
|
||||
"welcome_protocols",
|
||||
"onboarding_journeys",
|
||||
"profile_values",
|
||||
"audit_records",
|
||||
"outbox_events",
|
||||
)
|
||||
|
||||
USER_ENGINE_RECORD_COUNT_KEYS = (
|
||||
"users",
|
||||
"accounts",
|
||||
"tenant_accounts",
|
||||
"memberships",
|
||||
"applications",
|
||||
"bindings",
|
||||
"catalogs",
|
||||
"family_invitations",
|
||||
"registration_sessions",
|
||||
"identity_factors",
|
||||
"prepared_accounts",
|
||||
"access_profiles",
|
||||
"active_access_contexts",
|
||||
"welcome_protocols",
|
||||
"onboarding_journeys",
|
||||
"profile_values",
|
||||
"audit_records",
|
||||
"pending_outbox_events",
|
||||
)
|
||||
|
||||
USER_ENGINE_MIGRATIONS = (
|
||||
MigrationStep(
|
||||
version="0001_initial",
|
||||
name="initial durable store",
|
||||
description=(
|
||||
"Create the schema-version ledger, logical record table, audit "
|
||||
"log table, and pending outbox table used by user-engine store "
|
||||
"adapters."
|
||||
),
|
||||
record_types=USER_ENGINE_STORE_RECORD_TYPES,
|
||||
sql_path="migrations/postgres/0001_user_engine_store.sql",
|
||||
),
|
||||
)
|
||||
|
||||
LATEST_SCHEMA_VERSION = USER_ENGINE_MIGRATIONS[-1].version
|
||||
|
||||
|
||||
def migration_manifest() -> tuple[MigrationStep, ...]:
|
||||
"""Return the ordered durable-store migration manifest."""
|
||||
return USER_ENGINE_MIGRATIONS
|
||||
|
||||
|
||||
def validate_migration_manifest(
|
||||
migrations: tuple[MigrationStep, ...] = USER_ENGINE_MIGRATIONS,
|
||||
) -> tuple[str, ...]:
|
||||
"""Return manifest validation errors without touching provider resources."""
|
||||
errors: list[str] = []
|
||||
if not migrations:
|
||||
return ("migration manifest must not be empty",)
|
||||
|
||||
seen_versions: set[str] = set()
|
||||
previous_version = ""
|
||||
for migration in migrations:
|
||||
if not migration.version:
|
||||
errors.append("migration version must not be empty")
|
||||
if migration.version in seen_versions:
|
||||
errors.append(f"duplicate migration version {migration.version}")
|
||||
if previous_version and migration.version <= previous_version:
|
||||
errors.append(
|
||||
f"migration {migration.version} must sort after {previous_version}"
|
||||
)
|
||||
if not migration.name:
|
||||
errors.append(f"migration {migration.version} must have a name")
|
||||
if not migration.record_types:
|
||||
errors.append(
|
||||
f"migration {migration.version} must declare logical record types"
|
||||
)
|
||||
if len(set(migration.record_types)) != len(migration.record_types):
|
||||
errors.append(f"migration {migration.version} has duplicate record types")
|
||||
if migration.sql_path is not None:
|
||||
path = PurePosixPath(migration.sql_path)
|
||||
if path.is_absolute() or ".." in path.parts:
|
||||
errors.append(
|
||||
f"migration {migration.version} sql_path must stay repo-relative"
|
||||
)
|
||||
if path.suffix != ".sql":
|
||||
errors.append(
|
||||
f"migration {migration.version} sql_path must reference SQL"
|
||||
)
|
||||
seen_versions.add(migration.version)
|
||||
previous_version = migration.version
|
||||
return tuple(errors)
|
||||
Loading…
Add table
Add a link
Reference in a new issue