Skip to content

[AIT-1106] fix(uts): correct objects proxy specs and harden proxy rule/timeout guidance#501

Merged
sacOO7 merged 4 commits into
uts-liveobjectsfrom
fix/uts-proxy-liveobjects-spec
Jul 8, 2026
Merged

[AIT-1106] fix(uts): correct objects proxy specs and harden proxy rule/timeout guidance#501
sacOO7 merged 4 commits into
uts-liveobjectsfrom
fix/uts-proxy-liveobjects-spec

Conversation

@sacOO7

@sacOO7 sacOO7 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Intent

While evaluating the derived ably-java test suite (ObjectsFaultsTest) against the real sandbox through uts-proxy, every test in objects/integration/proxy/objects_faults.md failed. Root-causing those failures surfaced one genuine spec error (RTO20e), several spec/tooling format mismatches that fail silently or with opaque errors, and a recurring translation trap around timeouts. This PR fixes the specs at source and adds the guidance so other deriving SDKs don't rediscover these the hard way.

Every behavioural claim below was cross-validated against the features spec (objects-features.md), ably-js source (the reference SDK), and a passing ably-java derived test run against the sandbox.

Changes

1. objects/integration/proxy/objects_faults.md — RTO20e spec error (the substantive fix)

The test objects/proxy/RTO20e/publish-fails-on-channel-failed-0 mutated after the channel was already FAILED, yet asserted error 92008:

  • RTO26b requires a mutation on an already-DETACHED/FAILED/SUSPENDED channel to fail with 90001 "before doing anything else" — it never reaches publishAndApply.
  • RTO20e1's 92008 applies only to an operation already waiting for SYNCED when the channel transitions.
  • ably-js confirms: throwIfInvalidWriteApiConfigurationinvalidStateError() = 90001/400; the only 92008 is the sync-wait rejection in publishAndApply(). Its own test ("publishAndApply rejects when channel state changes during sync wait") never mutates after FAILED. ably-java behaves identically.
  • The test's own title ("enters FAILED during SYNCING") and the sibling unit test (objects/unit/RTO20e1) already describe the correct sequence — the proxy steps were the outlier.

The steps are restructured to the RTO20e1 sequence: force SYNCING via an injected ATTACHED carrying HAS_OBJECTS (flags: 128, RTO4c), mutate while SYNCING, wait for the publish ACK in the proxy log plus a brief real-time yield, inject the channel ERROR, and assert the pending operation fails with 92008 / statusCode 400 / cause 90000. A note explains the RTO26b-vs-RTO20e1 distinction so the inversion doesn't creep back in. The requirement table is split into RTO20e (wait) and RTO20e1 (fail) rows, mirroring the unit spec.

