test: cover verify_pin_cubit + three DFX services (+32 tests)#328
Merged
Conversation
Adds 32 unit tests across one screen cubit and three DFX backend services. mocktail mocks for AppStore + repository boundaries; `http.testing.MockClient` for the HTTP surface. - verify_pin_cubit (18): initial state, addDigit / deleteDigit incl. no-ops at boundaries + while locked, correct pin resets lockout and emits VerifyPinSuccess (real PBKDF2 via compute()), wrong pin path for the first attempt, lockout-disabled mode never persists, 5th wrong attempt triggers a 1-min temporary lockout, permanent lockout at threshold; onLockExpired preserves failedAttempts; checkBiometricAvailability for all four state transitions (in-window / expired / threshold / success / fail / unavailable) - dfx_country_service (5): list mapping + caching, error path, case- insensitive symbol lookup, unknown-symbol error, cache field - dfx_blockchain_api_service (5): POST shape (address + JWT + chain), testnet flips to 'Sepolia', empty balances → 0.0, 201 acceptance, ApiException on non-2xx - dfx_faucet_service (4): POST + JWT, 201 acceptance, ApiException on non-2xx, documents 'Bearer null' behaviour when no auth token set
3 tasks
TaprootFreak
added a commit
that referenced
this pull request
May 15, 2026
…sts) (#329) ## Summary Stage 6 of the coverage push. Adds 23 unit tests for two more DFX services and two screen-level cubits whose only previous coverage was at the widget layer. | File under test | Test file | Cases | | --- | --- | --- | | \`lib/packages/service/dfx/dfx_bank_account_service.dart\` | \`test/packages/service/dfx/dfx_bank_account_service_test.dart\` | 7 | | \`lib/packages/service/dfx/dfx_price_service.dart\` | \`test/packages/service/dfx/dfx_price_service_test.dart\` | 8 | | \`lib/screens/settings_contact/cubit/settings_contact_cubit.dart\` | \`test/screens/settings_contact/settings_contact_cubit_test.dart\` | 4 | | \`lib/screens/support/cubits/support_tickets/support_tickets_cubit.dart\` | \`test/screens/support/cubits/support_tickets_cubit_test.dart\` | 4 | ## What each file covers - **dfx_bank_account_service:** \`getBankAccounts\` GET shape (path, JWT header, list mapping) + \`ApiException\` on non-2xx; \`createBankAccount\` POST body, optional-label omission, error path; \`updateBankAccount\` PUT to \`/v1/bankAccount/{id}\` with only the provided fields (also pins the \`isDefault → "default"\` wire mapping). - **dfx_price_service:** \`getPriceOfAsset\` CHF / EUR scaled by 100 (rappen / cents); \`getPriceChart\` maps each entry with asset, scaled price, and UTC time; \`getChfToEurRate\` returns \`eur / chf\` or \`0.0\` when chf == 0 (no division by zero); all three paths throw on non-200. - **settings_contact_cubit:** Success with \`emailSet: true\` / \`false\`, Failure on async-throw, and a manual \`init()\` call recovering from a transient failure. - **support_tickets_cubit:** Loaded with DTO → \`SupportIssue\` mapping, Loaded(empty), Error on async-throw, and a manual \`loadTickets()\` call recovering from a transient failure. ## Notes - Both cubits fire their work (\`init()\` / \`loadTickets()\`) in the constructor, so by the time a stream subscriber attaches the synchronous Loading emit has already passed on the broadcast stream. The tests therefore assert the **final state** via \`stream.firstWhere\` rather than the full sequence — a deliberate documented choice in the test file headers. - Mocked \`getUser\`/\`getTickets\` use \`thenAnswer((_) async => throw …)\` instead of \`thenThrow\` so the throw goes through a microtask boundary (otherwise the cubit's \`catch\` block emits Failure before any listener can hear it). - Service tests use \`http.testing.MockClient\` against \`AppStore.httpClient\`, same pattern as #328. ## Excluded (and why) - \`dfx_kyc_service\` — large surface (≈ 260 lines), heavy overlap with the KYC cubit logic already covered by #319; would invite review-time conflicts. - \`dfx_brokerbot_service\`, \`real_unit_sell_payment_info_service\` — likely touched by #321 (dashboard buy/sell auth). - \`transaction_history_service\`, \`real_unit_account_service\` — depend on \`AppStore.wallet.currentAccount\` plumbing; will be covered alongside the wallet-coupled hook tests in a follow-up. - \`support_chat_cubit\`, \`support_create_ticket_cubit\` — share \`DfxSupportService\` with this PR; held back to keep the diff focused. ## Test plan - [x] \`flutter analyze\` on the four new files — clean - [x] \`flutter test\` — 23 / 23 passing locally - [ ] CI green
3 tasks
TaprootFreak
added a commit
that referenced
this pull request
May 15, 2026
## Summary Stage 10 of the coverage push. Adds 21 unit tests for the two remaining easy-to-mock DFX backend services. | Service under test | Test file | Cases | | --- | --- | --- | | \`lib/packages/service/dfx/dfx_support_service.dart\` | \`test/packages/service/dfx/dfx_support_service_test.dart\` | 9 | | \`lib/packages/service/dfx/dfx_brokerbot_service.dart\` | \`test/packages/service/dfx/dfx_brokerbot_service_test.dart\` | 12 | ## What each file covers - **dfx_support_service:** \`getTickets\` GET shape (path, Bearer JWT) + ApiException on non-200; \`getTicket\` by uid + ApiException; \`createTicket\` POST body (type/reason/name + optional message) + omits message when null + requires status \`201\` (200 is rejected as ApiException); \`sendMessage\` POST shape + ApiException on non-201. \`getAuthToken\` is short-circuited by pre-populating \`sessionCache.authToken\` so the signing flow stays out of these unit tests. - **dfx_brokerbot_service:** \`getBuyPrice\` GET + currency-code query + invalid-input guards (non-numeric, zero, negative, non-200); \`getBuyShares\` GET + currency + invalid-input guards; \`getSellPrice\` with Bearer JWT + ApiException on non-200 + the invalid-input case skips the HTTP call entirely; same matrix for \`getSellShares\`. ## Notes - Same mocktail + \`http.testing.MockClient\` pattern as the previous DFX-service PRs (#326 / #328 / #329). - The "invalid input never reaches HTTP" assertions are a small but meaningful contract: callers can rely on these methods to fail fast before any network round-trip. ## Excluded (deferred) - \`settings_user_data_cubit\` was on the original Stage-10 plan but coordinates 3 services + Country lookups + multi-branch KYC-step-status handling. It deserves its own focused PR rather than tagging it onto these two service tests. - \`dfx_kyc_service\` — held back to avoid review conflicts with open PR #332 (KYC routing / bitbox sign hardening). - Buy / sell / Bitbox cubits — held back while #321 and #332 are open. ## Test plan - [x] \`flutter analyze\` on the two new files — clean - [x] \`flutter test\` — 21 / 21 passing locally - [ ] CI green
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
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.
What each file covers
Notes
Excluded (and why)
Test plan