Skip to content

test: improve test usefulness and fuzz coverage - #4341

Merged
piotr-roslaniec merged 10 commits into
devfrom
test-usefulness-followup
Sep 24, 2026
Merged

piotr-roslaniec merged 10 commits into
devfrom
test-usefulness-followup

Conversation

@piotr-roslaniec

Copy link
Copy Markdown
Collaborator

Summary

  • harden protobuf and tBTC fuzz generators, add native fuzz targets, and turn discarded round trips into behavior assertions
  • classify transient Electrum failures with sentinel errors and fail loudly when required Bitcoin/TBTC fixtures are missing
  • repair vacuous assertions, share duplicated Solidity authorization behaviors, and restore the upgrade-during-DKG tests

Verification

  • go test ./...
  • bounded repeat gates for protobuf generators and scheduler shutdown behavior
  • ECDSA suite: 836 passing, 0 failing, 0 pending
  • Random Beacon Solidity suite
  • Go formatting/import checks, Prettier, and ESLint

Follow-up test-usefulness work for the dev release branch.

The round-trip helper's error was discarded at 38 call sites, which hid three
fuzz generators that could never produce a value the marshaler accepts.

fuzzEphemeralPublicKey fuzzed X and Y directly, so the point was almost never
on secp256k1 and SerializeCompressed emitted keys ParsePubKey rejects.
fuzzEphemeralPrivateKey fuzzed the public half and D independently, so the two
halves never agreed and IsKeyMatching would reject every key it made. The tbtc
DepositSweepProposal generator produced unbounded reveal blocks where Marshal
requires IsUint64.

Assert the discarded errors, derive both key halves from one normalized scalar,
and clamp reveal blocks in a local generator rather than narrowing the shared
big.Int generator. Rename FuzzUnmarshaler to AssertUnmarshalDoesNotPanic so its
name matches the only thing it checks.
shouldSkipElectrumIntegrationError matched three substrings of err.Error(), so
rewording an upstream message silently turned a skipped integration test into a
passing one, and an unrelated error whose text merely mentioned a timeout was
skipped.

Match errors.Is against goelectrum.ErrTimeout, wrappers.ErrRetryTimeout and
electrum.ErrFeeEstimateUnavailable. Document both new sentinels on the variable
and on the function that returns them, so depending on their identity is a
stated promise rather than an accident. Regression tests cover bare, wrapped and
doubly wrapped sentinels plus the two negative cases that guard against
over-skipping.
Two electrum tests ranged over a fixture map with no presence check, so deleting
a fixture made them run zero subtests and pass. The scenario loaders behave the
same way: filepath.Walk skips absent files and returns a shorter slice with a nil
error, so a consuming test loops zero times and reports success.

Add an explicit manifest of required fixtures per directory and a guard that
fails when one is absent. Each guard lives in the consuming package, never under
testdata, because go test excludes that directory from ./... and a guard placed
there would never run.
Several tests passed regardless of the behavior in their own title.

TestContextCancelation claimed to verify that goroutines stop on cancellation
but only called three methods and asserted nothing; it now runs
observeSystemMetrics directly and fails if it does not return.
TestLocalSubmitRelayEntry checked only that no error came back, so an
implementation that validated its input and discarded it would pass; it now
asserts the entry is readable afterwards.

Replace 22 sleeps in the scheduler tests with channel synchronization and
bounded polling that fails on timeout, cutting the package from 4.6s to 0.8s.
Keep a real observation window where the assertion is that nothing happens,
since proving a negative needs one. Split the two longest tests, 683 and 677
lines against a repo median of 33, into behavior-named functions with the same
case counts.
The repo had no func FuzzXxx(f *testing.F) targets, so every fuzzed input came
from a fixed generator and nothing was kept between runs.

Add round-trip targets for the two paths whose generators were found broken:
ephemeral key marshaling and the deposit sweep proposal. Both assert equality
after the round trip rather than mere absence of a panic, which is what makes
them able to catch the original bugs. Seeds cover the empty input, small values
and the top of each accepted domain.
The two authorization suites each repeated the same two test bodies 25 times
under different describe contexts. The repetition was contextually correct, but
fixing one assertion meant editing it 14 times.

Move the shared bodies into behavior functions invoked per context, passing the
fixture through a getter so each test reads the instance created by its own
beforeEach rather than a stale reference captured at module load. Test counts
are unchanged at 118 and 144.
…appened

assertNoValueChange treated any send on tw.iterated as proof the worker had run
after being stopped. workerFunc increments the counter and sends on the channel
as two separate steps, so a worker that incremented before the stop can deliver
its signal after it. At -count=20 this failed while reporting an identical value
on both sides of the comparison, which is a false positive.

Use the counter as the source of truth and treat the signal only as a reason to
re-check early. The package now passes at -count=20 in 16.8s against 30.7s for
the original sleep-based version, and at -count=50.

Also correct both native fuzz target comments. They claimed to catch the
generator bugs that motivated them, but each target builds its own input and
never calls FuzzFuncs, so neither would fail if a generator regressed. The
comments now name the tests that do guard the generators and state what the
targets actually cover.
The `describe.skip` on "when a contract gets upgraded during DKG" carried a
two-part rationale and both parts are now stale. The fixture does accept
`useAllowlist: false` as an explicit option, and the packaged TokenStaking
artifacts do still expose `stake` and `increaseAuthorization`.

Removing the skip restores "keeps data of the existing wallet" and "stores data
of a new wallet". Both fail when mutated: swapping the expected public key and
the expected members hash takes the file from 19 passing to 17 passing and 2
failing.

The ecdsa suite is 836 passing with no pending tests and no remaining skips.
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 7cc82c0b-3e98-4951-b3a9-0bd41f5c2262

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Keep Testnet fixtures required through the default manifest test while allowing the integration suite to skip Mainnet and Testnet4 when those networks have no vectors. Restore the justified G404 suppression for non-security backoff jitter.
Align the new and changed comments with the code they describe:

- internal/testdata/bitcoin/manifest.go: the integration tests now skip
  (presence check) when a network has no Transactions entry, so the
  RequiredTransactions doc no longer claims they range the map without
  a check
- pkg/bitcoin/electrum/electrum.go: EstimateSatPerVByteFee and
  ErrFeeEstimateUnavailable docs now state that the returned error
  wraps the last target's error, so the sentinel matches only when the
  last failure was an oracle -1
- solidity/ecdsa/test/Allowlist.test.ts: the two reverting cases are in
  the initialization group, not below it
- drop previously/now diff narration from the wrappers, generator,
  pbutils, and electrum skip-classifier comments, keeping the durable
  invariants they document
@piotr-roslaniec
piotr-roslaniec merged commit 770f753 into dev Sep 24, 2026
38 checks passed
@piotr-roslaniec
piotr-roslaniec deleted the test-usefulness-followup branch September 24, 2026 16:31
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