From 6bd34bf72ebc67180e5d14b44a66e3ebd908b46b Mon Sep 17 00:00:00 2001 From: owenpearson Date: Mon, 21 Sep 2026 17:49:18 +0100 Subject: [PATCH] UTS: rest/unit housekeeping fixes Removes a leaked absolute developer path, adds missing Test IDs, resolves a duplicate RSC17 test, relocates misfiled Realtime tests, corrects bad header claims (RSN3b/c, RSC7b), and loosens an over-specified RSC18 timing assertion. Closes #532 Co-Authored-By: Claude Opus 4.8 --- .../connection/connectivity_check_test.md | 168 ++++++++++++++++++ uts/rest/unit/channel/annotations.md | 8 + uts/rest/unit/channel/history.md | 2 +- uts/rest/unit/channel/idempotency.md | 2 +- uts/rest/unit/channel/publish.md | 2 +- uts/rest/unit/channels_collection.md | 2 +- uts/rest/unit/encoding/message_encoding.md | 2 +- uts/rest/unit/encoding/msgpack_interop.md | 4 + uts/rest/unit/fallback.md | 161 +---------------- uts/rest/unit/presence/rest_presence.md | 2 +- uts/rest/unit/rest_client.md | 57 +++--- uts/rest/unit/types/paginated_result.md | 2 +- 12 files changed, 214 insertions(+), 198 deletions(-) create mode 100644 uts/realtime/unit/connection/connectivity_check_test.md diff --git a/uts/realtime/unit/connection/connectivity_check_test.md b/uts/realtime/unit/connection/connectivity_check_test.md new file mode 100644 index 000000000..bef0c9697 --- /dev/null +++ b/uts/realtime/unit/connection/connectivity_check_test.md @@ -0,0 +1,168 @@ +# Connectivity Check Tests (REC3) + +Spec points: `REC3`, `REC3a`, `REC3b` + +## Test Type +Unit test with mocked WebSocket client and HTTP client + +## Mock Infrastructure + +See `uts/realtime/unit/helpers/mock_websocket.md` for the full Mock WebSocket Infrastructure specification. +See `uts/rest/unit/helpers/mock_http.md` for Mock HTTP Client specification. + +--- + +## REC3a - Default connectivity check URL + +**Test ID**: `realtime/unit/REC3a/default-connectivity-check-url-0` + +Tests that the default connectivity check URL is `https://internet-up.ably-realtime.com/is-the-internet-up.txt`. + +### Note +The connectivity check URL is used to verify internet connectivity before attempting to connect. + +### Setup +```pseudo +mock_http = MockHttpClient() +# Queue response for connectivity check +mock_http.queue_response_for_url( + "https://internet-up.ably-realtime.com/is-the-internet-up.txt", + 200, + "yes" +) + +client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) +``` + +### Test Steps +```pseudo +# Trigger connectivity check (implementation-specific) +# Some libraries expose this, others do it internally +result = AWAIT client.connection.checkConnectivity() +# OR: observe that connectivity check request was made during connection +``` + +### Assertions +```pseudo +connectivity_requests = mock_http.captured_requests.filter( + r => r.url.path CONTAINS "is-the-internet-up" +) +ASSERT connectivity_requests.length >= 1 +ASSERT connectivity_requests[0].url.toString() == "https://internet-up.ably-realtime.com/is-the-internet-up.txt" + +CLOSE_CLIENT(client) +``` + +--- + +## REC3b - Custom connectivity check URL + +**Test ID**: `realtime/unit/REC3b/custom-connectivity-check-url-0` + +Tests that the `connectivityCheckUrl` option overrides the default. + +### Setup +```pseudo +mock_http = MockHttpClient() +mock_http.queue_response_for_url( + "https://custom.example.com/connectivity", + 200, + "ok" +) + +client = Realtime(options: ClientOptions( + key: "appId.keyId:keySecret", + connectivityCheckUrl: "https://custom.example.com/connectivity" +)) +``` + +### Test Steps +```pseudo +result = AWAIT client.connection.checkConnectivity() +``` + +### Assertions +```pseudo +connectivity_requests = mock_http.captured_requests.filter( + r => r.url.host == "custom.example.com" +) +ASSERT connectivity_requests.length >= 1 +ASSERT connectivity_requests[0].url.toString() == "https://custom.example.com/connectivity" + +# Should NOT request the default URL +default_requests = mock_http.captured_requests.filter( + r => r.url.host == "internet-up.ably-realtime.com" +) +ASSERT default_requests.length == 0 + +CLOSE_CLIENT(client) +``` + +--- + +## REC3 - Connectivity check response validation + +**Test ID**: `realtime/unit/REC3/connectivity-check-validation-0` + +Tests that the connectivity check expects a specific response. + +### Test Cases + +| ID | Response | Expected Result | +|----|----------|-----------------| +| 1 | HTTP 200 with body "yes" | Connected | +| 2 | HTTP 200 with body "no" | Not connected | +| 3 | HTTP 200 with empty body | Not connected | +| 4 | HTTP 404 | Not connected | +| 5 | Network error | Not connected | + +### Setup (Case 1 - Success) +```pseudo +mock_http = MockHttpClient() +mock_http.queue_response_for_url( + "https://internet-up.ably-realtime.com/is-the-internet-up.txt", + 200, + "yes" +) + +client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) +result = AWAIT client.connection.checkConnectivity() + +ASSERT result == true + +CLOSE_CLIENT(client) +``` + +### Setup (Case 2 - Wrong body) +```pseudo +mock_http = MockHttpClient() +mock_http.queue_response_for_url( + "https://internet-up.ably-realtime.com/is-the-internet-up.txt", + 200, + "no" +) + +client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) +result = AWAIT client.connection.checkConnectivity() + +ASSERT result == false + +CLOSE_CLIENT(client) +``` + +### Setup (Case 4 - HTTP error) +```pseudo +mock_http = MockHttpClient() +mock_http.queue_response_for_url( + "https://internet-up.ably-realtime.com/is-the-internet-up.txt", + 404, + "Not Found" +) + +client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) +result = AWAIT client.connection.checkConnectivity() + +ASSERT result == false + +CLOSE_CLIENT(client) +``` diff --git a/uts/rest/unit/channel/annotations.md b/uts/rest/unit/channel/annotations.md index dade657d7..541ca224d 100644 --- a/uts/rest/unit/channel/annotations.md +++ b/uts/rest/unit/channel/annotations.md @@ -279,6 +279,8 @@ ASSERT "id" NOT IN annotation ## RSAN2a — delete sends POST with ANNOTATION_DELETE +**Test ID**: `rest/unit/RSAN2a/delete-post-annotation-delete-0` + **Spec requirement:** RSAN2a — Must be identical to RSAN1 `publish()` except that the `Annotation.action` is set to `ANNOTATION_DELETE`, not `ANNOTATION_CREATE`. Tests that `annotations.delete()` sends a POST with the delete action. @@ -330,6 +332,8 @@ ASSERT annotation["name"] == "like" ## RSAN3b — get sends GET to correct endpoint +**Test ID**: `rest/unit/RSAN3b/get-sends-get-0` + | Spec | Requirement | |------|-------------| | RSAN3b | Sends a GET request to `/channels/{channelName}/messages/{messageSerial}/annotations` | @@ -381,6 +385,8 @@ ASSERT request.url.path == "/channels/" + encode_uri_component(channel_name) + " ## RSAN3c — get returns PaginatedResult of Annotations +**Test ID**: `rest/unit/RSAN3c/get-returns-paginated-result-0` + **Spec requirement:** RSAN3c — Returns a `PaginatedResult` page containing the first page of decoded `Annotation` objects. Tests that the response is parsed into a paginated result of annotations with all fields. @@ -458,6 +464,8 @@ ASSERT ann2.clientId == "user-2" ## RSAN3b — get passes params as querystring +**Test ID**: `rest/unit/RSAN3b/get-params-querystring-1` + **Spec requirement:** RSAN3b — Any `params` are sent in the querystring. Tests that optional params are sent as query parameters. diff --git a/uts/rest/unit/channel/history.md b/uts/rest/unit/channel/history.md index 0e4a1c2e3..d267ebe8d 100644 --- a/uts/rest/unit/channel/history.md +++ b/uts/rest/unit/channel/history.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure defined in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure defined in `uts/rest/unit/rest_client.md`. The mock must support: - Handler-based configuration with `onConnectionAttempt` and `onRequest` callbacks diff --git a/uts/rest/unit/channel/idempotency.md b/uts/rest/unit/channel/idempotency.md index b21a9cfdb..ffa9a9c65 100644 --- a/uts/rest/unit/channel/idempotency.md +++ b/uts/rest/unit/channel/idempotency.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure defined in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure defined in `uts/rest/unit/rest_client.md`. The mock must support: - Handler-based configuration with `onConnectionAttempt` and `onRequest` callbacks diff --git a/uts/rest/unit/channel/publish.md b/uts/rest/unit/channel/publish.md index ca187b302..f593aa0cf 100644 --- a/uts/rest/unit/channel/publish.md +++ b/uts/rest/unit/channel/publish.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure defined in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure defined in `uts/rest/unit/rest_client.md`. The mock must support: - Handler-based configuration with `onConnectionAttempt` and `onRequest` callbacks diff --git a/uts/rest/unit/channels_collection.md b/uts/rest/unit/channels_collection.md index 8c98cb509..b1451fa79 100644 --- a/uts/rest/unit/channels_collection.md +++ b/uts/rest/unit/channels_collection.md @@ -1,6 +1,6 @@ # REST Channels Collection Tests -Spec points: `RSN1`, `RSN2`, `RSN3a`, `RSN3b`, `RSN3c`, `RSN4a`, `RSN4b` +Spec points: `RSN1`, `RSN2`, `RSN3a`, `RSN4a`, `RSN4b` ## Test Type Unit test - no network calls required diff --git a/uts/rest/unit/encoding/message_encoding.md b/uts/rest/unit/encoding/message_encoding.md index 0a2ca2061..b58099f22 100644 --- a/uts/rest/unit/encoding/message_encoding.md +++ b/uts/rest/unit/encoding/message_encoding.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure described in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure described in `uts/rest/unit/rest_client.md`. The mock supports: - Intercepting HTTP requests and capturing details (URL, headers, method, body) diff --git a/uts/rest/unit/encoding/msgpack_interop.md b/uts/rest/unit/encoding/msgpack_interop.md index c56484027..e149dbc20 100644 --- a/uts/rest/unit/encoding/msgpack_interop.md +++ b/uts/rest/unit/encoding/msgpack_interop.md @@ -23,6 +23,8 @@ field in the fixture describes the expected decoded content of that message. ## RSL6a3 - Decode binary-encoded protocol messages using interop fixtures +**Test ID**: `rest/unit/RSL6a3/msgpack-fixtures-decode-0` + **Spec requirement:** A set of tests should exist to ensure that the client library can successfully encode and decode binary encoded protocol messages. @@ -82,6 +84,8 @@ END ## RSL6a3 - Re-encode decoded messages back to msgpack (round-trip) +**Test ID**: `rest/unit/RSL6a3/msgpack-fixtures-round-trip-1` + ### Test: each fixture round-trips through encode/decode ```pseudo FOR EACH fixture IN fixtures: diff --git a/uts/rest/unit/fallback.md b/uts/rest/unit/fallback.md index 2a3f3df08..cbcc13d4b 100644 --- a/uts/rest/unit/fallback.md +++ b/uts/rest/unit/fallback.md @@ -1,6 +1,6 @@ # Host Fallback and Endpoint Configuration Tests -Spec points: `RSC15`, `RSC15a`, `RSC15f`, `RSC15j`, `RSC15l`, `RSC15m`, `REC1`, `REC1a`, `REC1b`, `REC1b1`, `REC1b2`, `REC1b3`, `REC1b4`, `REC1c`, `REC1c1`, `REC1c2`, `REC1d`, `REC1d1`, `REC1d2`, `REC2`, `REC2a`, `REC2a1`, `REC2a2`, `REC2b`, `REC2c`, `REC2c1`, `REC2c2`, `REC2c3`, `REC2c4`, `REC2c5`, `REC2c6`, `REC3`, `REC3a`, `REC3b` +Spec points: `RSC15`, `RSC15a`, `RSC15f`, `RSC15j`, `RSC15l`, `RSC15m`, `REC1`, `REC1a`, `REC1b`, `REC1b1`, `REC1b2`, `REC1b3`, `REC1b4`, `REC1c`, `REC1c1`, `REC1c2`, `REC1d`, `REC1d1`, `REC1d2`, `REC2`, `REC2a`, `REC2a1`, `REC2a2`, `REC2b`, `REC2c`, `REC2c1`, `REC2c2`, `REC2c3`, `REC2c4`, `REC2c5`, `REC2c6` ## Test Type Unit test with mocked HTTP client @@ -1407,162 +1407,3 @@ AWAIT client.time() FAILS WITH error ASSERT mock_http.captured_requests.length == 1 ASSERT mock_http.captured_requests[0].url.host == "custom.realtime.example.com" ``` - ---- - -# REC3 - Connectivity Check URL - -## REC3a - Default connectivity check URL - -**Test ID**: `rest/unit/REC3a/default-connectivity-check-url-0` - -Tests that the default connectivity check URL is `https://internet-up.ably-realtime.com/is-the-internet-up.txt`. - -### Note -This test is primarily relevant for Realtime clients that perform connectivity checks. The connectivity check URL is used to verify internet connectivity before attempting to connect. - -### Setup -```pseudo -mock_http = MockHttpClient() -# Queue response for connectivity check -mock_http.queue_response_for_url( - "https://internet-up.ably-realtime.com/is-the-internet-up.txt", - 200, - "yes" -) - -client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) -``` - -### Test Steps -```pseudo -# Trigger connectivity check (implementation-specific) -# Some libraries expose this, others do it internally -result = AWAIT client.connection.checkConnectivity() -# OR: observe that connectivity check request was made during connection -``` - -### Assertions -```pseudo -connectivity_requests = mock_http.captured_requests.filter( - r => r.url.path CONTAINS "is-the-internet-up" -) -ASSERT connectivity_requests.length >= 1 -ASSERT connectivity_requests[0].url.toString() == "https://internet-up.ably-realtime.com/is-the-internet-up.txt" - -CLOSE_CLIENT(client) -``` - ---- - -## REC3b - Custom connectivity check URL - -**Test ID**: `rest/unit/REC3b/custom-connectivity-check-url-0` - -Tests that the `connectivityCheckUrl` option overrides the default. - -### Setup -```pseudo -mock_http = MockHttpClient() -mock_http.queue_response_for_url( - "https://custom.example.com/connectivity", - 200, - "ok" -) - -client = Realtime(options: ClientOptions( - key: "appId.keyId:keySecret", - connectivityCheckUrl: "https://custom.example.com/connectivity" -)) -``` - -### Test Steps -```pseudo -result = AWAIT client.connection.checkConnectivity() -``` - -### Assertions -```pseudo -connectivity_requests = mock_http.captured_requests.filter( - r => r.url.host == "custom.example.com" -) -ASSERT connectivity_requests.length >= 1 -ASSERT connectivity_requests[0].url.toString() == "https://custom.example.com/connectivity" - -# Should NOT request the default URL -default_requests = mock_http.captured_requests.filter( - r => r.url.host == "internet-up.ably-realtime.com" -) -ASSERT default_requests.length == 0 - -CLOSE_CLIENT(client) -``` - ---- - -## REC3 - Connectivity check response validation - -**Test ID**: `rest/unit/REC3/connectivity-check-validation-0` - -Tests that the connectivity check expects a specific response. - -### Test Cases - -| ID | Response | Expected Result | -|----|----------|-----------------| -| 1 | HTTP 200 with body "yes" | Connected | -| 2 | HTTP 200 with body "no" | Not connected | -| 3 | HTTP 200 with empty body | Not connected | -| 4 | HTTP 404 | Not connected | -| 5 | Network error | Not connected | - -### Setup (Case 1 - Success) -```pseudo -mock_http = MockHttpClient() -mock_http.queue_response_for_url( - "https://internet-up.ably-realtime.com/is-the-internet-up.txt", - 200, - "yes" -) - -client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) -result = AWAIT client.connection.checkConnectivity() - -ASSERT result == true - -CLOSE_CLIENT(client) -``` - -### Setup (Case 2 - Wrong body) -```pseudo -mock_http = MockHttpClient() -mock_http.queue_response_for_url( - "https://internet-up.ably-realtime.com/is-the-internet-up.txt", - 200, - "no" -) - -client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) -result = AWAIT client.connection.checkConnectivity() - -ASSERT result == false - -CLOSE_CLIENT(client) -``` - -### Setup (Case 4 - HTTP error) -```pseudo -mock_http = MockHttpClient() -mock_http.queue_response_for_url( - "https://internet-up.ably-realtime.com/is-the-internet-up.txt", - 404, - "Not Found" -) - -client = Realtime(options: ClientOptions(key: "appId.keyId:keySecret")) -result = AWAIT client.connection.checkConnectivity() - -ASSERT result == false - -CLOSE_CLIENT(client) -``` diff --git a/uts/rest/unit/presence/rest_presence.md b/uts/rest/unit/presence/rest_presence.md index 979e07a50..838726a9f 100644 --- a/uts/rest/unit/presence/rest_presence.md +++ b/uts/rest/unit/presence/rest_presence.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure described in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure described in `uts/rest/unit/rest_client.md`. The mock supports: - Intercepting HTTP requests and capturing details (URL, headers, method, body) diff --git a/uts/rest/unit/rest_client.md b/uts/rest/unit/rest_client.md index 20986fd2b..e97144eda 100644 --- a/uts/rest/unit/rest_client.md +++ b/uts/rest/unit/rest_client.md @@ -1,6 +1,6 @@ # REST Client Tests -Spec points: `RSC5`, `RSC7`, `RSC7b`, `RSC7c`, `RSC7d`, `RSC7e`, `RSC8`, `RSC8a`, `RSC8b`, `RSC8c`, `RSC8d`, `RSC8e`, `RSC13`, `RSC17`, `RSC18` +Spec points: `RSC5`, `RSC7`, `RSC7c`, `RSC7d`, `RSC7e`, `RSC8`, `RSC8a`, `RSC8b`, `RSC8c`, `RSC8d`, `RSC8e`, `RSC13`, `RSC17`, `RSC18` ## Test Type Unit test with mocked HTTP client @@ -481,28 +481,6 @@ ASSERT client.clientId == client.auth.clientId --- -## RSC17 - ClientId Attribute - -**Test ID**: `rest/unit/RSC17/client-id-matches-auth-1` - -**Spec requirement:** When instantiating a `RestClient`, if a `clientId` attribute is set in `ClientOptions`, then the `Auth#clientId` attribute will contain the provided `clientId`. - -### Setup -```pseudo -client = Rest(options: ClientOptions( - key: "appId.keyId:keySecret", - clientId: "explicit-client-id" -)) -``` - -### Assertions -```pseudo -ASSERT client.clientId == "explicit-client-id" -ASSERT client.clientId == client.auth.clientId -``` - ---- - ## RSC18 - TLS configuration **Test ID**: `rest/unit/RSC18/tls-controls-protocol-scheme-0` @@ -547,22 +525,39 @@ FOR EACH test_case IN test_cases: **Test ID**: `rest/unit/RSC18/basic-auth-over-http-rejected-1` -**Spec requirement:** Basic authentication (API key) must be rejected when `tls` is false. Token authentication is permitted over HTTP. Error code 40103. +**Spec requirement:** RSC18 / RSA1 — Any attempt to use Basic Auth (API key) over HTTP without TLS must result in an error, as private keys cannot be submitted over an insecure connection. Token authentication is permitted over HTTP. -Tests that Basic authentication is rejected when TLS is disabled. +Tests that Basic authentication is rejected when TLS is disabled. The spec imposes no timing on when the error surfaces, so the failure is accepted whether it is raised at construction time or when the first request is attempted; the only requirement is that no request reaches the transport carrying the key over plaintext. ### Setup ```pseudo -# No mock needed - should fail before making request +mock_http = MockHttpClient() +mock_http.queue_response(200, { "time": 1234567890000 }) ``` ### Test Steps ```pseudo -Rest(options: ClientOptions( - key: "appId.keyId:keySecret", - tls: false -)) FAILS WITH error -ASSERT error.code == 40103 OR error.message CONTAINS "insecure" OR error.message CONTAINS "TLS" +error = null +TRY: + client = Rest(options: ClientOptions( + key: "appId.keyId:keySecret", + tls: false + )) + # Attempt a request; the client may instead have failed at construction above. + AWAIT client.time() +CATCH e: + error = e +END +``` + +### Assertions +```pseudo +# An error must have surfaced by the time the request would have been sent. +ASSERT error IS NOT null +ASSERT error.message CONTAINS "insecure" OR error.message CONTAINS "TLS" + +# No request carrying the key must have reached the transport over plaintext. +ASSERT mock_http.captured_requests.length == 0 ``` ### Note diff --git a/uts/rest/unit/types/paginated_result.md b/uts/rest/unit/types/paginated_result.md index 2f66c213f..18b756c5d 100644 --- a/uts/rest/unit/types/paginated_result.md +++ b/uts/rest/unit/types/paginated_result.md @@ -7,7 +7,7 @@ Unit test with mocked HTTP client ## Mock HTTP Infrastructure -These tests use the mock HTTP infrastructure described in `/Users/paddy/data/worknew/dev/dart-experiments/uts/rest/unit/rest_client.md`. +These tests use the mock HTTP infrastructure described in `uts/rest/unit/rest_client.md`. The mock supports: - Intercepting HTTP requests and capturing details (URL, headers, method, body)