Files
cleveragents-core/features/server_app.feature
freemo e13308b322 feat(server): integrate LangGraph Platform with RemoteGraph for server execution
Implement the server application module (ADR-048) with:

- FastAPI application factory (app.py) exposing the A2A JSON-RPC 2.0
  endpoint as the sole client-facing interface. Routes: POST /a2a
  (JSON-RPC dispatch), GET /.well-known/agent.json (Agent Card), and
  GET /health (container orchestration).

- RemoteGraph adapter (remote_graph.py) for invoking actor graphs
  via LangGraph Platform. Each actor graph deploys as a separate
  RemoteGraph enabling independent scaling. Includes health tracking,
  invocation counting, and a RemoteGraphRegistry for managing
  multiple graph deployments.

- Database configuration (database.py) supporting PostgreSQL for
  production and SQLite for development. Config-driven via
  CLEVERAGENTS_DATABASE_URL and CLEVERAGENTS_SERVER_ENV environment
  variables with validated Pydantic settings model.

- Docker deployment (docker/Dockerfile + docker-compose.yml) with
  multi-stage build, non-root user, health checks, and PostgreSQL
  service.

- FastAPI dependency added to pyproject.toml.

- 26 Behave BDD scenarios covering app factory, A2A dispatch, Agent
  Card, health endpoint, RemoteGraph config/adapter/registry, and
  database configuration.

- 10 Robot Framework integration tests with helper script verifying
  end-to-end server functionality.

ISSUES CLOSED: #693
2026-03-12 03:05:00 +00:00

161 lines
6.5 KiB
Gherkin

Feature: Server application with LangGraph Platform integration
As a CleverAgents server operator
I want the server to expose an A2A JSON-RPC 2.0 endpoint
So that clients can communicate via the A2A protocol over HTTP
# ---------------------------------------------------------------
# FastAPI application factory
# ---------------------------------------------------------------
Scenario: create_app returns a FastAPI instance
When I create a server application
Then the app should be a FastAPI instance
Scenario: Server has the A2A endpoint
Given a server application
Then the app should have a POST /a2a route
Scenario: Server has the Agent Card endpoint
Given a server application
Then the app should have a GET /.well-known/agent.json route
Scenario: Server has the health endpoint
Given a server application
Then the app should have a GET /health route
Scenario: Server does not expose OpenAPI docs
Given a server application
Then the app should not expose /docs
And the app should not expose /redoc
And the app should not expose /openapi.json
# ---------------------------------------------------------------
# A2A JSON-RPC dispatch via server endpoint
# ---------------------------------------------------------------
Scenario: A2A endpoint dispatches message/send
Given a server application with a test client
When I POST a JSON-RPC message/send request to /a2a
Then the response should contain a valid JSON-RPC result
And the result should have a task id
Scenario: A2A endpoint returns parse error for invalid JSON
Given a server application with a test client
When I POST invalid JSON to /a2a
Then the response should contain a JSON-RPC parse error
Scenario: A2A endpoint returns method not found for unknown method
Given a server application with a test client
When I POST a JSON-RPC request for method "unknown/method" to /a2a
Then the response should contain a JSON-RPC method not found error
Scenario: A2A endpoint returns 204 for notifications
Given a server application with a test client
When I POST a JSON-RPC notification to /a2a
Then the response status code should be 204
# ---------------------------------------------------------------
# Agent Card
# ---------------------------------------------------------------
Scenario: Agent Card contains server name and capabilities
Given a server application with a test client
When I GET /.well-known/agent.json
Then the Agent Card should have name "CleverAgents Server"
And the Agent Card should have authentication schemes
# ---------------------------------------------------------------
# Health endpoint
# ---------------------------------------------------------------
Scenario: Health endpoint returns status ok
Given a server application with a test client
When I GET /health
Then the health status should be "ok"
And the health response should have graph_count
# ---------------------------------------------------------------
# RemoteGraph adapter
# ---------------------------------------------------------------
Scenario: RemoteGraphConfig validates platform URL
When I create a RemoteGraphConfig with an empty platform URL
Then a ValueError should be raised for platform URL
Scenario: RemoteGraphConfig validates graph name
When I create a RemoteGraphConfig with an empty graph name
Then a ValueError should be raised for graph name
Scenario: RemoteGraphConfig rejects non-HTTP URLs
When I create a RemoteGraphConfig with platform URL "ftp://bad"
Then a ValueError should be raised for platform URL
Scenario: RemoteGraphAdapter stores config
Given a RemoteGraphConfig for "test_graph" at "http://localhost:8123"
When I create a RemoteGraphAdapter with that config
Then the adapter graph_name should be "test_graph"
And the adapter platform_url should be "http://localhost:8123"
Scenario: RemoteGraphAdapter invoke raises without platform
Given a RemoteGraphAdapter for "test_graph"
When I invoke the adapter with sample state
Then a RemoteGraphError should be raised
Scenario: RemoteGraphAdapter health is healthy by default
Given a RemoteGraphAdapter for "test_graph"
Then the adapter health should be "healthy"
Scenario: RemoteGraphAdapter tracks invocation errors
Given a RemoteGraphAdapter for "test_graph"
When I invoke the adapter and catch the error
Then the adapter health should be "unhealthy"
And the adapter last_error should not be None
# ---------------------------------------------------------------
# RemoteGraph registry
# ---------------------------------------------------------------
Scenario: RemoteGraphRegistry registers and retrieves adapters
Given an empty RemoteGraphRegistry
And a RemoteGraphAdapter for "strategy_actor"
When I register the adapter in the graph registry
Then the graph registry should have 1 adapter
And the graph registry should contain "strategy_actor"
Scenario: RemoteGraphRegistry lists graph names
Given a RemoteGraphRegistry with adapters "alpha" and "beta"
Then the registry graph list should be ["alpha", "beta"]
Scenario: RemoteGraphRegistry health summary
Given a RemoteGraphRegistry with adapters "alpha" and "beta"
Then the registry health summary should have keys "alpha" and "beta"
# ---------------------------------------------------------------
# Database configuration
# ---------------------------------------------------------------
Scenario: DatabaseConfig defaults to SQLite
When I create a default DatabaseConfig
Then the database URL should start with "sqlite://"
And is_sqlite should be True
And is_postgres should be False
Scenario: DatabaseConfig accepts PostgreSQL URL
When I create a DatabaseConfig with URL "postgresql://localhost/test"
Then the database URL should start with "postgresql://"
And is_sqlite should be False
And is_postgres should be True
Scenario: DatabaseConfig rejects empty URL
When I create a DatabaseConfig with an empty URL
Then a ValueError should be raised for database URL
Scenario: DatabaseConfig rejects invalid scheme
When I create a DatabaseConfig with URL "mysql://localhost/test"
Then a ValueError should be raised for database URL
Scenario: create_engine_from_config creates an engine for SQLite
Given a SQLite DatabaseConfig
When I create an engine from the config
Then the engine should not be None