Skip to content

test(coverage): close the coverage tail on shims, signing internals, decimal formatting - #76

Merged
joeblau merged 1 commit into
mainfrom
test/coverage-cov-tail
Jul 27, 2026
Merged

test(coverage): close the coverage tail on shims, signing internals, decimal formatting#76
joeblau merged 1 commit into
mainfrom
test/coverage-cov-tail

Conversation

@joeblau

@joeblau joeblau commented Jul 27, 2026

Copy link
Copy Markdown

Summary

Tests-only PR closing the remaining small coverage gaps in transport shims, signing internals, and the decimal/format machinery. Everything runs offline (HL_OFFLINE=1), exactly like CI.

File Before After
src/transport/_polyfills.ts 19/32 (59.4%) 31/31 (100%)
src/transport/_redact.ts 109/113 (96.5%) 113/113 (100%)
src/signing/_abstractWallet.ts 187/198 (94.4%) 198/198 (100%)
src/signing/_canonicalize.ts 159/166 (95.8%) 166/166 (100%)
src/signing/_fastWallet.ts 73/77 (94.8%) 74/77 (96.1%)
src/signing/_keccak.ts 48/51 (94.1%) 51/51 (100%)
src/utils/_decimal.ts 119/129 (92.2%) 129/129 (100%)
src/utils/_format.ts 62/64 (96.9%) 64/64 (100%)

(Before/after measured with HL_OFFLINE=1 bun test tests/ --coverage --coverage-reporter=lcov on the base commit vs this branch; the _polyfills.ts DA total differs by one line between runs due to Bun's line attribution — both runs report 100% of the lines it attributes after this PR.)

What was added

  • tests/transport/_polyfills.test.ts (new) — native pass-throughs (Promise_.withResolvers(), native DOMException_/CustomEvent_), plus the React Native fallbacks: platform globals are deleted and the module is re-imported through a cache-busting query (_polyfills.ts?react-native), so the selection IIFEs re-run and pick the bundled shims. Globals are restored in finally; the re-import never touches the instance the SDK already holds.
  • tests/transport/_redact.test.ts — a toJSON that returns its own owner (or an ancestor) closes a cycle immediately ("[Circular]") instead of descending forever.
  • tests/signing/mod.test.ts — signature validation (wrong length, invalid recovery value) and the wrap-vs-rethrow error paths of signTypedData, signRawDigest, getWalletAddress, getWalletChainId: non-SDK wallet failures are wrapped with the cause kept; AbstractWalletErrors are rethrown unchanged.
  • tests/signing/canonicalize_conformance.test.ts — the isCanonical fast-path variant branch (canonical → by reference, non-canonical → rebuilt, unmatched → CanonicalizeError) and the union structural matcher's required-key handling (an option missing a required key is skipped; an option missing only an optional key still matches).
  • tests/signing/fastWallet.test.tsderiveAddress rejects when the WASM module returns null or a malformed point (_setEccLoaderForTests with a stub module).
  • tests/signing/keccak.test.ts — a hasher throwing during the known-answer self-check is never trusted; and the real loader's import-failure fallback: hash-wasm is CJS, so dropping its require.cache record and hiding the entry file makes loadWasmKeccak's dynamic import really reject (restored in finally; verified that the real module re-loads afterwards). The test fails loudly (expect(served).toBe(false)) if a future runtime makes this simulation impossible.
  • tests/utils/format.test.ts — the half-even edge branches in _decimal.ts (round-up-to-one-unit past half, tie-to-even below, all-nines carry-out for both toDecimalPlacesHalfEven and toSignificantDigitsHalfEven), the public floatToWire guard tripping at 5e-9, and the subnormal mantissa decomposition in _format.ts — subnormals never take the exact path naturally (toFixed(9) is all zeros), so the tie predicate is forced via a scoped Number.prototype.toFixed patch (restored in finally); the wire result is still "0", matching the fast path.

_fastWallet.ts: 3 lines remain (54–55, 58) — not coverable tests-only

These are the real loadTinySecp256k1's shape-check return and catch. Reaching them requires the real import("tiny-secp256k1") to fail or return a partial module inside the shared bun test tests/ process. Verified experimentally that every mechanism fails or destabilizes the suite:

  • tiny-secp256k1 loads as ESM; an evaluated ESM module record is immutable — hiding/swapping package files afterwards has no effect.
  • mock.module is effectively one-shot per specifier and poisons it process-wide (mocks leak across test files; re-mocking the real namespace and mock.restore() do not undo it) — the repo's own module headers warn about this, and it would silently degrade the consensus-critical differential suites.
  • Forcing a real failure (hiding the package while uncached) makes the import reject, but the negative resolution is cached permanently — every later import("tiny-secp256k1") in the process keeps failing (verified in a clean sandbox), breaking subsequent factory calls.
  • require.cache injection doesn't apply: the ESM namespace is snapshotted at first import.

The same lines in the CJS hash-wasm loader were coverable (CJS recovers through require.cache deletion), which is why _keccak.ts reaches 100%.

Verification

  • bun run check — all gates pass (format, lint, docs, types, ts7, jsdoc, export).
  • HL_OFFLINE=1 bun test tests/ — 1049 pass, 0 fail, 236 skip (skip count unchanged from base).

…decimal formatting

Focused offline tests for the remaining uncovered lines:

- transport/_polyfills.ts: native pass-throughs, plus the React Native
  fallbacks via a cache-busting re-import with the platform globals deleted.
- transport/_redact.ts: a toJSON returning its own owner or an ancestor
  closes a cycle immediately.
- signing/_abstractWallet.ts: signature length/recovery-value validation and
  the wrap-vs-rethrow error paths of signTypedData, signRawDigest,
  getWalletAddress, and getWalletChainId.
- signing/_canonicalize.ts: the fast-path variant branch and the union
  structural matcher's required/optional key handling.
- signing/_fastWallet.ts: the deriveAddress guard for a broken WASM module.
- signing/_keccak.ts: a hasher throwing during the known-answer self-check,
  and the real loader's import-failure fallback (CJS entry hidden via
  require.cache drop + rename, restored afterwards).
- utils/_decimal.ts / _format.ts: half-even edge branches (round-up-to-unit,
  all-nines carry-out) and the subnormal mantissa decomposition, forced
  through the exact path.

_fastWallet.ts keeps 3 uncoverable lines (54-55, 58): the real ESM loader's
failure internals cannot be exercised in a shared bun test process — an
evaluated ESM module record is immutable, mock.module poisons the specifier
process-wide, and a forced resolution failure poisons it permanently, all of
which would break the differential suites.
@joeblau
joeblau merged commit 77ad5ab into main Jul 27, 2026
4 of 5 checks passed
@joeblau
joeblau deleted the test/coverage-cov-tail branch July 27, 2026 05:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant