Skip to content

Support running the integration suite against ABLY_LOCAL_SANDBOX_URL - #708

Merged
lmars merged 7 commits into
mainfrom
local-server-testing
Jul 22, 2026
Merged

Support running the integration suite against ABLY_LOCAL_SANDBOX_URL#708
lmars merged 7 commits into
mainfrom
local-server-testing

Conversation

@lmars

@lmars lmars commented Jul 21, 2026

Copy link
Copy Markdown
Member

Part of ongoing work to let the SDK test suite run against Ably-compatible open source servers, not just the cloud sandbox.

When ABLY_LOCAL_SANDBOX_URL is set, internal/ablytest provisions each test's app through a local sandbox's POST /apps (the same code path as the cloud sandbox) instead of the cloud, and routes clients to the isolated server child it returns (its own endpoint/port/TLS). The all-powerful test key is selected by parsing each key's capability for the [*]* wildcard rather than string-matching it, so provisioning is insensitive to the sandbox's JSON formatting.

All of this is inert unless ABLY_LOCAL_SANDBOX_URL is set — the normal cloud-sandbox path is unchanged.

🤖 Generated with Claude Code

lmars and others added 7 commits July 19, 2026 23:58
Each case in this test appended WithTLS(true) to the sandbox options. The
assertions only concern query-time/timestamp handling in Authorize, so the
TLS override is incidental — but because per-test options are merged after
the sandbox defaults, it overrode the transport the harness selected. Against
a plaintext local server that forced HTTPS on the default TLS port instead of
the app's plain port, so the token request stalled and the test timed out.

Dropping it lets the test inherit the harness's transport (TLS against the
cloud sandbox, as before; plain against a local ably-sandbox), and it passes
in both.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…wedged dial

The test's mock dial blocked on an empty dialErr channel whenever the
connection attempted a reconnect the test hadn't pre-fed an outcome for.
A dial blocked there wedges the connection in CONNECTING, where the
DISCONNECTED->SUSPENDED timer does not run, so the connection never became
SUSPENDED and the test hung until the outer timeout — despite exercising a
fully mocked transport that never touches the server.

Make the mock sticky: reuse the most recently injected dial outcome when the
channel is empty instead of blocking, mirroring a real dial (which always
returns within its timeout). The connection now retries freely, reaches
SUSPENDED on the shortened TTL, and the test passes in ~1s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TestRealtimeConn_RTN22a_RTN15h2_/RTN22_RTC8_Integration_ServerInitiatedAuth and
the RTC8a4 JWT-reauth subtest each constructed their Realtime client directly
with a bare WithEndpoint(ablytest.Endpoint) and no port/TLS override, discarding
the Sandbox returned by NewREST. That bypasses the harness's transport, so
against a plaintext local server the client dials the default TLS port, never
connects, and the test's unbounded Connect wait hangs until the outer timeout —
a harness artifact, not a server result (cf. ably-server TASK-78).

