226 lines
8.5 KiB
Python
226 lines
8.5 KiB
Python
from __future__ import annotations
|
|
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
from packages.hub_clients import ClientKind, generate_config
|
|
from services.control_api.persistence.db import Base, create_engine_from_url, session_factory
|
|
from services.control_api.persistence.machine_client import provision_machine_client
|
|
from services.control_api.persistence.models import (
|
|
ClientBindingRow,
|
|
ClientRow,
|
|
EnvironmentRow,
|
|
ProjectRow,
|
|
VirtualServerRow,
|
|
WorkspaceRow,
|
|
)
|
|
from services.runtime.client_service import PersistentClientService
|
|
|
|
|
|
@pytest.fixture
|
|
def database(): # type: ignore[no-untyped-def]
|
|
engine = create_engine_from_url("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
sessions = session_factory(engine)
|
|
workspace_id, project_id, environment_id = uuid4(), uuid4(), uuid4()
|
|
with sessions() as session, session.begin():
|
|
session.add(WorkspaceRow(id=workspace_id, slug="itworx", name="ITWorx"))
|
|
session.flush()
|
|
session.add(
|
|
ProjectRow(
|
|
id=project_id,
|
|
workspace_id=workspace_id,
|
|
slug="ludarium",
|
|
name="Ludarium",
|
|
)
|
|
)
|
|
session.flush()
|
|
session.add(
|
|
EnvironmentRow(
|
|
id=environment_id,
|
|
workspace_id=workspace_id,
|
|
project_id=project_id,
|
|
name="prod",
|
|
classification="production",
|
|
)
|
|
)
|
|
session.flush()
|
|
session.add(
|
|
VirtualServerRow(
|
|
workspace_id=workspace_id,
|
|
project_id=project_id,
|
|
environment_id=environment_id,
|
|
slug="ludarium-prod-readonly",
|
|
desired_hash="a" * 64,
|
|
enabled=True,
|
|
gateway_external_id="a" * 32,
|
|
)
|
|
)
|
|
yield sessions
|
|
engine.dispose()
|
|
|
|
|
|
def _provision(sessions): # type: ignore[no-untyped-def]
|
|
with sessions() as session, session.begin():
|
|
return provision_machine_client(
|
|
session,
|
|
issuer="https://auth.itworx.test/application/o/itworx-mcp-hub/",
|
|
subject="6f865098-6d67-4ee6-b0fe-b0173cc74e23",
|
|
oauth_username="svc-mcp-codex-ludarium",
|
|
name="Codex workstation",
|
|
project_slug="ludarium",
|
|
)
|
|
|
|
|
|
def test_real_machine_client_provisioning_is_idempotent_and_project_bound(database) -> None: # type: ignore[no-untyped-def]
|
|
first = _provision(database)
|
|
second = _provision(database)
|
|
assert first == second
|
|
assert first.endpoint == "https://mcp.example.com/mcp/ludarium/prod"
|
|
with database() as session:
|
|
client = session.get(ClientRow, first.client_id)
|
|
binding = session.get(ClientBindingRow, first.binding_id)
|
|
assert client is not None and binding is not None
|
|
assert client.status == "active"
|
|
assert client.record_metadata["identityProvisioned"] is True
|
|
assert binding.enabled is True
|
|
|
|
|
|
def test_real_machine_client_fails_closed_on_ambiguous_route(database) -> None: # type: ignore[no-untyped-def]
|
|
with database() as session, session.begin():
|
|
project = session.query(ProjectRow).filter_by(slug="ludarium").one()
|
|
environment = session.query(EnvironmentRow).filter_by(name="prod").one()
|
|
session.add(
|
|
VirtualServerRow(
|
|
workspace_id=project.workspace_id,
|
|
project_id=project.id,
|
|
environment_id=environment.id,
|
|
slug="ludarium-prod-other",
|
|
desired_hash="b" * 64,
|
|
enabled=True,
|
|
gateway_external_id="b" * 32,
|
|
)
|
|
)
|
|
with pytest.raises(ValueError, match="not uniquely determined"):
|
|
_provision(database)
|
|
|
|
|
|
def test_revoked_machine_client_cannot_be_silently_reactivated(database) -> None: # type: ignore[no-untyped-def]
|
|
result = _provision(database)
|
|
with database() as session, session.begin():
|
|
client = session.get(ClientRow, result.client_id)
|
|
assert client is not None
|
|
client.status = "revoked"
|
|
with pytest.raises(ValueError, match="cannot be reprovisioned"):
|
|
_provision(database)
|
|
|
|
|
|
@pytest.mark.parametrize("profile", [*ClientKind, None])
|
|
def test_pending_ui_client_is_activated_in_place_with_selected_profile(
|
|
database, profile: ClientKind | None
|
|
) -> None: # type: ignore[no-untyped-def]
|
|
pending_id = uuid4()
|
|
expected_profile = profile or ClientKind.STDIO_BRIDGE
|
|
metadata = {
|
|
"managedBy": "runtime-onboarding",
|
|
"project": "ludarium",
|
|
"environment": "prod",
|
|
}
|
|
if profile is not None:
|
|
metadata["kind"] = profile.value
|
|
with database() as session, session.begin():
|
|
workspace = session.query(WorkspaceRow).filter_by(slug="itworx").one()
|
|
project = session.query(ProjectRow).filter_by(slug="ludarium").one()
|
|
server = session.query(VirtualServerRow).filter_by(slug="ludarium-prod-readonly").one()
|
|
session.add(
|
|
ClientRow(
|
|
id=pending_id,
|
|
workspace_id=workspace.id,
|
|
subject=f"pending-{pending_id}",
|
|
issuer="https://auth.itworx.test/application/o/itworx-mcp-hub/",
|
|
status="pending",
|
|
record_metadata=metadata,
|
|
)
|
|
)
|
|
session.flush()
|
|
session.add(
|
|
ClientBindingRow(
|
|
workspace_id=workspace.id,
|
|
project_id=project.id,
|
|
client_id=pending_id,
|
|
virtual_server_id=server.id,
|
|
enabled=False,
|
|
)
|
|
)
|
|
with database() as session, session.begin():
|
|
result = provision_machine_client(
|
|
session,
|
|
issuer="https://auth.itworx.test/application/o/itworx-mcp-hub/",
|
|
subject="b7f5d1aa-f199-4334-b077-f6be0870115f",
|
|
oauth_username="svc-mcp-codex-ludarium",
|
|
name="Codex workstation",
|
|
project_slug="ludarium",
|
|
client_id=pending_id,
|
|
)
|
|
assert result.client_id == pending_id
|
|
with database() as session:
|
|
client = session.get(ClientRow, pending_id)
|
|
binding = session.get(ClientBindingRow, result.binding_id)
|
|
assert client is not None and binding is not None
|
|
assert client.status == "active"
|
|
assert client.subject == "b7f5d1aa-f199-4334-b077-f6be0870115f"
|
|
assert binding.enabled is True
|
|
assert client.record_metadata["kind"] == expected_profile.value
|
|
assert generate_config(PersistentClientService._record(client)).kind is expected_profile
|
|
|
|
|
|
def test_pending_ui_client_rejects_unknown_profile(database) -> None: # type: ignore[no-untyped-def]
|
|
pending_id = uuid4()
|
|
with database() as session, session.begin():
|
|
workspace = session.query(WorkspaceRow).filter_by(slug="itworx").one()
|
|
project = session.query(ProjectRow).filter_by(slug="ludarium").one()
|
|
server = session.query(VirtualServerRow).filter_by(slug="ludarium-prod-readonly").one()
|
|
session.add(
|
|
ClientRow(
|
|
id=pending_id,
|
|
workspace_id=workspace.id,
|
|
subject=f"pending-{pending_id}",
|
|
issuer="https://auth.itworx.test/application/o/itworx-mcp-hub/",
|
|
status="pending",
|
|
record_metadata={
|
|
"managedBy": "runtime-onboarding",
|
|
"project": "ludarium",
|
|
"environment": "prod",
|
|
"kind": "arbitrary-profile",
|
|
},
|
|
)
|
|
)
|
|
session.flush()
|
|
session.add(
|
|
ClientBindingRow(
|
|
workspace_id=workspace.id,
|
|
project_id=project.id,
|
|
client_id=pending_id,
|
|
virtual_server_id=server.id,
|
|
enabled=False,
|
|
)
|
|
)
|
|
with database() as session:
|
|
with pytest.raises(ValueError, match="invalid client kind"):
|
|
with session.begin():
|
|
provision_machine_client(
|
|
session,
|
|
issuer="https://auth.itworx.test/application/o/itworx-mcp-hub/",
|
|
subject="b7f5d1aa-f199-4334-b077-f6be0870115f",
|
|
oauth_username="svc-mcp-codex-ludarium",
|
|
name="Codex workstation",
|
|
project_slug="ludarium",
|
|
client_id=pending_id,
|
|
)
|
|
with database() as session:
|
|
client = session.get(ClientRow, pending_id)
|
|
assert client is not None
|
|
assert client.status == "pending"
|
|
assert client.record_metadata["kind"] == "arbitrary-profile"
|