Skip to content

test(screens): cover screen cubits/blocs (+37 tests)#327

Merged
TaprootFreak merged 1 commit into
developfrom
test/screen-cubits
May 15, 2026
Merged

test(screens): cover screen cubits/blocs (+37 tests)#327
TaprootFreak merged 1 commit into
developfrom
test/screen-cubits

Conversation

@TaprootFreak
Copy link
Copy Markdown
Contributor

Summary

Stage 4 of the coverage push. Adds bloc_test specs for five screen-level cubits/blocs that previously only had widget-level coverage. All run in pure Dart (no widget pumping); mocktail for the service / repository / SoftwareWallet boundaries.

Cubit Test file Cases
`legal_disclaimer_cubit.dart` `test/screens/legal/cubit/legal_disclaimer_cubit_test.dart` 7
`restore_wallet/cubit/validate_seed/validate_seed_cubit.dart` `test/screens/restore_wallet/cubit/validate_seed_cubit_test.dart` 7
`transaction_history/cubits/filter/transaction_history_filter_cubit.dart` `test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart` 6
`verify_seed/cubit/verify_seed_cubit.dart` `test/screens/verify_seed/cubit/verify_seed_cubit_test.dart` 6
`pin/bloc/setup_pin/setup_pin_cubit.dart` `test/screens/pin/setup_pin_cubit_test.dart` 11

What each file covers

  • legal_disclaimer_cubit: initial step-0 state, nextStep advance, full walk to last step, onComplete callback fires on last step (no emit), no-op without callback on last step, previousStep moves back, no-op at step 0.
  • validate_seed_cubit: initial uncomplete; `checkSeedLength` for 12 valid words / fewer-than-12 / 12 words with an out-of-wordlist token / extra inner whitespace tolerated; `validateSeed` delegates to `WalletService` for both branches.
  • transaction_history_filter_cubit: subscribes to the repo stream with the configured asset + address, default 1-year-back `startDate`, stream pushes populate `all` + `filtered`, date-window filter narrows `filtered` without touching `all`, boundaries are inclusive, filter re-applies on subsequent stream emissions.
  • verify_seed_cubit: 4 random ascending word indices within seed length, debug-mode pre-fill, `canVerify` reflects all four slots filled, `updateWord` trims + lowercases + clears `hasError`, `verify` returns true and marks the wallet current on match, `verify` returns false / flags `hasError` / does NOT mark current on mismatch.
  • setup_pin_cubit: initial state, `addDigit` appends / 6-digit cap, `deleteDigit` / no-op on empty, create→confirm transition on 6th digit, matching confirm-pin persists salt + hash and emits `isComplete` (exercises real PBKDF2 via `compute()`), mismatching confirm-pin resets + flags `mismatch`, `reset` returns to initial, `isBiometricAvailable` + `enableBiometrics` passthrough to `BiometricService`.

Notes

  • The `setup_pin_cubit` matching-pin test runs a real 600k-iteration PBKDF2 through `compute()`. On the Flutter-test isolate shim this takes ~12 s on a Mac Studio. The test uses a 30 s timeout to keep CI honest without flaking.
  • `SecureStorage.setPinSalt` takes a `Uint8List`, which is a restricted type — a `Fake` subclass is illegal, so the mocktail fallback is registered with a real `Uint8List(0)` instance instead.

Excluded (and why)

Test plan

  • `flutter analyze test/screens/` clean
  • `flutter test` on all 5 new files — 37 / 37 passing (~8 s total locally)
  • CI green

Adds bloc_test specs for five screen-level cubits/blocs that previously
only had widget-level coverage.

- legal_disclaimer_cubit (7): initial state, nextStep advance,
  full walk to last step, onComplete callback on last step, no-op
  without callback, previousStep, no-op at step 0
- validate_seed_cubit (7): initial state, checkSeedLength (12 valid /
  short / unknown word / extra whitespace), validateSeed (valid /
  invalid)
- transaction_history_filter_cubit (6): repo stream subscription,
  default 1-year window, stream-driven population, date-window filter,
  inclusive boundaries, filter re-application on subsequent emissions
