Files
cleveragents-core/robot/helper_langgraph_platform_integration.py
HAL9000 093d993611 style(server): fix ruff format violations in langgraph platform step definitions and robot helper
Applied ruff format auto-fix to resolve line-length formatting violations in:
- features/steps/langgraph_platform_remote_graph_steps.py
- robot/helper_langgraph_platform_integration.py

These files had multi-line assert statements that ruff format collapses to
single lines when they fit within the line length limit.

ISSUES CLOSED: #693
2026-06-06 13:54:43 -04:00

404 lines
13 KiB
Python

"""Helper script for langgraph_platform_integration.robot tests.
Each subcommand is a self-contained check that prints a sentinel on success.
Validates the LangGraph Platform RemoteGraph integration per ADR-048.
"""
from __future__ import annotations
import logging
import sys
from collections.abc import Callable
from pathlib import Path
# Keep helper stderr focused on assertion failures and tracebacks.
logging.disable(logging.CRITICAL)
# Ensure local source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from cleveragents.infrastructure.database.postgresql import ( # noqa: E402
PostgreSQLConnectionConfig,
build_postgresql_url,
is_postgresql_url,
)
from cleveragents.langgraph.remote_graph import ( # noqa: E402
RemoteGraphConfig,
RemoteGraphManager,
RemoteGraphNotAvailableError,
)
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def manager_not_configured() -> None:
"""Verify RemoteGraphManager reports unavailable when no platform URL given."""
manager = RemoteGraphManager()
assert not manager.is_available, (
"Expected manager.is_available=False when no platform URL given"
)
assert manager.platform_url is None, (
f"Expected platform_url=None, got {manager.platform_url!r}"
)
# All operations should raise RemoteGraphNotAvailableError
config = RemoteGraphConfig(
graph_id="test-actor",
platform_url="https://langgraph.example.com",
)
try:
manager.register_graph(config)
raise AssertionError(
"Expected RemoteGraphNotAvailableError from register_graph"
)
except RemoteGraphNotAvailableError:
pass
try:
manager.list_graphs()
raise AssertionError("Expected RemoteGraphNotAvailableError from list_graphs")
except RemoteGraphNotAvailableError:
pass
try:
manager.invoke("test-actor", {})
raise AssertionError("Expected RemoteGraphNotAvailableError from invoke")
except RemoteGraphNotAvailableError:
pass
# health_check returns False (no exception)
result = manager.health_check()
assert result is False, f"Expected health_check=False, got {result!r}"
print("remote-graph-manager-not-configured-ok")
def manager_configured() -> None:
"""Verify RemoteGraphManager is available when platform URL is given."""
platform_url = "https://langgraph.example.com"
manager = RemoteGraphManager(platform_url=platform_url)
assert manager.is_available, (
"Expected manager.is_available=True when platform URL is given"
)
assert manager.platform_url == platform_url, (
f"Expected platform_url={platform_url!r}, got {manager.platform_url!r}"
)
# Initially no graphs registered
graphs = manager.list_graphs()
assert graphs == [], f"Expected empty graph list, got {graphs!r}"
print("remote-graph-manager-configured-ok")
def graph_registration() -> None:
"""Verify graph registration and listing lifecycle."""
platform_url = "https://langgraph.example.com"
manager = RemoteGraphManager(platform_url=platform_url)
# Register multiple graphs
for graph_id in ("strategy-actor", "execution-actor", "estimation-actor"):
config = RemoteGraphConfig(
graph_id=graph_id,
platform_url=platform_url,
)
manager.register_graph(config)
# Verify all are listed in sorted order
graphs = manager.list_graphs()
expected = sorted(["strategy-actor", "execution-actor", "estimation-actor"])
assert graphs == expected, f"Expected {expected!r}, got {graphs!r}"
# Verify get_graph_config returns correct config
config = manager.get_graph_config("strategy-actor")
assert config.graph_id == "strategy-actor", (
f"Expected graph_id='strategy-actor', got {config.graph_id!r}"
)
assert config.platform_url == platform_url, (
f"Expected platform_url={platform_url!r}, got {config.platform_url!r}"
)
# Verify unregister removes the graph
manager.unregister_graph("estimation-actor")
graphs_after = manager.list_graphs()
assert "estimation-actor" not in graphs_after, (
f"Expected 'estimation-actor' to be removed, but found in {graphs_after!r}"
)
assert len(graphs_after) == 2, (
f"Expected 2 graphs after unregister, got {len(graphs_after)}"
)
# Verify get_graph_config raises for unknown graph
try:
manager.get_graph_config("unknown-actor")
raise AssertionError("Expected KeyError for unknown graph")
except KeyError:
pass
# Verify unregister raises for unknown graph
try:
manager.unregister_graph("ghost-actor")
raise AssertionError("Expected KeyError for unregistered graph")
except KeyError:
pass
print("remote-graph-registration-ok")
def invoke_stub() -> None:
"""Verify invoke raises RemoteGraphNotAvailableError for stub implementation."""
platform_url = "https://langgraph.example.com"
manager = RemoteGraphManager(platform_url=platform_url)
# Register a graph
config = RemoteGraphConfig(
graph_id="strategy-actor",
platform_url=platform_url,
)
manager.register_graph(config)
# Invoke should raise RemoteGraphNotAvailableError (stub)
try:
manager.invoke("strategy-actor", {"messages": []})
raise AssertionError("Expected RemoteGraphNotAvailableError from invoke")
except RemoteGraphNotAvailableError as exc:
assert "strategy-actor" in str(exc.details), (
f"Expected graph_id in error details, got {exc.details!r}"
)
# Invoke unregistered graph should raise KeyError
try:
manager.invoke("missing-actor", {})
raise AssertionError("Expected KeyError for unregistered graph")
except KeyError:
pass
# Invoke with thread_id should also raise RemoteGraphNotAvailableError
try:
manager.invoke("strategy-actor", {}, thread_id="thread-001")
raise AssertionError(
"Expected RemoteGraphNotAvailableError from invoke with thread_id"
)
except RemoteGraphNotAvailableError as exc:
assert exc.details.get("thread_id") == "thread-001", (
f"Expected thread_id in error details, got {exc.details!r}"
)
print("remote-graph-invoke-stub-ok")
def health_check() -> None:
"""Verify health_check returns False for stub implementation."""
# Not configured
manager_unconfigured = RemoteGraphManager()
result = manager_unconfigured.health_check()
assert result is False, (
f"Expected health_check=False for unconfigured manager, got {result!r}"
)
# Configured (stub still returns False)
manager_configured = RemoteGraphManager(
platform_url="https://langgraph.example.com"
)
result = manager_configured.health_check()
assert result is False, (
f"Expected health_check=False for stub configured manager, got {result!r}"
)
print("remote-graph-health-check-ok")
def postgresql_config() -> None:
"""Verify PostgreSQLConnectionConfig validation and URL building."""
# Valid config
config = PostgreSQLConnectionConfig(
host="db.example.com",
database="cleveragents",
username="app",
password="secret",
)
assert config.host == "db.example.com", (
f"Expected host='db.example.com', got {config.host!r}"
)
assert config.database == "cleveragents", (
f"Expected database='cleveragents', got {config.database!r}"
)
assert config.username == "app", f"Expected username='app', got {config.username!r}"
assert config.port == 5432, f"Expected port=5432, got {config.port}"
assert config.ssl_mode == "prefer", (
f"Expected ssl_mode='prefer', got {config.ssl_mode!r}"
)
assert config.pool_size == 5, f"Expected pool_size=5, got {config.pool_size}"
assert config.max_overflow == 10, (
f"Expected max_overflow=10, got {config.max_overflow}"
)
# Async URL
async_url = config.to_url(async_driver=True)
assert async_url.startswith("postgresql+asyncpg://"), (
f"Expected async URL to start with 'postgresql+asyncpg://', got {async_url!r}"
)
assert "db.example.com" in async_url, f"Expected host in URL, got {async_url!r}"
assert "cleveragents" in async_url, f"Expected database in URL, got {async_url!r}"
# Sync URL
sync_url = config.to_url(async_driver=False)
assert sync_url.startswith("postgresql://"), (
f"Expected sync URL to start with 'postgresql://', got {sync_url!r}"
)
# Custom port
config_custom_port = PostgreSQLConnectionConfig(
host="db.example.com",
database="cleveragents",
username="app",
password="secret",
port=5433,
)
assert config_custom_port.port == 5433, (
f"Expected port=5433, got {config_custom_port.port}"
)
# build_postgresql_url helper
url = build_postgresql_url(
host="db.example.com",
database="mydb",
username="user",
password="pass",
)
assert url.startswith("postgresql+asyncpg://"), (
f"Expected async URL from build_postgresql_url, got {url!r}"
)
assert "db.example.com" in url, f"Expected host in URL, got {url!r}"
assert "mydb" in url, f"Expected database in URL, got {url!r}"
# Validation errors
try:
PostgreSQLConnectionConfig(
host="",
database="mydb",
username="user",
password="pass",
)
raise AssertionError("Expected validation error for empty host")
except AssertionError:
raise
except Exception:
pass
try:
PostgreSQLConnectionConfig(
host="db.example.com",
database="mydb",
username="user",
password="pass",
port=0,
)
raise AssertionError("Expected validation error for port=0")
except AssertionError:
raise
except Exception:
pass
try:
PostgreSQLConnectionConfig(
host="db.example.com",
database="mydb",
username="user",
password="pass",
pool_size=0,
)
raise AssertionError("Expected validation error for pool_size=0")
except AssertionError:
raise
except Exception:
pass
print("postgresql-config-ok")
def postgresql_url_detection() -> None:
"""Verify is_postgresql_url correctly identifies PostgreSQL URLs."""
# PostgreSQL URLs
assert is_postgresql_url("postgresql://user:pass@host/db") is True, (
"Expected True for postgresql:// URL"
)
assert is_postgresql_url("postgresql+asyncpg://user:pass@host/db") is True, (
"Expected True for postgresql+asyncpg:// URL"
)
assert is_postgresql_url("POSTGRESQL://user:pass@host/db") is True, (
"Expected True for uppercase POSTGRESQL:// URL"
)
# Non-PostgreSQL URLs
assert is_postgresql_url("sqlite:///path/to/db.sqlite") is False, (
"Expected False for sqlite:// URL"
)
assert is_postgresql_url("mysql://user:pass@host/db") is False, (
"Expected False for mysql:// URL"
)
assert is_postgresql_url("") is False, "Expected False for empty string"
print("postgresql-url-detection-ok")
def module_exports() -> None:
"""Verify RemoteGraph classes are exported from langgraph package."""
from cleveragents.infrastructure.database import build_postgresql_url as bpu
from cleveragents.infrastructure.database import is_postgresql_url as ipgu
from cleveragents.langgraph import RemoteGraphConfig as RGC
from cleveragents.langgraph import RemoteGraphManager as RGM
from cleveragents.langgraph import RemoteGraphNotAvailableError as RGNAE
# Verify classes are importable and functional
manager = RGM()
assert not manager.is_available, "Expected manager.is_available=False"
config = RGC(
graph_id="test",
platform_url="https://langgraph.example.com",
)
assert config.graph_id == "test", (
f"Expected graph_id='test', got {config.graph_id!r}"
)
# Verify error class
err = RGNAE("test error")
assert str(err) == "test error", f"Expected 'test error', got {str(err)!r}"
# Verify database exports
assert callable(bpu), "Expected build_postgresql_url to be callable"
assert callable(ipgu), "Expected is_postgresql_url to be callable"
print("module-exports-ok")
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
_COMMANDS: dict[str, Callable[[], None]] = {
"manager-not-configured": manager_not_configured,
"manager-configured": manager_configured,
"graph-registration": graph_registration,
"invoke-stub": invoke_stub,
"health-check": health_check,
"postgresql-config": postgresql_config,
"postgresql-url-detection": postgresql_url_detection,
"module-exports": module_exports,
}
if __name__ == "__main__":
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
sys.exit(2)
fn = _COMMANDS[sys.argv[1]]
fn()