2. Proxy rule format fixes (silent/opaque failure modes)

  • objects_faults.md: two rules wrote "match": { "action": 20 } as a JSON number — uts-proxy rejects this at session creation with HTTP 400 (cannot unmarshal number into Go struct field MatchConfig.match.action of type string). Now "20" (string).
  • connection_resume.md (RTN15g): rule action type "refuse" does not exist (refuse_connection does). Unknown action types hit a silent passthrough in the proxy, so the second ws_connect was never refused and the test premise broke with no error. Corrected.
  • helpers/proxy.md: the Match Conditions section now documents the action string format explicitly, plus the v0.3.0 limitation that action names resolve only up to AUTH (17) — unresolvable names (e.g. "OBJECT_SYNC") silently never match, so objects actions need numeric strings until the proxy's name table is extended.
  • A full regex sweep of uts/objects, uts/realtime and uts/rest (all match types, action types, trigger_action types and match fields validated against the Go proxy's structs) found no further instances.

3. Wall-clock timeout guidance (recurring translation trap)

All WITH timeout / poll_until / WAIT durations in integration specs are wall-clock time, but frameworks that virtualise time by default (e.g. kotlinx-coroutines runTest) fast-forward the clock while the test idles — a naively translated 15s timeout around a real network await fires instantly with a misleading error. Added:

  • docs/writing-derived-tests.md: new section Integration timeouts are wall-clock (beware virtual-time frameworks) — the counterpart to the existing No real timers in unit tests note — with the failure mode and the shared-helper remedy.
  • docs/integration-testing.md (Timeout Strategy) and docs/writing-test-specs.md (State Transitions): short pointers to it.
  • helpers/proxy.md: new convention (Specify token revocation - Auth#revokeTokens method #10).
  • objects_faults.md: the two previously unbounded object.get() awaits now carry WITH timeout: 15 seconds — every await in the file is bounded, and the Common Cleanup covers multi-client tests explicitly.

4. objects/PLAN.md

Summary row updated to the corrected RTO20e scenario ("in-flight publish failing when channel enters FAILED during sync wait").

Validation

  • The corrected objects_faults.md is fully implemented by the ably-java derived suite: all 5 tests pass against the sandbox through uts-proxy, including the restructured RTO20e (92008 with cause 90000 observed end-to-end).
  • A deterministic spec↔test audit reports 5/5 Test ID coverage with no missing assertions.
  • Test IDs are unchanged, so existing derived-test UTS: tags remain valid.

Follow-ups (not in this PR)

  • ably/uts-proxy: extend actionNames with OBJECT/OBJECT_SYNC/ANNOTATION so name-based matching works for objects actions.
  • Consider moving realtime/integration/helpers/proxy.md to uts/docs/ — it is cross-module infrastructure consumed by realtime, rest and objects; best done as a dedicated rename commit with a reference sweep.
  • The RTO20e proxy Test ID (publish-fails-on-channel-failed-0) now under-describes the scenario; kept stable for traceability, but a coordinated rename could accompany review feedback.

🤖 Generated with Claude Code

@github-actions github-actions Bot temporarily deployed to staging/pull/501 July 4, 2026 07:57 Inactive
…uidance

Fixes found while evaluating the derived ably-java ObjectsFaultsTest against
the sandbox through uts-proxy:

- objects_faults.md: restructure the RTO20e test to the RTO20e1 sequence
  (mutation in flight while SYNCING, then channel enters FAILED -> 92008).
  The previous steps mutated after FAILED, which per RTO26b fails with 90001
  and never reaches publishAndApply - confirmed against objects-features.md,
  ably-js source and both ably-js/ably-java behaviour.
- objects_faults.md: rule match.action values must be strings ("20"), not
  JSON numbers (uts-proxy rejects numbers with HTTP 400); bound every await
  with a timeout; extend the action-number table (ACK, ERROR); align the
  Common Cleanup with multi-client tests.
- connection_resume.md: RTN15g rule used invalid action type "refuse"
  (silently passes through) - corrected to "refuse_connection".
- helpers/proxy.md: document the match.action string format, the v0.3.0
  action-name resolution limit (names beyond AUTH need numeric strings), and
  add a wall-clock timeout convention.
- docs: add wall-clock timeout guidance for integration specs
  (writing-derived-tests.md new section, integration-testing.md and
  writing-test-specs.md pointers) - virtual-time test frameworks (e.g.
  kotlinx-coroutines runTest) fire spec timeouts instantly unless waits run
  on the real clock.
- PLAN.md: update the objects_faults.md summary row to the corrected RTO20e
  scenario.
@sacOO7 sacOO7 force-pushed the fix/uts-proxy-liveobjects-spec branch from c077c25 to 233d3a3 Compare July 4, 2026 08:12
@github-actions github-actions Bot temporarily deployed to staging/pull/501 July 4, 2026 08:12 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/501 July 4, 2026 08:14 Inactive
proxy.md is cross-module infrastructure documentation, not a realtime helper:
it is consumed by the realtime (7 proxy specs), rest (rest_fallback.md) and
objects (objects_faults.md) modules plus both process docs, unlike the
genuinely module-local helper specs (mock_websocket.md, mock_http.md,
standard_test_pool.md) which are each used only by their own module. Its old
location forced cross-module reach-ins and implied it was realtime-specific.

- git mv uts/realtime/integration/helpers/proxy.md -> uts/docs/proxy.md,
  leaving a one-line stub at the old path so external deep links keep
  resolving
- update all 19 references across 12 files, normalising the pre-existing
  stale `uts/test/` prefix carried by the realtime proxy specs' references
  and replacing two mentions of the long-gone in-repo `uts/test/proxy/`
  directory with a link to the ably/uts-proxy repo
- update the directory-tree diagrams in uts/README.md,
  docs/integration-testing.md and docs/writing-test-specs.md
- adjust proxy.md's own cross-reference to sibling style
  (writing-derived-tests.md)

No content changes to the spec itself beyond that one internal reference.

Note: ~80 unit/proxy spec files still carry stale `uts/test/...` legacy paths
unrelated to proxy.md — left for a separate cleanup PR.
@sacOO7 sacOO7 force-pushed the fix/uts-proxy-liveobjects-spec branch from ab649f3 to 3dbee55 Compare July 4, 2026 14:11
@github-actions github-actions Bot temporarily deployed to staging/pull/501 July 4, 2026 14:12 Inactive
sacOO7 added a commit to ably/ably-java that referenced this pull request Jul 4, 2026
…rrected spec

Proxy session creation failed with HTTP 400 for any rule using a frame-action
match: the uts-proxy's MatchConfig.match.action is a Go string (action name or
numeric string), but the rule builders serialised messageAction as a JSON
number. wsFrameToClientRule/wsFrameToServerRule now send it as a string.

Add withRealTimeout to the shared infra: inside runTest, a bare withTimeout
measures virtual (kotlinx.coroutines.test) time, which fast-forwards while the
test idles - a timeout wrapping a real network await fires instantly. All
real-network waits in integration tests must run on a real-thread dispatcher,
matching the existing awaitState/awaitChannelState/pollUntil helpers.

Rework ObjectsFaultsTest against the corrected source spec
(objects/integration/proxy/objects_faults.md, ably/specification#501):

- bound every object.get() with withRealTimeout instead of virtual-time
  withTimeout (all five tests previously failed instantly)
- RTO7/RTO8: fetch the channel handle once and reuse it - re-calling
  Channels.get() with mode options on an attached channel throws
- RTO20e: exercise the RTO20e1 sequence (mutation in flight while SYNCING,
  then the channel enters FAILED -> 92008/400 with the injected 90000 channel
  error as cause), mirroring ably-js's sync-wait rejection test; the spec's
  original mutate-after-FAILED steps hit the RTO26b precondition (90001)
  instead and were fixed at source
- implement the spec's Common Cleanup: close clients and await CLOSED (with
  the connection-state guard) before tearing down the proxy session

Update proxy spec path references (ObjectsFaultsTest/AuthReauthTest KDocs,
uts-to-kotlin skill) to uts/docs/proxy.md per the relocation in
ably/specification#501; uts/README.md's GitHub URLs are left until that PR
reaches main. See PROXY_UTS_SPEC_FIX.md for the full evidence chain.

All five ObjectsFaultsTest tests pass against the sandbox through uts-proxy.
@sacOO7 sacOO7 requested review from Copilot and ttypic July 6, 2026 09:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the UTS proxy-based integration specs and supporting documentation to (1) correct the Objects proxy fault scenario for RTO20e/RTO20e1, (2) harden proxy rule format guidance (notably match.action string typing and action-name limitations), and (3) clarify that integration/proxy timeouts are wall-clock to avoid virtual-time framework pitfalls in derived SDK tests.

Changes:

  • Corrects objects_faults.md to test the RTO20e1 “in-flight operation fails during sync-wait” scenario (92008) rather than mutating after FAILED (which should be 90001 per write preconditions).
  • Fixes/clarifies uts-proxy rule formats and action typing, including correcting an invalid refuse action type and documenting match.action requirements/limitations.
  • Adds explicit wall-clock timeout guidance across docs and bounds previously unbounded awaits in the Objects proxy spec.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
uts/rest/integration/proxy/rest_fallback.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/rest_faults.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/presence_reentry.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/heartbeat.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/connection_resume.md Updates proxy reference; fixes proxy rule action type to refuse_connection.
uts/realtime/integration/proxy/connection_open_failures.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/channel_faults.md Updates proxy infrastructure reference path.
uts/realtime/integration/proxy/auth_reauth.md Updates proxy reference and points to external uts-proxy repo.
uts/README.md Updates tree/docs pointers to the cross-module proxy spec location.
uts/objects/PLAN.md Updates summary row to reflect corrected RTO20e scenario wording.
uts/objects/integration/proxy/objects_faults.md Corrects RTO20e/RTO20e1 test flow; fixes match.action typing; adds wall-clock waits/timeouts and cleanup guidance.
uts/docs/writing-test-specs.md Updates proxy doc references; clarifies integration timeout semantics.
uts/docs/writing-derived-tests.md Adds wall-clock timeout guidance for derived integration/proxy tests (virtual-time trap).
uts/docs/proxy.md Documents match.action as string (and name-resolution limits); adds wall-clock timeout convention.
uts/docs/integration-testing.md Updates proxy doc reference; clarifies timeout strategy as wall-clock.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread uts/rest/integration/proxy/rest_fallback.md
Comment thread uts/realtime/integration/proxy/rest_faults.md
Comment thread uts/realtime/integration/proxy/presence_reentry.md
Comment thread uts/realtime/integration/proxy/heartbeat.md
Comment thread uts/realtime/integration/proxy/connection_open_failures.md
Comment thread uts/realtime/integration/proxy/channel_faults.md
Comment thread uts/realtime/integration/proxy/auth_reauth.md
Comment thread uts/realtime/integration/proxy/connection_resume.md
Comment thread uts/objects/integration/proxy/objects_faults.md
@sacOO7 sacOO7 changed the title fix(uts): correct objects proxy specs and harden proxy rule/timeout guidance [AIT-1106] fix(uts): correct objects proxy specs and harden proxy rule/timeout guidance Jul 6, 2026
@paddybyers

Copy link
Copy Markdown
Member

@sacOO7 can you also check please whether or not these changes require a change in ably/ably-js#2219 ?

@sacOO7

sacOO7 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

@sacOO7 can you also check please whether or not these changes require a change in ably/ably-js#2219 ?

There was exact same recorded deviation for proxy tests. Also, integration tests were only running in json mode.
Fixed at -> ably/ably-js#2256

sacOO7 added a commit to ably/ably-js that referenced this pull request Jul 6, 2026
…son+msgpack variants

- objects_faults: restructure the RTO20e proxy test to the corrected spec
  sequence (ably/specification#501) — force SYNCING via injected ATTACHED
  with HAS_OBJECTS, mutate while SYNCING, wait for the publish ACK in the
  proxy log, then inject channel ERROR and assert the pending operation
  fails with 92008 / statusCode 400 / cause 90000 (RTO20e1). Removes the
  90001 DEVIATION, which documented the spec error now fixed at source.
- objects_gc: replace the deviation comments with the real spec references —
  undefined reads are RTLM5d2h-compliant (spec says undefined/null), and the
  timer-based GC sweep is covered at the unit tier (RTO10/RTO10b1/RTLM19
  unit tests), so only observable tombstone semantics belong here.
- objects_sync: align the RTO4 subscribe-only test title with the corrected
  spec wording and drop its DEVIATION comment.
- Add describeWithProtocols helper and run objects_lifecycle, objects_sync
  and objects_gc once per protocol variant (json, msgpack) as their specs
  require, without any command-line or environment parameter.

All suites verified against the nonprod sandbox: objects_faults 5/5,
lifecycle/sync/gc 24/24 across both protocol variants.
@github-actions github-actions Bot temporarily deployed to staging/pull/501 July 6, 2026 17:52 Inactive

@ttypic ttypic 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.

LGTM

@sacOO7 sacOO7 merged commit f64479c into uts-liveobjects Jul 8, 2026
2 checks passed
@sacOO7 sacOO7 deleted the fix/uts-proxy-liveobjects-spec branch July 8, 2026 12:43
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.

4 participants