- verify_seed_cubit (6): random ascending word indices, debug-mode
  pre-fill, canVerify, updateWord trims/lowercases + clears error,
  verify success marks current wallet, verify failure flags error
- setup_pin_cubit (11): initial state, addDigit append / 6-digit cap,
  deleteDigit / no-op on empty, create→confirm transition, matching
  pin persists salt + hash and emits isComplete (real PBKDF2 on
  compute-isolate shim), mismatching pin resets + flags mismatch,
  reset, isBiometricAvailable + enableBiometrics passthrough

The matching-pin test exercises a real 600k-iteration PBKDF2 hash via
`compute()`; on the Flutter-test isolate shim it takes ~12s. Generous
timeout in the test reflects this.
@TaprootFreak TaprootFreak marked this pull request as ready for review May 15, 2026 08:24
@TaprootFreak TaprootFreak merged commit 53f66b0 into develop May 15, 2026
1 check passed
@TaprootFreak TaprootFreak deleted the test/screen-cubits branch May 15, 2026 08:24
TaprootFreak added a commit that referenced this pull request May 15, 2026
## Summary
Stage 5 of the coverage push. Adds 32 unit tests for the PIN unlock
cubit and three previously-untested DFX backend services. mocktail mocks
for AppStore + repository boundaries; \`http.testing.MockClient\` for
the HTTP surface.

| File under test | Test file | Cases |
| --- | --- | --- |
| \`lib/screens/pin/bloc/verify_pin/verify_pin_cubit.dart\` |
\`test/screens/pin/verify_pin_cubit_test.dart\` | 18 |
| \`lib/packages/service/dfx/dfx_country_service.dart\` |
\`test/packages/service/dfx/dfx_country_service_test.dart\` | 5 |
| \`lib/packages/service/dfx/dfx_blockchain_api_service.dart\` |
\`test/packages/service/dfx/dfx_blockchain_api_service_test.dart\` | 5 |
| \`lib/packages/service/dfx/dfx_faucet_service.dart\` |
\`test/packages/service/dfx/dfx_faucet_service_test.dart\` | 4 |

## What each file covers
- **verify_pin_cubit:** initial state; \`addDigit\` / \`deleteDigit\`
including no-ops at boundaries + while \`VerifyPinTemporarilyLocked\` /
\`VerifyPinLocked\`; correct pin resets lockout and emits
\`VerifyPinSuccess\` (real 600k-iter PBKDF2 via \`compute()\`);
wrong-pin first-attempt path; \`enableLockout: false\` never persists
attempts; 5th wrong attempt triggers a 1-minute temporary lockout;
reaching \`permanentLockoutThreshold\` emits \`VerifyPinLocked\` and
skips the temporary-lockout write; \`onLockExpired\` preserves
\`failedAttempts\`; \`checkBiometricAvailability\` for the in-window /
expired / threshold / success / fail / unavailable branches.
- **dfx_country_service:** list mapping from the DTO + cache (single
fetch even on repeated reads), non-200 throws, case-insensitive symbol
lookup, unknown-symbol error, public \`cachedCountries\` field is
populated post-fetch.
- **dfx_blockchain_api_service:** POST shape (address + Bearer JWT +
chain name + asset id), testnet flips \`blockchain\` to \`'Sepolia'\`,
empty balances list returns \`0.0\`, 201 acceptance in addition to 200,
\`ApiException\` on non-2xx with the JSON body.
- **dfx_faucet_service:** POST + Bearer JWT to \`/v1/faucet\`, 201
acceptance, \`ApiException\` on non-2xx, **documents** the current
behaviour of sending the literal string \`'Bearer null'\` when no auth
token is set — useful as a regression marker if/when the service grows a
guard.

## Notes
- The verify-pin happy-path test exercises a real PBKDF2 (600k
iterations) via \`compute()\`. On the Flutter-test isolate shim this
takes a few seconds; the test uses a 30 s timeout, same approach as in
PR #327's setup_pin test.
- \`MockClient\` lets us stub \`AppStore.httpClient\` without giving the
service its own real \`http.Client\`; the production code is unchanged.

## Excluded (and why)
- \`dfx_kyc_service\`, \`real_unit_registration_service\` — overlap with
the KYC cubits that #319 already covers; want to avoid a stack on top of
that work mid-review.
- \`real_unit_account_service\`, \`transaction_history_service\` — hit
\`appStore.wallet.currentAccount.primaryAddress.address.hexEip55\`,
which needs a real \`SoftwareWallet\` plumbed through the mock. Doable
but more setup than the surface justifies; will be covered in a
follow-up alongside hook tests.
- \`dfx_brokerbot_service\`, \`real_unit_sell_payment_info_service\` —
possibly touched by #321 (dashboard buy/sell auth). Hold to avoid
review-time conflicts.
- \`biometric_service\`, \`price_service\` — same constraints documented
in #326.

## Test plan
- [x] \`flutter analyze\` on the four new files — clean
- [x] \`flutter test\` — 32 / 32 passing locally
- [ ] CI green
TaprootFreak added a commit that referenced this pull request May 15, 2026
…21 tests) (#331)

## Summary
Stage 8 of the coverage push. Adds 21 unit tests across five
previously-untested cubits.

| Cubit under test | Test file | Cases |
| --- | --- | --- |
| \`create_wallet/bloc/create_wallet_cubit.dart\` |
\`test/screens/create_wallet/create_wallet_cubit_test.dart\` | 4 |
| \`restore_wallet/cubit/restore_wallet/restore_wallet_cubit.dart\` |
\`test/screens/restore_wallet/restore_wallet_cubit_test.dart\` | 3 |
| \`debug_auth/cubit/debug_auth_cubit.dart\` |
\`test/screens/debug_auth/debug_auth_cubit_test.dart\` | 7 |
| \`dashboard/bloc/balance_cubit.dart\` |
\`test/screens/dashboard/balance_cubit_test.dart\` | 4 |
| \`dashboard/bloc/pending_transactions_cubit.dart\` |
\`test/screens/dashboard/pending_transactions_cubit_test.dart\` | 3 |

## What each file covers
- **create_wallet_cubit:** initial hides the seed + has no wallet,
\`createWallet\` stores the new \`SoftwareWallet\` (pinning the
\`'Obi-Wallet-Kenobi'\` default name), \`toggleShowSeed\` flips and
returns to hidden after two toggles, \`toggleShowSeed\` preserves the
wallet field.
- **restore_wallet_cubit:** initial state, \`restoreWallet\`
canonicalises mixed whitespace (\` x y \` → \`x y\`) before delegating
to \`WalletService.restoreWallet\`, the interim \`isLoading=true\` state
is observable.
- **debug_auth_cubit:** seeds \`address\` + \`savedSignature\` from the
service; empty-address fallback when service has none;
\`fetchSignMessage\` success + error; \`authenticate\` success +
uses-state-address + error.
- **balance_cubit:** initial zero-balance state shape \`(chain,
contract, wallet, asset)\`; subscribes to
\`BalanceRepository.watchBalance\` on init; emits each pushed balance
through the stream; \`close()\` cancels the subscription cleanly.
- **pending_transactions_cubit:** initial empty list; emits fetched list
on construction; falls back to empty list (not state-error) when the
service throws.

## Excluded (and why)
- **dashboard_bloc** + transaction-history sub-cubits + portfolio/price
chart cubits — pull in multiple services or larger event-driven flows;
would dwarf this PR.
- **transaction_history_receipt_cubit** / **settings_tax_report_cubit**
— both use \`getTemporaryDirectory()\` + real \`File\` IO; needs
path_provider platform-channel plumbing.
- **sell_***, **sell_bitbox_***, **hardware_connect_bitbox_*** —
Bitbox-coupled or DFX-sell-flow-coupled; held back to avoid review
conflicts with #321 area.
- **transaction_history_filter_cubit** — already covered by #327.

## Test plan
- [x] \`flutter analyze\` on the five new files — clean
- [x] \`flutter test\` — 21 / 21 passing locally
- [ ] CI green
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.

1 participant