test(bitbox): guard exception surface + lock-in P1 channel-hash ordering#467
Merged
Conversation
Contributor
BitBox02 simulator checkHost: Firmware:
Summary: 15 total · 15 passed · 0 failed |
c25e81c to
5c3f159
Compare
…e-confirm Two cheap test additions that close concrete drift risks in the BitBox integration path: - `BitboxNotConnectedException` now overrides toString() so a leaked raw exception in a snackbar / log surfaces as "BitBox is not connected" instead of the default `Instance of '...'` Dart representation. A new `exception_surface_test` enumerates every typed exception in the app and asserts the rendered string is human-readable, so any future exception missing the override is caught at test time rather than in the UI. - A new test in `connect_bitbox_cubit_test` pins the pairing-channel-hash ordering required by BitBox firmware quirk P1 (hash must reach the UI before the host calls confirmPairing). The current cubit already does the right thing — the test guards against future refactors silently inverting the order, which would defeat the on-device hash comparison.
PR #461 introduced WalletLockedException; the surface-drift test should enumerate every typed exception so a missing toString() on any of them is caught at test time. ApiException's toString() is also worth pinning for the same reason — it's the most-leaked exception in the app.
5c3f159 to
ba53442
Compare
This was referenced May 20, 2026
TaprootFreak
added a commit
that referenced
this pull request
May 20, 2026
…d integration-test conventions (#476) ## Summary Documents three test-style conventions in `CONTRIBUTING.md` so the patterns introduced by #467, #470, and #473 don't decay back to ad-hoc mocks over time. ## Conventions added | # | Rule | Reference example | |---|---|---| | 1 | Service-lifecycle tests use the real class as SUT with `installSimulatedBitboxPlatform` + `fake_async`. No mocking of the service itself. | `test/packages/hardware_wallet/bitbox_service_test.dart` | | 2 | Every typed `Exception` in `lib/` needs a `toString()` override and an entry in the shared surface test. | `test/packages/service/dfx/exceptions/exception_surface_test.dart` | | 3 | Platform-specific code paths need an `integration_test/` counterpart OR an explicit `// @no-integration-test: <reason>` annotation. | (forward-looking — no `integration_test/` yet) | ## Why Conventions only stick if they live in the repo, not in PR descriptions. Without this, a future hire would naturally reach for mocktail mocks of `BitboxService` again, the exception surface test would not get appended to as new exceptions land, and platform-specific code would keep slipping through review. ## Test plan - [x] CONTRIBUTING.md edits visible and well-formatted - [ ] Reviewer agrees the conventions reflect the patterns shipped in #467/#470/#473
TaprootFreak
added a commit
that referenced
this pull request
May 20, 2026
…ariants The exception-surface drift test shipped in #467 enumerated 4 of the 6 typed Exception classes in lib/. RegistrationRequiredException and KycLevelRequiredException (both ApiException subclasses with their own toString() override) were missed, defeating the test's whole-set guarantee. Adds both to the surface list with a minimal constructor invocation. All 6 typed exceptions now have their toString() pinned against "Instance of …" drift.
TaprootFreak
added a commit
that referenced
this pull request
May 20, 2026
…ariants (#478) ## Summary The exception-surface drift test shipped in #467 enumerates only 4 of the 6 typed `Exception` classes that actually exist in `lib/`. Two `ApiException` subclasses — `RegistrationRequiredException` and `KycLevelRequiredException` — both define their own `toString()` override but were not listed in the surface guard. A regression on either would not be caught. Adds both to the list. Test count moves from 4 → 6. Discovered during the independent review of #476 (`docs(contributing)`), which pointed at this test as the canonical example for Convention 2. The convention can't honestly reference an incomplete enumeration. ## Test plan - [x] All 6 typed exceptions enumerated - [x] `flutter analyze` clean - [x] `flutter test` green on the touched file - [ ] CI once marked ready
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.
Summary
Two cheap, plugin-independent test additions that close concrete drift risks identified during the PR #461 review.
1. Exception surface guard
BitboxNotConnectedExceptionhad notoString()override. Any code path that surfaces the raw exception (snackbar fallback, log line,error.toString()) therefore rendered the defaultInstance of '...'Dart string instead of a human-readable message — a real failure mode observed in the BitBox-disconnect flows.toString() => 'BitBox is not connected'.test/packages/service/dfx/exceptions/exception_surface_test.dartenumerates every typed exception in the app (BitboxNotConnectedException,SigningCancelledException) and asserts the rendered string contains noInstance ofand is non-empty. New typed exceptions just need to be appended to the list.2. P1 channel-hash-before-confirm drift test
BitBox firmware quirk P1 (critical, per
DFXswiss/bitbox-testkit): the pairing channel hash must reach the UI before the host callsconfirmPairing()on the device — otherwise the user has no opportunity to verify the hash against the on-device display.ConnectBitboxCubitalready does the right thing today (BitboxCheckHashis emitted before user-drivenconfirmPairing()), but nothing prevents a future refactor from silently inverting the order. The new test inconnect_bitbox_cubit_test.dartpins this ordering with averifyNever(() => service.confirmPairing())assertion at theBitboxCheckHashstate.Out of scope (follow-up PRs)
bitbox_flutterv0.0.5 → v0.0.7 bump (unlocks theinstallSimulatedBitboxPlatformhelper) — separate dependency-bump PRBitboxServicelifecycle suite (re-attach on reconnect, observer behavior, USB-FD release) — depends on the bump aboveTest plan
dart run tool/generate_localization.dartflutter analyzeon touched files — cleanflutter test test/packages/service/dfx/exceptions/exception_surface_test.dart test/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit_test.dart— 11/11 greenflutter testin CI