forked from cleveragents/cleveragents-core
5f7bba3e96
Implement ServerHttpClient with httpx for server communication including: - Health check endpoint (GET /health) - Version negotiation (GET /version, POST /version/negotiate) - Pagination helpers for list endpoints - Per-request timeout and retry policy with exponential backoff - Request/response logging with auth header redaction - TLS verification toggle with warning when disabled - Server error responses mapped to domain errors (A2aNotAvailableError, etc.) - Client-specific exceptions (ServerConnectionError, ServerTimeoutError, ServerVersionMismatchError) - Settings fields: server_base_url, server_api_token, server_tls_verify, server_request_timeout - Factory function create_client_from_settings wired to Settings - httpx added to pyproject.toml dependencies - Behave scenarios (23 scenarios, 72 steps) - Robot Framework smoke tests - ASV benchmark for connection overhead baseline - Reference documentation at docs/reference/server_client_http.md ISSUES CLOSED: #335
176 lines
7.8 KiB
Gherkin
176 lines
7.8 KiB
Gherkin
@phase2 @client @http
|
|
Feature: Server HTTP client
|
|
As a developer
|
|
I want an HTTP client for server communication
|
|
So that I can perform health checks, version negotiation, and paginated queries
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Construction and configuration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Create client with default settings
|
|
Given a ServerHttpClient with default settings
|
|
Then the client base_url should be "http://localhost:8080"
|
|
And the client tls_verify should be true
|
|
And the client timeout should be 30.0
|
|
|
|
Scenario: Create client with custom settings
|
|
Given a ServerHttpClient with base_url "https://example.com" and token "tok_test123abcdefghij"
|
|
Then the client base_url should be "https://example.com"
|
|
And the client tls_verify should be true
|
|
|
|
Scenario: TLS verification disabled logs warning
|
|
Given a ServerHttpClient with tls_verify disabled
|
|
Then the client tls_verify should be false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Health check
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Health check returns true for healthy server
|
|
Given a ServerHttpClient with a mock healthy server
|
|
When I call health_check on the http client
|
|
Then the health check result should be true
|
|
|
|
Scenario: Health check returns false for unreachable server
|
|
Given a ServerHttpClient with an unreachable server
|
|
When I call health_check on the http client
|
|
Then the health check result should be false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Version retrieval
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Get version returns server version string
|
|
Given a ServerHttpClient with a mock version server returning "2.1.0"
|
|
When I call get_version on the http client
|
|
Then the version result should be "2.1.0"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Version negotiation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Negotiate version succeeds with compatible version
|
|
Given a ServerHttpClient with a mock negotiate server returning "1.0"
|
|
When I call negotiate_version on the http client
|
|
Then the http client negotiated version should be "1.0"
|
|
|
|
Scenario: Negotiate version fails when server returns empty
|
|
Given a ServerHttpClient with a mock negotiate server returning empty
|
|
When I call negotiate_version on the http client expecting error
|
|
Then a ServerVersionMismatchError should be raised from http client
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pagination
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: List endpoint parses paginated object response
|
|
Given a ServerHttpClient with a mock paginated endpoint returning 3 items
|
|
When I call list_endpoint with page 1 per_page 10
|
|
Then the page result should have 3 items
|
|
And the page result should have total 3
|
|
|
|
Scenario: List endpoint parses list response
|
|
Given a ServerHttpClient with a mock list endpoint returning 5 items
|
|
When I call list_endpoint with page 1 per_page 10
|
|
Then the page result should have 5 items
|
|
And the page result has_next should be false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Error mapping
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Server 503 raises A2aNotAvailableError
|
|
Given a ServerHttpClient with a mock server returning 503
|
|
When I call get_version and capture the error
|
|
Then an A2aNotAvailableError should be raised from the http client
|
|
|
|
Scenario: Server 502 raises ServerConnectionError
|
|
Given a ServerHttpClient with a mock server returning 502
|
|
When I call get_version and capture the error
|
|
Then a ServerConnectionError should be raised from the http client
|
|
|
|
Scenario: Server 401 raises ServerConnectionError with auth message
|
|
Given a ServerHttpClient with a mock server returning 401
|
|
When I call get_version and capture the error
|
|
Then a ServerConnectionError should be raised from the http client
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Retry logic
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Retries on 500 for idempotent GET
|
|
Given a ServerHttpClient with a mock server that fails twice then succeeds
|
|
When I call get_version on the http client
|
|
Then the version result should be "1.0.0"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Auth header redaction
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Auth headers are redacted in log output
|
|
Given request headers with an Authorization bearer token
|
|
When I redact the headers
|
|
Then the Authorization value should be "***REDACTED***"
|
|
And the Accept header should not be redacted
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Client lifecycle
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Close is a safe no-op
|
|
Given a ServerHttpClient with default settings
|
|
When I call close on the http client
|
|
Then no error should be raised from the http client
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Factory from settings
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: create_client_from_settings raises when URL not configured
|
|
When I call create_client_from_settings without a configured URL
|
|
Then a ValueError should be raised with URL configuration message from factory
|
|
|
|
Scenario: create_client_from_settings creates client when URL is set
|
|
When I call create_client_from_settings with a configured URL
|
|
Then a ServerHttpClient should be returned
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Client exception attributes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: ServerConnectionError carries url and cause attributes
|
|
Given a ServerConnectionError with url "https://x.com" and cause
|
|
Then the error url attribute should be "https://x.com"
|
|
And the error cause attribute should not be None
|
|
|
|
Scenario: ServerTimeoutError carries timeout_seconds attribute
|
|
Given a ServerTimeoutError with 10.0 seconds
|
|
Then the error timeout_seconds attribute should be 10.0
|
|
|
|
Scenario: ServerVersionMismatchError carries version attributes
|
|
Given a ServerVersionMismatchError with client "2.0" and server versions
|
|
Then the error client_version attribute should be "2.0"
|
|
And the error server_versions attribute should contain "1.0"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Backoff helper
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Backoff delay grows exponentially and is capped
|
|
When I compute backoff delay for attempt 0 with base 1.0 and max 10.0
|
|
Then the delay should be 1.0
|
|
When I compute backoff delay for attempt 5 with base 1.0 and max 10.0
|
|
Then the delay should be 10.0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PageResult attributes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: PageResult stores pagination metadata
|
|
Given a PageResult with 2 items page 1 per_page 10 total 20 has_next true
|
|
Then the page result page should be 1
|
|
And the page result per_page should be 10
|
|
And the page result total should be 20
|
|
And the page result has_next should be true
|