Skip to content

fix: hardware transfer unit tests write mock transfers into the app's UserDefaults #733

Description

@jvsena42

TransferViewModelHwTests builds a real TransferService, so running the unit suite writes mock transfer records into the app's own UserDefaults. They can never settle, and the wallet is left showing a permanent "TRANSFER IN PROGRESS" banner with an inflated balance.

How it happens

makeViewModel in BitkitTests/TransferViewModelHwTests.swift uses the hardware convenience initializer:

TransferViewModel(hwFunding: funding, hwConnecting: connecting, hwFeeRateProvider: { feeRate }, hwTimeouts: timeouts)

That initializer constructs a real TransferService (Bitkit/ViewModels/TransferViewModel.swift:248), which defaults to TransferStorage.sharedUserDefaults.standard. The test bundle is hosted in the Bitkit app, so .standard is the app's preferences.

Any test that drives a successful mock broadcast reaches fundPaidOrdertransferService.createTransfer(...) and persists a record built from the fixtures: IBtOrder.mock() supplies id: "order123" and clientBalanceSat: 85967 (Bitkit/Extensions/IBtOrder+Mock.swift:12), MockHwFunding.broadcastTxId supplies "txid" (BitkitTests/HwTransferMocks.swift:26).

Why it never clears

order123 is not a real Blocktank order, so resolveChannelId fails on every sync and isSettled stays false forever:

DEBUG: Order order123 not found in Blocktank response - TransferService
DEBUG: Transfer 914A2E05-… has no resolvable channelId
DEBUG: Active transfers: 2
DEBUG: Balances in state: onchain=100000 lightning=41370 toSavings=0 toSpending=171934

ActivityLatest.shouldShowBanner is balanceInTransferToSpending > 0, so the banner pins on. The header total also overstates by the phantom amount — 313 304 shown against 141 370 actually held (2 × 85 967 = 171 934).

Reproduction

  1. Note the wallet's balance and that no transfer banner is showing.
  2. xcodebuild test -project Bitkit.xcodeproj -scheme Bitkit -destination "platform=iOS Simulator,id=<udid>" ONLY_ACTIVE_ARCH=YES -only-testing:BitkitTests/TransferViewModelHwTests
  3. Launch the app. The banner is showing and the balance is inflated.

The persisted records, from Library/Preferences/to.bitkit.plist (key transfers):

{"fundingTxId": "txid", "amountSats": 85967, "lspOrderId": "order123", "isSettled": false, }
{"fundingTxId": "txid", "amountSats": 85967, "lspOrderId": "order123", "isSettled": false, }

Suggested fix

Most of the seam already exists — TransferService.init takes storage: TransferStorage = .shared and TransferStorage.init(suiteName:) already supports an isolated suite. What is missing is a way to reach it from the hardware convenience initializer, which hardcodes the default. Letting the tests pass in a suite-scoped TransferStorage (or an injected TransferService) would keep them off UserDefaults.standard.

Worth checking whether other suites hosted in the app write through singletons the same way.

Workaround

Remove the entries with lspOrderId == "order123" from the transfers key in the app container's to.bitkit.plist while the app is stopped. Note that xcrun simctl spawn <udid> defaults write to.bitkit … does not work — it resolves to a domain outside the app container; the file has to be edited in Containers/Data/Application/<id>/Library/Preferences/.

Notes

Not a regression from any current branch — the convenience initializer and the affected tests are on master. Found while verifying #686, where a test run polluted the wallet this way.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions