@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 result 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" Scenario: A2A endpoint returns -32700 parse error for invalid JSON bytes Given a running ASGI test client When I POST invalid JSON bytes to /a2a Then the response status code should be 200 And the response body should contain "-32700" Scenario: A2A endpoint returns -32603 internal error when dispatch raises Given a running ASGI test client with a failing facade When I POST a JSON-RPC request to /a2a with operation "_cleveragents/health/check" Then the response status code should be 200 And the response body should contain "-32603" # ----------------------------------------------------------------------- # 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 Scenario: ServerLifecycle request_shutdown is a no-op when server not started Given a fresh ServerLifecycle When I call request_shutdown on the lifecycle Then the lifecycle should not be started # ----------------------------------------------------------------------- # 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