Files
cleveragents-core/features/server_lifecycle.feature
T
HAL9000 e4224eb8ec fix(a2a): resolve JSON-RPC protocol conformance and code quality issues
- Fix malformed JSON parse error: wrap request.json() in try/except,
  return -32700 Parse error instead of unhandled HTTP 500
- Fix JSON-RPC error responses: add required 'id' field to all error
  responses per JSON-RPC 2.0 Section 5
- Fix HTTP status codes: return HTTP 200 for all JSON-RPC responses
  (error codes expressed in JSON body per JSON-RPC 2.0 over HTTP)
- Fix error message leakage: return generic messages instead of raw
  Pydantic ValidationError internals (CWE-209)
- Add catch-all exception handler returning -32603 Internal error
- Fix Agent Card url field: use base server URL without /a2a suffix;
  interfaces[0].url correctly uses endpoint URL with /a2a suffix
- Fix 0.0.0.0 host: substitute 127.0.0.1 for Agent Card URL
- Fix context typing: replace context: Any with context: Context in
  all step files per project convention
- Fix inline imports: move all imports to top of step files
- Fix _COMMANDS typing: use dict[str, Callable[[], None]] to avoid
  type: ignore suppression in helper files
- Add BDD scenarios for entity-sync and namespace-mgmt skill enumeration
- Update server_lifecycle.feature: correct HTTP status assertions to 200

ISSUES CLOSED: #867
2026-06-11 19:58:52 -04:00

169 lines
7.4 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 an empty 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 "healthy"
Scenario: A2A endpoint returns HTTP 200 with JSON-RPC 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 200
And the response body should contain "-32601"
Scenario: A2A endpoint returns HTTP 200 with JSON-RPC error 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 200
And the response body should contain "-32600"
# -----------------------------------------------------------------------
# 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 an empty 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
# -----------------------------------------------------------------------
# Full start lifecycle (mocked uvicorn)
# -----------------------------------------------------------------------
Scenario: ServerLifecycle start runs uvicorn and marks stopped on exit
Given a ServerLifecycle with mocked uvicorn server run
When I call start on the lifecycle
Then the lifecycle should be started
And the lifecycle should be stopped
And the mocked uvicorn server run should have been called
Scenario: ServerLifecycle start installs signal handlers on main thread
Given a ServerLifecycle with mocked uvicorn and signal handlers
When I call start on the lifecycle
Then SIGTERM and SIGINT handlers should have been installed
Scenario: Signal handler calls request_shutdown with signal name
Given a ServerLifecycle with mocked uvicorn server run
When I call start on the lifecycle
And I invoke the installed SIGTERM handler
Then the mocked server should_exit should be true
# -----------------------------------------------------------------------
# run_server convenience function
# -----------------------------------------------------------------------
Scenario: run_server resolves defaults from Settings
When I call run_server with mocked ServerLifecycle
Then the lifecycle should have been created with Settings defaults
And start should have been called on the lifecycle
Scenario: run_server passes explicit host and port overrides
When I call run_server with host "10.0.0.1" and port 3000 using mocked lifecycle
Then the lifecycle should have been created with host "10.0.0.1" and port 3000
# -----------------------------------------------------------------------
# Signal handler edge case — non-main thread
# -----------------------------------------------------------------------
Scenario: Signal handlers are skipped when not on main thread
Given a ServerLifecycle with mocked uvicorn and signal tracking
When I call _install_signal_handlers from a non-main thread
Then no signal handlers should have been installed