forked from cleveragents/cleveragents-core
8e9aa7af48
- Promote _request() to public method request() in ServerHttpClient to fix encapsulation violation across sync_client and remote_project - Fix WebSocket connect() to raise NotImplementedError with clear TODO documenting that real websockets transport is not yet implemented - Fix thread safety: protect last_event_id write under self._lock in ws_client.process_event() - Broaden exception handling in sync() to catch ServerTimeoutError and A2aNotAvailableError in addition to ServerConnectionError - Add has_next to PageResult in benchmark and test helpers - Add comments explaining Retry-After header logging-only behavior and why blocking time.sleep is acceptable in sync client - Update all test steps, robot helpers, and benchmarks to use the renamed public request() method and updated connect() behavior Refs: #335, #336, #337, #338
136 lines
5.7 KiB
Gherkin
136 lines
5.7 KiB
Gherkin
@phase2 @client @websocket
|
|
Feature: WebSocket updates client
|
|
As a developer
|
|
I want a WebSocket client for real-time plan updates
|
|
So that I can receive status, progress, and log events from the server
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Construction
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Create WebSocket client with default settings
|
|
Given a WebSocketClient with default settings
|
|
Then the ws_url should be "ws://localhost:8080/ws"
|
|
And the heartbeat interval should be 30.0
|
|
And the dedup capacity should be 1000
|
|
|
|
Scenario: Create WebSocket client with custom settings
|
|
Given a WebSocketClient with url "wss://example.com/ws" and token "tok_ws1234567890abcdef"
|
|
Then the ws_url should be "wss://example.com/ws"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Connection lifecycle
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Connect raises NotImplementedError until real transport is available
|
|
Given a WebSocketClient with default settings
|
|
When I attempt to connect the ws client
|
|
Then a NotImplementedError should be raised from ws client
|
|
|
|
Scenario: Disconnect sets state to disconnected
|
|
Given a WebSocketClient with default settings
|
|
And the ws client state is set to connected
|
|
When I disconnect the ws client
|
|
Then the ws client should not be connected
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Reconnection
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Reconnect increments reconnect counter
|
|
Given a WebSocketClient with default settings and max_reconnects 5
|
|
And the ws client state is set to connected
|
|
When I simulate a reconnect
|
|
Then the reconnect count should be 1
|
|
And the ws client should be connected
|
|
|
|
Scenario: Reconnect exceeding max raises ServerConnectionError
|
|
Given a WebSocketClient with default settings and max_reconnects 2
|
|
When I exhaust reconnect attempts
|
|
Then a ServerConnectionError should be raised from ws client
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Event processing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Process event dispatches to subscriber
|
|
Given a WebSocketClient with a subscriber
|
|
When I process a plan status event with id "evt-001"
|
|
Then the subscriber should receive event "evt-001"
|
|
And the last event id should be "evt-001"
|
|
|
|
Scenario: Process duplicate event is skipped
|
|
Given a WebSocketClient with a subscriber
|
|
When I process a plan status event with id "evt-dup-001"
|
|
And I process a plan status event with id "evt-dup-001" again
|
|
Then the subscriber should have received 1 event
|
|
|
|
Scenario: Unsubscribe removes callback
|
|
Given a WebSocketClient with a subscriber
|
|
When I unsubscribe the callback
|
|
And I process a plan status event with id "evt-unsub"
|
|
Then the subscriber should have received 0 events
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Heartbeat
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Heartbeat resets reconnect counter
|
|
Given a WebSocketClient with default settings and max_reconnects 5
|
|
And the ws client state is set to connected
|
|
When I simulate a reconnect
|
|
And I handle a heartbeat
|
|
Then the reconnect count should be 0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Version negotiation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Version negotiation stores agreed version
|
|
Given a WebSocketClient with default settings
|
|
When I negotiate ws version "1.0"
|
|
Then the negotiated ws version should be "1.0"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# EventDeduplicator
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: Deduplicator detects duplicates
|
|
Given an EventDeduplicator with capacity 3
|
|
When I check event "a" for duplicate
|
|
Then the dedup result should be not duplicate
|
|
When I check event "a" for duplicate
|
|
Then the dedup result should be duplicate
|
|
|
|
Scenario: Deduplicator evicts oldest when full
|
|
Given an EventDeduplicator with capacity 2
|
|
When I add events "x" and "y" and "z" to the deduplicator
|
|
Then event "x" should not be tracked
|
|
And the deduplicator size should be 2
|
|
|
|
Scenario: Deduplicator clear removes all entries
|
|
Given an EventDeduplicator with capacity 10
|
|
When I add events "a" and "b" and "c" to the deduplicator
|
|
And I clear the deduplicator
|
|
Then the deduplicator size should be 0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ConnectionState
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: ConnectionState reset clears all fields
|
|
Given a ConnectionState with connected true and last_event_id "ev1"
|
|
When I reset the connection state
|
|
Then the connection state should be disconnected
|
|
And the connection state last_event_id should be empty
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Backoff helper
|
|
# ---------------------------------------------------------------------------
|
|
|
|
Scenario: WS backoff delay grows exponentially and is capped
|
|
When I compute ws backoff delay for attempt 0 base 1.0 max 30.0
|
|
Then the ws delay should be 1.0
|
|
When I compute ws backoff delay for attempt 10 base 1.0 max 30.0
|
|
Then the ws delay should be 30.0
|