forked from cleveragents/cleveragents-core
394c488d64
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
123 lines
5.1 KiB
Gherkin
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
|