Files
cleveragents-core/features/server_lifecycle.feature
freemo 5f0a545113 feat(server): ASGI endpoint via uvicorn
Implement FastAPI-based ASGI application served by uvicorn for
the CleverAgents server mode. Add health check endpoint, A2A
JSON-RPC routing, configurable host:port binding, and graceful
shutdown handling. Server launches via `agents server start`.

- Add FastAPI ASGI app factory at infrastructure/server/asgi_app.py
  with /.well-known/agent.json (Agent Card), /health (liveness),
  and /a2a (A2A JSON-RPC 2.0 dispatch via A2aLocalFacade)
- Add ServerLifecycle class at infrastructure/server/server_lifecycle.py
  wrapping uvicorn.Server with SIGTERM/SIGINT graceful shutdown
- Add `agents server start` CLI command with --host, --port, --log-level
  options, resolving defaults from Settings
- Add fastapi>=0.115.0 dependency to pyproject.toml (uvicorn already present)
- Add Behave BDD tests (features/server_lifecycle.feature, 20 scenarios)
- Add Robot Framework integration tests (robot/server_lifecycle.robot)
- Update CHANGELOG.md and vulture_whitelist.py

ISSUES CLOSED: #862
2026-03-22 23:50:04 +00:00

123 lines
5.1 KiB
Gherkin

@phase2 @a2a @server
Feature: ASGI Server Lifecycle
As a platform operator
I want to start, health-check, and shut down the CleverAgents ASGI server
So that the A2A JSON-RPC 2.0 endpoint is available for client communication
# -----------------------------------------------------------------------
# ASGI application creation
# -----------------------------------------------------------------------
Scenario: create_asgi_app returns a FastAPI application
When I create an ASGI application with default settings
Then the ASGI app should be a FastAPI instance
Scenario: create_asgi_app accepts a custom facade
Given an A2aLocalFacade with no services
When I create an ASGI application with that facade
Then the ASGI app should be a FastAPI instance
Scenario: create_asgi_app rejects invalid facade type
When I try to create an ASGI application with facade "not-a-facade"
Then a TypeError should be raised for invalid facade
Scenario: create_asgi_app rejects invalid port
When I try to create an ASGI application with port 0
Then a ValueError should be raised for invalid port
Scenario: create_asgi_app rejects invalid host
When I try to create an ASGI application with host ""
Then a ValueError should be raised for invalid host
# -----------------------------------------------------------------------
# Health check endpoint
# -----------------------------------------------------------------------
Scenario: Health endpoint returns healthy status
Given a running ASGI test client
When I request GET /health
Then the response status code should be 200
And the response body should contain "healthy"
# -----------------------------------------------------------------------
# Agent Card discovery endpoint
# -----------------------------------------------------------------------
Scenario: Agent Card endpoint returns valid agent card
Given a running ASGI test client
When I request GET /.well-known/agent.json
Then the response status code should be 200
And the response body should contain "CleverAgents"
And the response body should contain "url"
# -----------------------------------------------------------------------
# A2A JSON-RPC endpoint
# -----------------------------------------------------------------------
Scenario: A2A endpoint dispatches health check operation
Given a running ASGI test client
When I POST a JSON-RPC request to /a2a with operation "_cleveragents/health/check"
Then the response status code should be 200
And the A2A response status should be "ok"
Scenario: A2A endpoint returns error for unknown operation
Given a running ASGI test client
When I POST a JSON-RPC request to /a2a with operation "nonexistent.operation"
Then the response status code should be 404
Scenario: A2A endpoint returns 400 for malformed request
Given a running ASGI test client
When I POST a malformed JSON body to /a2a
Then the response status code should be 400
# -----------------------------------------------------------------------
# ServerLifecycle construction
# -----------------------------------------------------------------------
Scenario: ServerLifecycle accepts valid configuration
When I create a ServerLifecycle with host "127.0.0.1" and port 9000
Then the lifecycle host should be "127.0.0.1"
And the lifecycle port should be 9000
And the lifecycle should not be started
And the lifecycle should not be stopped
Scenario: ServerLifecycle rejects empty host
When I try to create a ServerLifecycle with host ""
Then a ValueError should be raised for invalid host
Scenario: ServerLifecycle rejects invalid port 0
When I try to create a ServerLifecycle with port 0
Then a ValueError should be raised for invalid port
Scenario: ServerLifecycle rejects port above 65535
When I try to create a ServerLifecycle with port 70000
Then a ValueError should be raised for invalid port
Scenario: ServerLifecycle rejects empty log_level
When I try to create a ServerLifecycle with empty log_level
Then a ValueError should be raised for invalid log_level
# -----------------------------------------------------------------------
# Server configuration from Settings
# -----------------------------------------------------------------------
Scenario: Settings provides default server host
Then the default server host from Settings should be "0.0.0.0"
Scenario: Settings provides default server port
Then the default server port from Settings should be 8080
# -----------------------------------------------------------------------
# Graceful shutdown
# -----------------------------------------------------------------------
Scenario: ServerLifecycle request_shutdown sets flag
Given a ServerLifecycle with a mock uvicorn server
When I call request_shutdown on the lifecycle
Then the mock server should_exit flag should be true
Scenario: ServerLifecycle cannot be started twice
Given a ServerLifecycle that has already been started
When I try to start the lifecycle again
Then a RuntimeError should be raised for double start