6033967e6a
Ship Keystone auth, multi-service handlers (Yoga→Dalmatian), Compose/Helm packaging, API contract packs, and pytest/Pulumi coverage labs.
110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
"""Per-path OpenStack contract registration (Proxmox-style)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.routing import APIRoute
|
|
|
|
from app.openstack.contract_loader import ensure_loaded, load_series_pack
|
|
from app.openstack.mount import build_openstack_handlers, mount_openstack_routes
|
|
from app.openstack.registry import (
|
|
HandlerRegistry,
|
|
normalize_path_template,
|
|
register_specialized_handlers,
|
|
)
|
|
from app.openstack.routes import nova
|
|
from app.openstack.schema_engine import remount_schema_services
|
|
|
|
|
|
def test_normalize_path_template_collapses_param_names() -> None:
|
|
assert normalize_path_template("/v2.1/servers/{id}") == normalize_path_template(
|
|
"/v2.1/servers/{server_id}"
|
|
)
|
|
assert normalize_path_template("/v1/{account}/{container}/{object}") == normalize_path_template(
|
|
"/v1/{account}/{container}/{object_name:path}"
|
|
)
|
|
|
|
|
|
def test_swift_object_handler_resolves_from_contract_path() -> None:
|
|
registry = build_openstack_handlers()
|
|
assert registry.get("swift", "/v1/{account}/{container}/{object}", "GET") is not None
|
|
assert registry.get("swift", "/v1/{account}/{container}/{object}", "PUT") is not None
|
|
|
|
|
|
def test_handler_registry_structural_lookup() -> None:
|
|
registry = HandlerRegistry()
|
|
|
|
async def handler(request): # noqa: ANN001
|
|
return request
|
|
|
|
registry.register("nova", "/v2.1/servers/{server_id}", "GET", handler)
|
|
found = registry.get("nova", "/v2.1/servers/{id}", "GET")
|
|
assert found is handler
|
|
|
|
|
|
def test_specialized_handlers_imported_from_nova_router() -> None:
|
|
registry = HandlerRegistry()
|
|
count = register_specialized_handlers(registry, "nova", nova.router)
|
|
assert count > 0
|
|
assert registry.get("nova", "/v2.1/servers", "GET") is not None
|
|
assert registry.get("nova", "/v2.1/servers/{id}", "GET") is not None
|
|
|
|
|
|
def test_mount_registers_one_route_per_unique_method_path() -> None:
|
|
app = FastAPI()
|
|
mount_openstack_routes(app, series="dalmatian")
|
|
|
|
packs = load_series_pack("dalmatian")
|
|
expected = 0
|
|
for pack in packs.values():
|
|
expected += len({(op.method, op.path) for op in pack.operations})
|
|
|
|
contract_routes = [
|
|
route
|
|
for route in app.router.routes
|
|
if isinstance(route, APIRoute)
|
|
and isinstance(route.name, str)
|
|
and route.name.startswith("os-contract:")
|
|
]
|
|
# Contract paths plus specialized-only aliases (trailing slash, PUT tags, …).
|
|
assert len(contract_routes) >= expected
|
|
assert app.state.openstack_schema_ops == len(contract_routes)
|
|
# name format: os-contract:{service}:{METHOD}:{path}
|
|
mounted_ops = set()
|
|
for route in contract_routes:
|
|
rest = route.name[len("os-contract:") :]
|
|
_service, _, remainder = rest.partition(":")
|
|
method, _, path = remainder.partition(":")
|
|
mounted_ops.add((method, path))
|
|
for pack in packs.values():
|
|
for op in pack.operations:
|
|
assert (op.method, op.path) in mounted_ops
|
|
# No legacy schema-* route names.
|
|
assert not any(
|
|
isinstance(getattr(r, "name", None), str) and str(r.name).startswith("schema-")
|
|
for r in app.router.routes
|
|
)
|
|
|
|
|
|
def test_remount_preserves_handlers_and_route_count() -> None:
|
|
app = FastAPI()
|
|
mount_openstack_routes(app, series="dalmatian")
|
|
handlers = app.state.openstack_handlers
|
|
assert isinstance(handlers, HandlerRegistry)
|
|
before = app.state.openstack_schema_ops
|
|
|
|
ensure_loaded("caracal")
|
|
summary = remount_schema_services(app, "caracal")
|
|
assert app.state.openstack_handlers is handlers
|
|
assert summary["routes_mounted"] == app.state.openstack_schema_ops
|
|
assert app.state.openstack_schema_ops > 0
|
|
# Switching series rebuilds routes; count may differ by series deltas.
|
|
assert isinstance(before, int)
|
|
|
|
|
|
def test_build_openstack_handlers_covers_core_services() -> None:
|
|
registry = build_openstack_handlers()
|
|
for service in ("keystone", "nova", "neutron", "glance", "cinder"):
|
|
keys = [k for k in registry.keys() if k[0] == service]
|
|
assert keys, f"expected handlers for {service}"
|