fix: [SDK-5279] recreate the push subscription when the fetched user has none - #1750
Conversation
…has none The on-new-session Fetch User self-heal only ran when the response carried a "subscriptions" array. A user whose only subscription was deleted server-side comes back with no "subscriptions" key at all, so the check never fired and the device kept its stale subscription id until reinstall. - Treat an absent "subscriptions" key as an empty list in the self-heal, gated on the response carrying an identity object so a malformed 200 cannot trigger a duplicate create - Let OneSignalUserMocks.setUserManagerInternalUser take a push token - Add UserExecutorTests covering the missing key, a list without this device, a list that still has it, and a response with no identity
|
Ran this through three reviewers on different models (Claude Opus 5, GPT-5.6 Sol, Grok 4.6). The fix is right for the bug it targets, and the ordering details that could have sunk it all check out: the existence check runs after
|
|
Potential issues:
|
Review follow-up on #1750. The gate only guarded against a fetch response without an identity object, which the server does not send, and hydration already trusts the same 200. The self-heal now reads the response the same way: an absent "subscriptions" key means the user has none. - Remove the no-identity test along with the gate - Restore the shared manager's subscription executor in tearDown
Went through these with the follow-up commit (bdd5eb5). Kept the PR to the reported behavior: a fetch either carries a parseable subscriptions array or omits the key, which is what a user with no subscriptions looks like, and hydration trusts the same 200 without checks, so the self-heal now does too. That is why the identity gate came out rather than getting tightened, and why there is no guard for a value that does not parse. Negative tests stay on the id assertion, since the self-heal clears the id before it queues anything and I did not want a production seam for the drain. Executor leak fixed with a tearDown restore. Tests stay at the three cases the bug needs. The {} Create response is #1748's. The currentUser window predates this PR and stays a separate follow-up. |
Thanks for the look. I decided not to guard against either shape here. After going through these I kept the PR to the reported behavior rather than adding guards for response shapes the server does not send. The fetch response has exactly two forms in practice: a parseable subscriptions array, or no key at all when the user has no subscriptions (the case this PR fixes). Hydration already trusts the same 200 with no checks on identity or subscriptions, so guarding only the self-heal would not buy much and hides the fact that a malformed response has bigger problems by then. The follow-up commit (bdd5eb5) removes the weak identity gate rather than tightening it, so the self-heal reads the response the same way hydration does. If you have a real path where the server returns one of those shapes, I'll add the guard and a test. |
Description
One Line Summary
Recreate the push subscription on a new session when the fetched user has no subscriptions. SDK-5279.
Details
Motivation
Deleting a user's only push subscription from the dashboard left the device without push until reinstall. The fetch-user response has no
subscriptionskey when the user has none, and the on-new-session self-heal inOSUserExecutorrequired that key, so it never ran for the case it exists for. Fixes #1745.Scope
subscriptionskey as an empty list, then clears the stale id and sends the Create as it already does when the list omits this device.Testing
Unit testing
Three
UserExecutorTestscases for the on-new-session fetch: nosubscriptionskey, a list without this device, and a list that still has it. The first fails without the fix. The first fails without the fix.OneSignalUserMocks.setUserManagerInternalUsertakes an optional push token so the Create request has a readable mock key.Manual testing
Reproduced on a device before the fix. With the fix, the next launch logs
found this device's push subscription gone, sends the Create, and the IAM fetch that follows uses the new subscription id. Unit tests run on an iPhone 17 Pro simulator.Affected code checklist
Checklist
Overview
Testing
Final pass