Context
test/integration/ contains exactly one spec today (kyc_sign_flow_test.dart, added in #320). The Tier-1 definition is in docs/testing.md:8 and docs/testing.md:270:
Tier 1 — cubit / widget + SDK-boundary fake. Sign ceremonies via FakeBitboxCredentials; HTTP via MockClient from http/testing. Pre-seed JWT via getAuthToken() → loadSignature() → getAuthResponse().
docs/testing.md:307-310 adds the rule:
Crosses ≥ 2 production layers AND uses the FakeBitboxCredentials boundary. Runs headless, no device. A behaviour change in any of the layers should break it.
The first version of this issue listed 11 candidate flows. Eight of them do not touch the BitBox boundary at all — they don't qualify as Tier 1 under the repo's own definition. This revision keeps only the 3 that do.
How kyc_sign_flow_test.dart actually looks
The cited "reference template" wires FakeBitboxCredentials → Eip712Signer.signRegistration — one static method, one production type. It is not a cross-layer template. Use it as a fake-Bitbox example, not as a Tier-1-layered example. The first cross-layer Tier-1 spec is the work this issue creates.
Scope — true Tier-1 candidates only
Each candidate below was verified against the source. To qualify it must (a) cross ≥ 2 production layers and (b) hit the BitboxCredentials boundary so FakeBitboxCredentials is the right test seam. HTTP is mocked at the boundary per docs/testing.md:106 (MockClient + pre-seeded JWT).
In scope — 3 specs
Out of scope — these are not Tier-1 candidates
Each of the following was in the prior version of this issue. They are cubit/service cross-layer flows that do not touch BitBox — FakeBitboxCredentials is the wrong seam and they belong elsewhere (Tier 0 cross-layer test under test/, or a different ticket entirely).
buy_flow_test.dart — BuyPaymentInfoCubit (lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart) emits BuyPaymentInfoFailure(PaymentInfoError.bitboxDisconnected) but the actual sign is DfxAuthService.signMessage (covered by the auth_sign_flow_test.dart above).
sell_flow_test.dart — same pattern as buy.
restore_wallet_flow_test.dart — RestoreWalletCubit(_walletService, _authService) takes no BitboxCredentials. Pure software-wallet seed restore (bip39.validateMnemonic).
pin_lockout_flow_test.dart — PinAuthCubit uses lockoutDuration from pin_constants.dart. No BitBox.
receive_flow_test.dart — derives address from wallet.currentAccount.primaryAddress. No sign ceremony.
transaction_history_flow_test.dart — API + DB, no BitBox.
support_chat_flow_test.dart — DfxSupportService.sendMessage(ticketUid, message). No attachment-upload code path exists.
kyc_email_step_flow_test.dart — code/email-link only; only KYC registration (above) touches BitBox.
kyc_financial_data_flow_test.dart — KycFinancialDataCubit → KycService.getFinancialData(url); no BitBox.
delete_wallet_flow_test.dart — WalletService.deleteCurrentWallet() at lib/packages/service/wallet_service.dart:275 only calls _repository.deleteWallet(id) + _settingsRepository.removeCurrentWalletId(). Does NOT clear all storages and does NOT stop the BitBox observer.
A separate cross-layer "test/" ticket (without FakeBitboxCredentials) could cover the eight removed flows if there's appetite — but they are not Tier-1.
Acceptance criteria
- Each spec uses
FakeBitboxCredentials at the BitBox boundary
- HTTP via
MockClient from package:http/testing, JWT pre-seeded (docs/testing.md:106-108)
- Storage layer uses Drift in-memory (no platform channels)
- Each spec asserts the happy path and at least three of the four
FakeBitboxBehavior failure modes (cancel, disconnect, timeout, malformed) — success is the happy path
- All specs run inside the existing
Analyze & Test job (today ~7-9 min, timeout-minutes: 30)
- The
.coverage-floor-lines is currently 94; Tier-1 specs exercise lines already covered by Tier 0 (services + cubits already at 100 % activated-surface), so they will not move the scoped line coverage — do not commit a floor bump
Dependencies
Estimated effort
| Spec |
Days |
kyc_registration_flow_test.dart (extend existing signer-only into multi-layer) |
1.0 |
sell_bitbox_flow_test.dart (4 behavior modes + EIP-1559 payload assertion) |
1.0 |
auth_sign_flow_test.dart (lazy-fetch + 401 retry + JWT seeding) |
0.5-1.0 |
Documentation + cross-link from docs/testing.md |
0.25 |
| Total |
~3 engineer-days |
Related
Context
test/integration/contains exactly one spec today (kyc_sign_flow_test.dart, added in #320). The Tier-1 definition is indocs/testing.md:8anddocs/testing.md:270:docs/testing.md:307-310adds the rule:The first version of this issue listed 11 candidate flows. Eight of them do not touch the BitBox boundary at all — they don't qualify as Tier 1 under the repo's own definition. This revision keeps only the 3 that do.
How
kyc_sign_flow_test.dartactually looksThe cited "reference template" wires
FakeBitboxCredentials → Eip712Signer.signRegistration— one static method, one production type. It is not a cross-layer template. Use it as a fake-Bitbox example, not as a Tier-1-layered example. The first cross-layer Tier-1 spec is the work this issue creates.Scope — true Tier-1 candidates only
Each candidate below was verified against the source. To qualify it must (a) cross ≥ 2 production layers and (b) hit the
BitboxCredentialsboundary soFakeBitboxCredentialsis the right test seam. HTTP is mocked at the boundary perdocs/testing.md:106(MockClient+ pre-seeded JWT).In scope — 3 specs
kyc_registration_flow_test.dart— extend the existing signer-only spec into the multi-layer ceremony:KycRegistrationSubmitCubit → RealUnitRegistrationService → DfxAuthService → Eip712Signer → FakeBitboxCredentials. Covers the full registration submission path includingBitboxNotConnectedExceptionbranch andKYC_LEVEL_REQUIREDmapping (already covered by Tier 0 service tests, but not as a multi-layer assertion).sell_bitbox_flow_test.dart—SellBitboxCubit → BitboxCredentials.signToSignature(EVM tx sign withchainId+isEIP1559,lib/screens/sell_bitbox/cubit/sell_bitbox_cubit.dart:193). Covers the full sell-BitBox sign ceremony including the fourFakeBitboxBehaviorfailure modes (cancel,disconnect,timeout,malformed).auth_sign_flow_test.dart—DfxAuthService.signMessagelazy-fetch + 401 retry, driven throughFakeBitboxCredentials. This is the common dependency for buy/sell/KYC flows (every authenticated DFX call walks this path on a cold cache). One cross-layer spec here covers regression for all consumers. Overlap note: at Tier 2 (Tier 2: extend BitBox02 firmware simulator beyondhardware_connect_bitbox#548),EthSignMessageAscii/EthSignMessageBoundaryalready exercise the firmware-side ofsignPersonalMessage. The Tier 1 spec covers the Dart-side multi-layer composition (cubit → service → signer) — distinct from Tier 2's "firmware accepts the payload bytes" assertion. Both layers are intentional.Out of scope — these are not Tier-1 candidates
Each of the following was in the prior version of this issue. They are cubit/service cross-layer flows that do not touch BitBox —
FakeBitboxCredentialsis the wrong seam and they belong elsewhere (Tier 0 cross-layer test undertest/, or a different ticket entirely).—buy_flow_test.dartBuyPaymentInfoCubit(lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart) emitsBuyPaymentInfoFailure(PaymentInfoError.bitboxDisconnected)but the actual sign isDfxAuthService.signMessage(covered by theauth_sign_flow_test.dartabove).— same pattern as buy.sell_flow_test.dart—restore_wallet_flow_test.dartRestoreWalletCubit(_walletService, _authService)takes noBitboxCredentials. Pure software-wallet seed restore (bip39.validateMnemonic).—pin_lockout_flow_test.dartPinAuthCubituseslockoutDurationfrompin_constants.dart. No BitBox.— derives address fromreceive_flow_test.dartwallet.currentAccount.primaryAddress. No sign ceremony.— API + DB, no BitBox.transaction_history_flow_test.dart—support_chat_flow_test.dartDfxSupportService.sendMessage(ticketUid, message). No attachment-upload code path exists.— code/email-link only; only KYC registration (above) touches BitBox.kyc_email_step_flow_test.dart—kyc_financial_data_flow_test.dartKycFinancialDataCubit→KycService.getFinancialData(url); no BitBox.—delete_wallet_flow_test.dartWalletService.deleteCurrentWallet()atlib/packages/service/wallet_service.dart:275only calls_repository.deleteWallet(id)+_settingsRepository.removeCurrentWalletId(). Does NOT clear all storages and does NOT stop the BitBox observer.A separate cross-layer "test/" ticket (without
FakeBitboxCredentials) could cover the eight removed flows if there's appetite — but they are not Tier-1.Acceptance criteria
FakeBitboxCredentialsat the BitBox boundaryMockClientfrompackage:http/testing, JWT pre-seeded (docs/testing.md:106-108)FakeBitboxBehaviorfailure modes (cancel,disconnect,timeout,malformed) —successis the happy pathAnalyze & Testjob (today ~7-9 min,timeout-minutes: 30).coverage-floor-linesis currently94; Tier-1 specs exercise lines already covered by Tier 0 (services + cubits already at 100 % activated-surface), so they will not move the scoped line coverage — do not commit a floor bumpDependencies
hardware_connect_bitbox#548) is intentional, not redundant — different layers, different assertions.Estimated effort
kyc_registration_flow_test.dart(extend existing signer-only into multi-layer)sell_bitbox_flow_test.dart(4 behavior modes + EIP-1559 payload assertion)auth_sign_flow_test.dart(lazy-fetch + 401 retry + JWT seeding)docs/testing.mdRelated
docs/testing.md:270-309— Tier 1 sectiontest/helper/fake_bitbox_credentials.dart— the fake, withFakeBitboxBehaviorenumtest/integration/kyc_sign_flow_test.dart— current spec (signer-only)3bee3c7— Tier-0dfx_auth_servicelazy-fetch + 401 retry coverage (in develop, no separate PR)hardware_connect_bitbox#548 — Tier 2 (firmware-simulator) sibling — different layer for the same auth-sign path