Skip to content

Stabilize TransactionManagerTest timing and threading#428

Merged
ianrumac merged 2 commits into
developfrom
stabilize-transaction-manager-test
Jul 3, 2026
Merged

Stabilize TransactionManagerTest timing and threading#428
ianrumac merged 2 commits into
developfrom
stabilize-transaction-manager-test

Conversation

@claude

@claude claude Bot commented Jul 3, 2026

Copy link
Copy Markdown

Requested by Ian Rumac · Slack thread

Changes in this pull request

  • Migrate TransactionManagerTest to the stable test timing/threading approach already used elsewhere in the repo, replacing IOScope(this.coroutineContext) / IOScope(this@createBillingWrapper.coroutineContext) with IOScope(backgroundScope.coroutineContext).

Before / After

Before: TransactionManagerTest flaked on CI with Instrumentation run failed due to Process crashed / emulator console failures (notably test_purchase_failed_without_alert). The TransactionManager under test was given its coroutine scope via IOScope(this.coroutineContext), i.e. a child of the test's own runTest job. Fire-and-forget coroutines that TransactionManager launches on that scope (transaction tracking, receipt work, etc.) were therefore awaited by runTest and were not cleanly torn down. If one of them was still running or threw after the test body finished, runTest could hang until the 5-minute timeout or the SuperwallScope exception handler would rethrow on the emulator, crashing the instrumentation process.

After: The test now injects IOScope(backgroundScope.coroutineContext). backgroundScope shares the same TestCoroutineScheduler/dispatcher, so advanceUntilIdle() still drives the SDK's coroutines deterministically, but work launched there is not awaited by runTest and is automatically cancelled when the test body completes. This removes the leaked/never-completing coroutine that caused the hangs and process crashes. This mirrors the pattern the team standardized on in WebPaywallRedeemerTest (IOScope(backgroundScope.coroutineContext), introduced in the "Fix flaky test" change) and ConfigManagerInstrumentedTest (backgroundScope as the injected ioScope).

How

TransactionManager's ioScope parameter is already an injectable seam, so no production change was required. The only edits are in the test file: the two call sites that built an IOScope from the raw TestScope context (manager(...) at the constructor's ioScope = ..., and createBillingWrapper(...)) now build it from backgroundScope.coroutineContext. Every test's assertions and intent are unchanged; only the coroutine-scope wiring was touched. No test coverage was removed.

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs.
  • I added/updated tests or detailed why my change isn't tested. (This change is test-only.)
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes. (N/A — internal test stabilization, no user-facing change.)
  • I have run ktlint in the main directory and fixed any issues.
  • I have updated the SDK documentation as well as the online docs. (N/A — test-only change.)
  • I have reviewed the contributing guide

Note: I could not run the instrumentation tests, unit tests, or ktlint here — there is no Android emulator or Gradle toolchain available in this environment. The change should be validated on CI.


Generated by Claude Code

claude added 2 commits July 3, 2026 13:41
The Transaction.State.Fail event is the only transaction event emitted
from a fire-and-forget ioScope.launchWithTracking{} coroutine (Start,
Abandon and Pending track inline within the awaited purchase() chain).
The test_purchase_failed_* tests sampled events.value after
advanceUntilIdle(), racing that detached emission and intermittently
missing the Fail event. Await it via events.first { predicate },
mirroring ObserverModeTest's deterministic flow-await idiom.
@ianrumac ianrumac marked this pull request as ready for review July 3, 2026 15:52
@ianrumac ianrumac merged commit abcaa7f into develop Jul 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants