Push activation: full implementation (RSH2/RSH3/RSH4-5/RSH6/RSH8) with UTS-derived tests - #13
Draft
paddybyers wants to merge 2 commits into
Draft
paddybyers wants to merge 2 commits into
paddybyers wants to merge 2 commits into
Conversation
… UTS-derived tests Implement the activation half of the push spec, fully conformant with the extended spec of ably/specification#513 (including Push#updateToken, the APNs token-variant slots, and the unified PATCH registration sync), with zero deviations. All 75 UTS-derived tests pass (62 unit, 7 integration, 6 proxy), validated against the live sandbox and through uts-proxy fault injection. Public API: - lib/src/push/push_platform.dart: the portable platform primitives — PushKeyValueStorage (getItem/setItem/removeItem), PushDeviceToken (transportType, token, optional apnsTokenType per PDT1-PDT4), PushPlatformConfig (platform, formFactor, storage, requestToken), and the activation callback types (RegisterCallback returning a DeviceRegistrationResult, DeregisterCallback, UpdatedCallback). - Push gains activate({registerCallback, updatedCallback}) (RSH2a), deactivate({deregisterCallback}) (RSH2b) and updateToken(token) (RSH2f); ClientOptions gains pushPlatform; RestClient and RealtimeClient gain Future<LocalDevice> getDevice() (RSH8/RSH8a). Implementation (lib/src/impl/push_activation_impl.dart): - LocalDeviceManager: RSH8 load/generate/persist under the standard ably.push.* keys; UUIDv4 id and base64(sha256(secure random)) secret (RSH3a2b); RSH8a partial-state loading ("to the extent that they exist"); RSH8a1 corrupt-state discard (incl. unknown persisted machine state names falling back to NotActivated); RSH6a raw X-Ably-DeviceToken and RSH6b X-Ably-DeviceSecret device auth; PCP3a/RSH8l2 apnsDeviceTokens slot handling preserving unregistered variants; identity reset on deregistration (RSH3g2a). - ActivationStateMachine: the seven states and ten events, the RSH4 pending-event queue (peek/consume/put-back) with RSH5 sequential processing, coalescing completer lists so concurrent activate/deactivate calls all resolve (RSH3b1a/RSH3c1a/RSH3g1a), the RSH3a2a re-activation validation (61002 clientId check; RSH3d3b PATCH sync carrying the complete recipient), RSH3d2c1 deregistration classification (2xx/401/ 40005 -> Deregistered, otherwise DeregistrationFailed with rollback per RSH3g3b), registrar callbacks, updatedCallback delivery (RSH3e2c/ RSH3e3d), and serialized fire-and-forget persistence. - Admin own-device push auth (RSH1b1/b3/b5, RSH1c3/c4) in the push admin implementations; channel push operations hydrate the LocalDevice on first use (RSH8a) via a loadDevice hook threaded through the channel factories. - RSH8d/RSH8e late identification: AuthImpl.onTokenChanged fires after authorize(), the device clientId is set and persisted, and a GotPushDeviceDetails sync follows when registered. Behavioural change, auth (RSA7e2): supplying clientId alongside an API key no longer forces token auth; basic auth is retained and the client identifies itself with a base64 X-Ably-ClientId header (via a new additionalAuthHeaders hook on AblyHttpClient). Required by the push specs (an identified client's activation makes no token requests). The pre-existing "RSA4b - key + clientId triggers token auth" test asserted the old behaviour but cited a UTS id that actually specifies the authCallback case; it is rewritten to assert RSA7e2. Test infrastructure: MockPushStorage (test/helpers, with docs and self-tests) implementing the UTS mock spec (dump/seed/fault flags/ onOperation); MockHttpClient's jsonBody now falls back to msgpack decoding, since the SDK's default useBinaryProtocol encodes msgpack request bodies. Three integration/proxy tests are skipped pending the sandbox deploy of ably/realtime#8591 (a server bug rejecting registration-update PATCHes for devices whose stored recipient is ablyChannel); unskip once deployed. Suite results: unit 1274 passing; integration + proxy 10 passing + 3 pending-deploy skips; dart analyze clean. Completion matrix updated (RSH 8/8 Full, push types 4/4 Full). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Formats the 10 files from the previous commit that dart format --set-exit-if-changed flagged in CI, and restructures three expect() calls whose reformatting tripped require_trailing_commas. No behavioural change; analyze clean, tests unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the activation half of the push spec — the state machine, LocalDevice, device authentication, and platform primitives — fully conformant with the extended spec of ably/specification#513 (including
Push#updateToken, the APNs token-variant slots, and the unified PATCH registration sync), with zero recorded deviations. Every test is derived from the UTS suite in that PR; the sibling derived suite for ably-js is ably/ably-pubsub-js#2283.Public API
PushPlatformConfigonClientOptions.pushPlatform— the injectable platform primitives:PushKeyValueStorage(getItem/setItem/removeItem, AsyncStorage-shaped),requestToken() → PushDeviceToken {transportType, token, apnsTokenType?}, plusplatform/formFactor. The core stays pure Dart; concrete providers (shared_preferences storage, firebase_messaging/APNs token providers) are a future companion package.Pushgainsactivate({registerCallback, updatedCallback})(RSH2a),deactivate({deregisterCallback})(RSH2b),updateToken(token)(RSH2f — the delivery path for FCMonTokenRefresh/APNs re-registration/ActivityKit tokens, including the token-variant slots per PCP3a/RSH8l2).RestClient/RealtimeClientgainFuture<LocalDevice> getDevice()(RSH8/RSH8a).Implementation highlights (
lib/src/impl/push_activation_impl.dart)Deregistered; otherwise failure + rollback per RSH3g3b), registrar callbacks, andupdatedCallbackdelivery (RSH3e2c/RSH3e3d).X-Ably-DeviceToken/X-Ably-DeviceSecretdevice auth (RSH6a/b), admin own-device auth (RSH1b/RSH1c clauses), identity reset on deregistration, and RSH8d/RSH8e late-clientId wiring via a newAuthImpl.onTokenChangedhook.Supplying
clientIdalongside an API key no longer forces token auth: basic auth is retained and the client identifies via a base64X-Ably-ClientIdheader. This is required by the push specs (an identified client's activation makes no token requests) and matches RSA7e2. The pre-existing test "RSA4b - key + clientId triggers token auth" asserted the old behaviour but cited a UTS id that actually specifies the authCallback case; it is rewritten to assert RSA7e2. Please review this change specifically.Tests
75 UTS-derived tests, each
// UTS:-tagged:MockPushStorageadded totest/helpers(docs + self-tests).Results: unit suite 1274 passing / 0 failing; integration + proxy 10 passing + 3 skipped;
dart analyzeclean. The 3 skips are pending the sandbox deploy of ably/realtime#8591 (a server bug rejecting registration-update PATCHes for stored-ablyChannel-recipient devices, found by this suite) — unskip once deployed.Completion matrix: push notifications 8/8 Full, push types 4/4 Full.
🤖 Generated with Claude Code