Files
cleveragents-core/features/server_lifecycle.feature
freemo 394c488d64 feat(a2a): Agent Card discovery endpoint
Implement A2A Agent Card discovery per ADR-047 and issue #867:

- AgentCard Pydantic model (agent_card.py) with nested models for
  skills, capabilities, extensions, security schemes, and interfaces.
- Factory function build_agent_card() enumerates supported operations
  from the facade and maps them to A2A skills (plan-lifecycle,
  registry-crud, context-mgmt, entity-sync, namespace-mgmt,
  health-diagnostics).
- Version negotiation: supportedVersions and version fields from
  A2aVersion constants.
- A2A conformance validation (validate_agent_card_conformance) checks
  required fields, version support, and structural integrity.
- Updated ASGI app to build the Agent Card from the facade model
  instead of a raw dict, with conformance validation at startup.
- Behave BDD tests: 34 scenarios covering model construction, field
  validation, conformance checks, serialization, endpoint responses,
  and skill enumeration from facade operations.
- Robot Framework integration tests: 6 test cases for build, conformance,
  endpoint, serialization, skill enumeration, and version info.
- Updated A2A package exports and CHANGELOG.

ISSUES CLOSED: #867
2026-03-24 00:16:07 +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 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 "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 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