Build these clients from app.Options(...) like the rest of the suite so they use
the provisioned app's endpoint/port/TLS. Against the cloud sandbox this is the
same transport as before; against a local ably-sandbox the three tests now
connect and exercise the real behaviour — RTN22/RTN22a and RTC8a4 pass, and the
one genuine gap they surface (RTC8a's channel detach on capability downgrade)
is now a real failure rather than an artefact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TestAuth_JWT_Token_RSA8c and TestIdempotent_retry built their secondary request
paths from a bare WithEndpoint(app.Endpoint)/WithEndpoint(ablytest.Endpoint)
with no port/TLS — the JWT-auth REST clients directly, and the idempotent-retry
proxy's target URL via ApplyOptionsWithDefaults(nopts...). Since a per-test
local app's endpoint/port/TLS live only in app.Options(), those requests went to
the endpoint name's default host rather than the provisioned child, which 404s
the app id ("No application found with id ..."). With the cloud sandbox the
endpoint name resolved to the right host so it happened to work.

Build these from app.Options(...) so the JWT clients and the proxy's default URL
use the provisioned app's transport. TestIdempotent_retry now passes; the JWT
test's four connect paths pass, and its remaining failure is a genuine server
divergence (the local server returns 40101 rather than 40144 for an invalid
JWT), not a harness artifact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ostFallback

TestRest_rememberHostFallback (RSC15f) had the same defaultURL-from-nopts bug as
TestIdempotent_retry: the client used app.Options() but the proxy's forward
target was resolved from nopts alone, so against a per-test local child the
"success host" request went to the wrong server and 404'd the app id. Resolve it
from app.Options(nopts...) too. The test now passes against the local server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Across the integration suite, many tests did `x, err := call(); assert.NoError
(t, err)` and then immediately dereferenced or indexed x. assert.NoError only
records the failure and lets the test continue, so when call() returned
(nil, err) — as the client correctly does against an unimplemented or failing
server endpoint — execution fell through to a nil dereference or out-of-range
index, surfacing as a crash (or a panic-in-teardown hang) rather than a clean
FAIL. Convert those sites to require.NoError so the test stops at the failed
check. No passing test changes behaviour — require only differs from assert when
err is non-nil, where continuing was never valid anyway.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When ABLY_LOCAL_SANDBOX_URL is set, internal/ablytest provisions each
test's app through a local sandbox's POST /apps (the same code path as the
cloud sandbox) instead of the cloud, and routes clients to the isolated
ably-server child it returns (its own endpoint/port/TLS). Every test gets a
fresh app, so tests don't contend over shared server state and can run
concurrently. The all-powerful test key is selected by parsing each key's
capability for the "[*]*" wildcard rather than string-matching it, so
provisioning is insensitive to the local sandbox's JSON formatting. The
cloud path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The changes add local sandbox routing support, derive integration-test client options from provisioned apps, improve reconnect test dialing, and replace selected non-fatal assertions with fail-fast requirements across authentication, REST, realtime, pagination, and protocol tests.

Changes

Sandbox routing and integration test updates

Layer / File(s) Summary
Local sandbox provisioning and routing
internal/ablytest/ablytest.go, internal/ablytest/sandbox.go
Local sandbox environment configuration, endpoint routing, URL generation, and wildcard capability parsing are added.
App-derived authentication and transport setup
ably/auth_integration_test.go, ably/realtime_conn_integration_test.go, ably/realtime_conn_spec_integration_test.go, ably/rest_channel_integration_test.go, ably/rest_client_integration_test.go
Tests use provisioned app options for client construction and proxy URLs, while reconnect dialing reuses the latest injected result without blocking.
Fail-fast integration assertions
ably/*integration_test.go
Selected setup, request, parsing, decoding, and result assertions now use require to stop execution after failures.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • ably/ably-go#679: Related endpoint and app-option routing changes in sandbox integration tests.
  • ably/ably-go#705: Related wildcard capability selection and sandbox routing updates.

Suggested reviewers: simonwoolf

Poem

I’m a rabbit who routes through the sandbox bright,
With local app options set just right.
Failed checks now stop with a thump,
Reconnect dials no longer jump.
JWTs and channels hop in line—
Integration tests now align!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: enabling the integration suite to run against ABLY_LOCAL_SANDBOX_URL.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch local-server-testing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ably/rest_client_integration_test.go (1)

350-350: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Hardcoded cloud sandbox host breaks local sandbox testing.

When running the test suite against a local sandbox (via ABLY_LOCAL_SANDBOX_URL), the initial requested host (hosts[0]) will be the provisioned local endpoint (e.g., localhost) rather than the default sandbox.realtime.ably-nonprod.net. The hardcoded assertions will cause these tests to fail.

Derive the expected host dynamically to support both environments, similar to the fix applied in TestRest_rememberHostFallback.

  • ably/rest_client_integration_test.go#L350-L350: Replace the hardcoded host string with a dynamic extraction:
    expectedPrimaryHost, _ := url.Parse(ably.ApplyOptionsWithDefaults(app.Options(options...)...).RestURL())
    assert.Equal(t, expectedPrimaryHost.Hostname(), hosts[0]) // primary host
  • ably/rest_client_integration_test.go#L396-L396: Apply the same dynamic host calculation and assertion here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ably/rest_client_integration_test.go` at line 350, Replace the hardcoded
primary-host assertions at ably/rest_client_integration_test.go:350-350 and
ably/rest_client_integration_test.go:396-396 with an expected hostname derived
from ApplyOptionsWithDefaults(...).RestURL(), then compare its Hostname() to
hosts[0] so both local and cloud sandbox environments are supported.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@ably/rest_client_integration_test.go`:
- Line 350: Replace the hardcoded primary-host assertions at
ably/rest_client_integration_test.go:350-350 and
ably/rest_client_integration_test.go:396-396 with an expected hostname derived
from ApplyOptionsWithDefaults(...).RestURL(), then compare its Hostname() to
hosts[0] so both local and cloud sandbox environments are supported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58fedcec-ec93-4b91-9b9e-aaba95743c4f

📥 Commits

Reviewing files that changed from the base of the PR and between a023b51 and ecb6c87.

📒 Files selected for processing (13)
  • ably/auth_integration_test.go
  • ably/http_paginated_response_integration_test.go
  • ably/proto_message_integration_test.go
  • ably/realtime_channel_integration_test.go
  • ably/realtime_channel_spec_integration_test.go
  • ably/realtime_client_integration_test.go
  • ably/realtime_conn_integration_test.go
  • ably/realtime_conn_spec_integration_test.go
  • ably/rest_channel_integration_test.go
  • ably/rest_channel_spec_integration_test.go
  • ably/rest_client_integration_test.go
  • internal/ablytest/ablytest.go
  • internal/ablytest/sandbox.go

@lmars
lmars requested a review from owenpearson July 22, 2026 10:00
@lmars
lmars merged commit 26cb171 into main Jul 22, 2026
8 checks passed
@lmars
lmars deleted the local-server-testing branch July 22, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants