Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 6 additions & 7 deletions .claude/skills/uts-to-kotlin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,13 +676,12 @@ Recognise a **proxy** spec by a reference to `create_proxy_session()`, proxy `ru

A **direct-sandbox** spec (no `create_proxy_session`, no rules — just happy-path interop against `nonprod:sandbox`) uses the same `SandboxApp` provisioning and the same `runTest` / `@BeforeAll`+`runBlocking` lifecycle as a proxy test, but **drops all proxy wiring**: no `ProxyManager.ensureProxy()`, no `ProxySession`, no `connectThroughProxy`. The client connects straight to the sandbox host. `ChannelHistoryTest` (realtime) and `ObjectsLifecycleTest` (liveobjects) are the reference examples — read one before translating a direct-sandbox spec.

**Client wiring** — point both transports at the sandbox host (explicit hosts auto-disable fallback hosts, so no `fallbackHosts`):
**Client wiring** — point the client at the sandbox endpoint:

```kotlin
private fun newClient(useBinaryProtocol: Boolean): AblyRealtime = TestRealtimeClient {
key = app.defaultKey
realtimeHost = SandboxApp.sandboxHost // sandbox.realtime.ably-nonprod.net
restHost = SandboxApp.sandboxHost
endpoint = SandboxApp.sandboxEndpoint // nonprod:sandbox
this.useBinaryProtocol = useBinaryProtocol
autoConnect = false
}
Expand Down Expand Up @@ -717,7 +716,7 @@ Import `org.junit.jupiter.params.ParameterizedTest` and `org.junit.jupiter.param
| Pseudocode | Kotlin |
|---|---|
| `AWAIT channel.attach()` | `channel.attach()` then `awaitChannelState(channel, ChannelState.attached, 10.seconds)` |
| `AWAIT channel.publish(name, data)` (await the ack) | wrap the **non-deprecated** `publish(name, data, Callback<PublishResult>)` overload in `suspendCancellableCoroutine` — resume on `onSuccess`, fail on `onError` (the `CompletionListener` overload is deprecated) |
| `AWAIT channel.publish(name, data)` (await the ack) | wrap the `publish(name, data, Callback<PublishResult>)` overload in `suspendCancellableCoroutine` — resume on `onSuccess`, fail on `onError` |
| `poll_until(() => AWAIT channel.history().items.length == N, …)` | `pollUntil(10.seconds, 500.milliseconds) { channel.history(null).items().size == N }` (`history()` is a blocking REST call; `null` = no params) |

Use generous timeouts (10–30s) — real network is involved. Everything else is the shared foundation described at the top of this section; a direct-sandbox test just skips the proxy-only subsections (`ProxySession`, rule factories, the event log).
Expand All @@ -728,7 +727,7 @@ Three helpers live under `uts/src/main/kotlin/io/ably/lib/uts/infra/integration/

- **`ProxyManager`** (`infra/integration/proxy/ProxyManager.kt`, package `io.ably.lib.uts.infra.integration.proxy`) — downloads/starts the shared `uts-proxy` process. Call `ProxyManager.ensureProxy()` once per suite in setup.
- **`ProxySession`** (`infra/integration/proxy/ProxySession.kt`, same package) — one programmable session wrapping the proxy control API; also defines the `connectThroughProxy` extension and the rule-builder helpers.
- **`SandboxApp`** (`infra/integration/SandboxApp.kt`, package `io.ably.lib.uts.infra.integration`) — provisions/deletes a sandbox test app from the shared `test-app-setup.json` in ably-common. `SandboxApp.create()` returns a `SandboxApp` with `appId`, `defaultKey`, and `keys` (`defaultKey` is a full-capability `appId.keyId:keySecret`); `app.delete()` tears it down. Provision in suite setup, delete in teardown. Also owns the single upstream sandbox host constant `SandboxApp.sandboxHost` (`sandbox.realtime.ably-nonprod.net`, the resolved `nonprod:sandbox` endpoint) — the default target of every `ProxySession` (both `realtimeHost` and `restHost`), and what direct-sandbox clients set `realtimeHost` / `restHost` from.
- **`SandboxApp`** (`infra/integration/SandboxApp.kt`, package `io.ably.lib.uts.infra.integration`) — provisions/deletes a sandbox test app from the shared `test-app-setup.json` in ably-common. `SandboxApp.create()` returns a `SandboxApp` with `appId`, `defaultKey`, and `keys` (`defaultKey` is a full-capability `appId.keyId:keySecret`); `app.delete()` tears it down. Provision in suite setup, delete in teardown. Also owns `SandboxApp.sandboxEndpoint` (`nonprod:sandbox`), which direct-sandbox clients set as their `endpoint`, and `SandboxApp.sandboxHost` (`sandbox.realtime.ably-nonprod.net`, the hostname that endpoint resolves to) — the default upstream target of every `ProxySession`.

Import what the tier needs: a **direct-sandbox** test imports `io.ably.lib.uts.infra.integration.SandboxApp` plus `io.ably.lib.uts.infra.unit.TestRealtimeClient` and `io.ably.lib.uts.infra.{awaitState, pollUntil}`; a **proxy** test additionally imports `io.ably.lib.uts.infra.integration.proxy.{ProxyManager, ProxySession, connectThroughProxy}`.

Expand Down Expand Up @@ -799,11 +798,11 @@ val client = TestRealtimeClient {
}
```

ably-java has **no `endpoint` ClientOptions field**; `connectThroughProxy` sets the discrete host fields for you:
`connectThroughProxy` sets the proxy-def options for you:

| Proxy-def option | What `connectThroughProxy` sets |
|---|---|
| `endpoint: "localhost"` | `realtimeHost` **and** `restHost` = `session.proxyHost` (`"localhost"`) |
| `endpoint: "localhost"` | `endpoint = session.proxyHost` (`"localhost"`) |
| `port: proxy_port` | `port = session.proxyPort` |
| `tls: false` | `tls = false` |
| `useBinaryProtocol: false` | already the `ClientOptionsBuilder` default — left untouched |
Expand Down
7 changes: 3 additions & 4 deletions .claude/skills/uts-to-kotlin/references/objects-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,9 @@ REST provisioning" convention survives even though the module's internals are vi
> envelope on the legacy `sandbox-rest.ably.io` host; both were aligned upstream — to this V2 shape and to
> the canonical nonprod sandbox host `sandbox.realtime.ably-nonprod.net` — in ably/specification#497.
>
> **Sandbox host.** `provisionObjectsViaRest` sets `restHost = SandboxApp.sandboxHost`
> (`sandbox.realtime.ably-nonprod.net`) — the same nonprod host `SandboxApp` and the realtime clients use,
> **not** `environment="sandbox"` (which resolves to the legacy `sandbox-rest.ably.io`, and
> can't be combined with `restHost` per `Hosts.java` TO3k2/TO3k3). The REST call hits the live sandbox
> **Sandbox host.** `provisionObjectsViaRest` sets `endpoint = SandboxApp.sandboxEndpoint`
> (`nonprod:sandbox`, resolving to `sandbox.realtime.ably-nonprod.net`) — the same endpoint `SandboxApp`
> and the realtime clients use. The REST call hits the live sandbox
> today, and the realtime client observes the provisioned data through the SDK's OBJECT_SYNC +
> `RealtimeObject.get()` (both implemented).

Expand Down
29 changes: 28 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,31 @@ jobs:
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :java:runUtsUnitTests :uts:runUtsUnitTests
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :core:runUtsUnitTests :uts:runUtsUnitTests

# A second UTS leg through the server door's builders (see the uts.side handling in
# uts/.../ClientFactories.kt): the builders stamp a side-declaring agent entry and pass
# everything else through, so conformance must be identical on both legs; SideModesTest
# fails a leg whose stamp does not match. There is no device leg on the JVM — the device
# door is an Android artifact, covered by the instrumentation tests in emulate.yml.
- run: ./gradlew :uts:runUtsUnitTests -Duts.side=server

# Continuously proves the release pre-flight and that every published module
# builds a publishable artifact set, so version/coordinate regressions surface
# on PRs rather than on release day.
release-dry-run:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
with:
persist-credentials: false
- name: Set up the JDK
uses: actions/setup-java@17f84c3641ba7b8f6deff6309fc4c864478f5d62 # v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew verifyReleaseArtifacts publishToMavenLocal
8 changes: 4 additions & 4 deletions .github/workflows/emulate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
android-api-level: [ 21, 24, 29, 35 ]
android-api-level: [ 24, 29, 35 ]

steps:
- name: checkout
Expand Down Expand Up @@ -50,10 +50,10 @@ jobs:
arch: ${{ steps.get-avd-arch.outputs.arch }}
target: default
# Print emulator logs if tests fail
script: ./gradlew :android:connectedAndroidTest ${{ matrix.android-api-level == 19 && '-PhttpURLConnection' || '' }} || (adb logcat -d System.out:I && exit 1)
script: ./gradlew :core-android:connectedAndroidTest :device:connectedAndroidTest ${{ matrix.android-api-level == 19 && '-PhttpURLConnection' || '' }} || (adb logcat -d System.out:I && exit 1)

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: android-build-reports-${{ matrix.android-api-level }}
path: android/build/reports/
name: core-android-build-reports-${{ matrix.android-api-level }}
path: core-android/build/reports/
22 changes: 13 additions & 9 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRestSuite -PhttpURLConnection
- run: ./gradlew :core:testHttpSuite -PhttpURLConnection

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: java-build-reports-rest
path: java/build/reports/
name: core-build-reports-rest
path: core/build/reports/

check-realtime-httpurlconnection:
runs-on: ubuntu-latest
Expand All @@ -54,13 +54,13 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRealtimeSuite -PhttpURLConnection
- run: ./gradlew :core:testRealtimeSuite -PhttpURLConnection

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: java-build-reports-realtime
path: java/build/reports/
name: core-build-reports-realtime
path: core/build/reports/
check-rest-okhttp:
runs-on: ubuntu-latest
permissions:
Expand All @@ -80,7 +80,7 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRestSuite
- run: ./gradlew :core:testHttpSuite

check-realtime-okhttp:
runs-on: ubuntu-latest
Expand All @@ -101,7 +101,7 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRealtimeSuite
- run: ./gradlew :core:testRealtimeSuite

check-liveobjects:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -143,4 +143,8 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:runUtsIntegrationTests :uts:runUtsIntegrationTests
- run: ./gradlew :core:runUtsIntegrationTests :uts:runUtsIntegrationTests

# A second leg through the server door's builders — see the uts.side handling in
# uts/.../ClientFactories.kt and the matching leg in check.yml.
- run: ./gradlew :uts:runUtsIntegrationTests -Duts.side=server
2 changes: 1 addition & 1 deletion .github/workflows/javadoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ jobs:
- name: Upload Documentation
uses: ably/sdk-upload-action@4e694297f208b72b5a9f6b1248a1556f19f821d6 # v2
with:
sourcePath: java/build/docs/javadoc
sourcePath: core/build/docs/javadoc
Comment thread
ttypic marked this conversation as resolved.
Comment thread
ttypic marked this conversation as resolved.
githubToken: ${{ secrets.GITHUB_TOKEN }}
artifactName: javadoc
6 changes: 6 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

# Fails before anything is uploaded if the artifact set, group or lockstep
# version drifts (core, core-android, device and server release
# together on the same version; partial release must be impossible).
- name: Release pre-flight
Comment thread
ttypic marked this conversation as resolved.
run: ./gradlew verifyReleaseArtifacts

- name: Publish and release to Maven Central
run: ./gradlew publishAndReleaseToMavenCentral
env:
Expand Down
Loading