Skip to content

ci: run the shielded wallet suite and stop the shield filter sweeping up unrelated tests - #4579

Open
Claudius-Maginificent wants to merge 3 commits into
v4.2-devfrom
fix/wallet-ci-shield-filter
Open

ci: run the shielded wallet suite and stop the shield filter sweeping up unrelated tests#4579
Claudius-Maginificent wants to merge 3 commits into
v4.2-devfrom
fix/wallet-ci-shield-filter

Conversation

@Claudius-Maginificent

@Claudius-Maginificent Claudius-Maginificent commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

Two bugs in our test workflows meant 185 wallet tests were being skipped on every CI run, and 145 of them were running in no job at all. One of the skipped tests had been failing for some time — a test that guards the maximum spendable balance reported to a user. Nothing surfaced it, because the filter meant to skip one suite was matching a word instead.

This turns those tests back on, fixes the one that was red, and adds a guard so a filter that stops matching anything fails the build instead of quietly reporting success.

User story

As a developer working on wallet code, I want the tests I write to actually run in CI, so that a broken change is caught before it merges rather than months later by someone who did not write it.

As a reviewer, I want a green check to mean the suite ran, so that an empty test run cannot pass as success.

Scenario

Actual behavior

A test filter intended to skip the shielded wallet suite instead skipped every test whose name contained the word "shield" anywhere. That swept in 40 unrelated tests — input-selection arithmetic, error-code mappings, memo encoding — one of which was failing and reported by nothing. Separately, the 145 tests it was actually aimed at were excluded by both workflows and covered by neither, so they ran nowhere.

Expected behavior

Each workflow skips only what it means to skip and what another job genuinely covers. The 145 shielded wallet tests run. The 40 unrelated tests run. A filter that matches nothing fails the step.

Issue being fixed or feature implemented

Two independent CI defects, plus the one failing test they were hiding.

Bug 1 — the filter matched a word, not a suite

Both Rust test workflows excluded shielded tests with -E 'not test(~shield)'. nextest's test(~…) is a substring match against the full test path, module segments included, so this does not select "the shielded suites" — it selects anything with the word shield anywhere in its name.

Across the three wallet packages it dropped 185 tests. Only 145 were the shielded suite. The other 40 were ordinary logic tests that happen to contain the word:

  • 12 in wallet::platform_wallet::shield_input_selection_tests — pure arithmetic over input selection and reported maximum spendable balance
  • 4 in changeset::shielded_changeset::activity_changeset_tests
  • 24 in platform-wallet-ffi — error-code mappings, memo encoding, preflight null-pointer checks

They cost about a tenth of a second and were being thrown away.

One of them was failing. regression_reports_max_from_usable_suffix_not_total_account_balance guards the maximum spendable balance a shield operation reports — funds-adjacent. It had been red for some time and nothing surfaced it, because the filter aimed at the shielded suite swept up the module by name.

The failure was a stale fixture, not a code bug. reserve() is 2 × compute_minimum_shielded_fee(2 actions); the v10 event constants (proof verification 100_000_000 → 40_000_000, storage 344 → 550 bytes/action) moved it from 325_702_400 to 228_280_000. The test's hardcoded leading balance of 297_264_780 sits between the two, so the "leading address is below the reserve" shape it asserts stopped existing. The planner is correct either way — with a lower reserve that address genuinely is a viable fee-paying input. The fixture now derives that balance as reserve() - 1, the largest value that must still be rejected by the strict > reserve test: a tighter boundary than the constant was, and one no future fee change can invalidate. The shape-precondition guard is deleted because it became true by construction.

Bug 2 — 145 tests ran in no CI job at all

tests-rs-workspace.yml excluded shielded tests and compensated with a dedicated "Run shielded tests" phase. That phase runs --package dpp --package drive --package drive-abci --package dash-sdk. The three wallet packages are not in it.

So the entire wallet::shielded:: suite — Orchard proving, viewing-key binds, note scans, sync — was excluded by both workflows and executed by neither. 145 tests, no coverage, no signal.

Measured on this branch: 145 run, 145 passed, 6.1 s wall clock (roughly 40–100 s of CPU depending on cache warmth). Nothing was red; the suite had simply gone unobserved.

The cost argument for excluding them does not hold either. --all-features compiles those test binaries whether or not the filter selects them, so the exclusion saved only execution time on artifacts that were already built and then discarded. Six seconds.

Fixed in both workflows, because they are mutually exclusive paths — tests-rs-wallet.yml is a fast path that runs instead of the workspace job for wallet-scoped PRs, so fixing one alone leaves the suite unrun on the other. Dropping either commit leaves the suite unrun on that path.

What was done?

Three files: the two workflows and one test fixture.

Why the workspace fix scopes by package, not module path

The workspace job's ~shield exclusion is legitimate for dpp / drive / drive-abci / dash-sdk: those are Orchard proving tests, and the compensating phase deliberately runs them under cargo test in one shared process so a single verifying key is built rather than one per test. Retargeting that filter by module path would pull them into the nextest phase, where process-per-test means each rebuilds its own VK — and they would run twice.

The wallet packages need none of that: 145 tests in 6 s under nextest. So the exclusion is scoped by package instead — keep a test if it is not shield-named or it belongs to a wallet package:

-E '(not test(~shield) or package(platform-wallet) or package(platform-wallet-storage) or package(platform-wallet-ffi))
    and (not binary_id(=drive-abci::strategy_tests) or test(~comprehensive_mixed_operations))'

The strategy-simulation step keeps its ~shield exclusion unchanged, because drive-abci is in the compensating phase, so that exclusion is compensated and is not a hole.

Zero-match guard

Every nextest invocation touched here pins --no-tests fail. A filter that stops selecting anything now fails the step instead of reporting green over an empty run — verified by pointing a deliberately non-matching expression at the suite and watching it exit 4. Pinned rather than left to nextest's default: a default that can change across a version bump is not a guarantee.

Scope note

The fixture fix travels with the workflow change on purpose — narrowing the filter is precisely what makes that test start running, so shipping the workflow alone would land this PR red on its own change.

How Has This Been Tested?

On this branch, through the repository's own commands:

  • New wallet job command, exactly as the workflow runs it: 1804 tests run, 1804 passed, 25.6 s — including all 145 shielded tests and the previously-failing balance regression.
  • Shielded suite alone: 145 run, 145 passed, 6.1 s wall clock.
  • The workspace filterset selects all 1804 wallet tests, byte-identical to an unfiltered listing (empty diff).
  • package() scoping confirmed to discriminate: excluding two wallet packages from a test(~shield) selection leaves exactly the 24 platform-wallet-ffi tests and nothing else.
  • Zero-match guard: a deliberately non-matching expression exits 4 with "error: no tests to run".
  • cargo clippy -p platform-wallet --all-targets --all-features --locked -- -D warnings: clean.
  • Both workflow files parse under PyYAML and their changed steps round-trip to the intended commands.

Not verified, stated deliberately. The workspace job itself was not executed — proving the expression's effect on dpp/drive/drive-abci would mean building their test binaries. Its behaviour there rests on the package() discrimination proof plus boolean reasoning, not on execution.

Flakiness caveat. The 145-test result is one run on one machine with generous parallelism. It establishes that the suite passes and is not slow; it does not establish that it is non-flaky on a loaded CI runner, and Orchard proving tests are exactly the kind that can be. The honest expectation is "turn it on and watch the first few runs", not "turn it on and forget it". If it proves flaky, the right response is to fix or quarantine the specific tests, not to reinstate a filter that hides 40 unrelated ones with them.

Breaking Changes

None. CI configuration and one test fixture; no library, API, or runtime behaviour changes.

Note for reviewers also following #3968

That branch adds further platform-wallet-storage tests with shielded in their names, which the old filter would also have swept up. They do not exist on this base, so they are not counted in any figure above.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • Bug Fixes
    • Improved wallet test coverage by ensuring shielded wallet tests run reliably.
    • Added safeguards so test jobs fail when no tests match the selected criteria.
    • Updated test validation to remain accurate across changing fee and reserve schedules.
    • Corrected wallet workspace test selection to prevent relevant shielded tests from being skipped.

lklimek and others added 3 commits September 1, 2026 12:14
…rom the reserve

`regression_reports_max_from_usable_suffix_not_total_account_balance`
failed on `--all-features`, on this branch and on its base. The failing
line was the test's own shape-precondition guard, not a production
assertion, and its message said what to do: re-seed the balances when the
versioned reserve drops below the hardcoded leading balance.

That is exactly what happened. `reserve()` is
`2 x compute_minimum_shielded_fee(2 actions)`. Under the v9 event
constants (proof verification 100_000_000, storage 344 bytes/action) it
was 325_702_400; under v10 (40_000_000 and 550 bytes/action) it is
228_280_000. The fixture's 297_264_780 sits between the two, so the
leading address stopped being sub-reserve dust and the "usable suffix"
shape the test claims to build no longer existed. The planner is right
either way: with a lower reserve that address genuinely is a viable input
0, so the whole balance genuinely is usable. Maximum spendable balance was
never miscomputed.

Derive the leading balance as `reserve() - 1` instead — the largest
balance that must still be rejected by the strict `> reserve` viability
test, so a tighter boundary than the magic number was, and one no future
fee re-balance can invalidate. The guard goes with it: the precondition is
now true by construction.

CI never caught this. The wallet job filtered nextest with
`not test(~shield)`, a substring match on the full test path, so the
`shield_input_selection_tests` module was excluded as collateral by a
filter aimed at the shielded-wallet suite. It took 47 pure-logic tests
across the three wallet crates with it — input selection, FFI error codes
and memo encoding, SQLite viewing-key rows — for a measured 0.089 s of
runtime. Exclude by module path (`wallet::shielded::`) so the step skips
what it means to skip, and pin `--no-tests fail` so a filter that stops
selecting anything fails the step instead of passing green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ages

`-E 'not test(~shield)'` is a substring match on the full test path, so it
selects "anything with the word shield in its name", not "the shielded
suites". For dpp, drive, drive-abci and dash-sdk that is survivable: the
"Run shielded tests" phase compensates, and those packages genuinely need
it — they are Orchard proving tests, and that phase runs them under
`cargo test` in one shared process so a single verifying key is built
instead of one per test.

The three wallet packages have neither the compensator nor the need. They
are absent from the shielded phase's package list, so everything the
substring dropped fell into a gap no job covered: 145 `wallet::shielded::`
tests, plus ~47 more whose only sin is the word — input selection, FFI
error codes, memo encoding, SQLite viewing-key rows. Measured, they cost
~6 s wall under nextest's process-per-test model, so the VK-reuse argument
that justifies the exclusion elsewhere does not apply to them.

Carve the wallet packages out of the exclusion by package rather than
retarget the pattern by module path: a module-path filter would also stop
excluding dpp/drive/drive-abci/dash-sdk, pulling their proving tests into
the nextest phase where each would rebuild its own verifying key. The
strategy-simulation phase keeps its `~shield` exclusion for the same
reason — drive-abci IS in the compensating phase.

Both nextest invocations pin `--no-tests fail` so an expression that stops
selecting anything fails the step instead of passing green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 145 `wallet::shielded::` tests ran in no CI job at all. Both workflows
excluded them, and tests-rs-workspace.yml's compensating "Run shielded
tests" phase covers dpp / drive / drive-abci / dash-sdk only — the three
wallet packages are absent from its package list. The sibling commit fixes
the workspace path; this fixes the fast path a wallet-scoped PR actually
takes, and both are needed because the two workflows are mutually
exclusive for any given PR.

Drop the test filter entirely rather than narrow it again. Nothing in
these three packages is worth excluding: the shielded suite is 145 tests
in ~6 s wall (~97 s CPU) under nextest's process-per-test model, and
`--all-features` compiles those binaries whether or not they are selected
— so the exclusion was saving execution time on artifacts already built
and discarded. Removing the expression also removes the whole class of
substring-filter bug from this workflow rather than moving it.

Measured on this base: 1804 tests, 1804 passed, 25.6 s for the step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 1, 2026
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 7c07ce13-69a4-4a8d-a1a2-9e32e9796aa1

📥 Commits

Reviewing files that changed from the base of the PR and between 0c2f337 and 5377ad6.

📒 Files selected for processing (3)
  • .github/workflows/tests-rs-wallet.yml
  • .github/workflows/tests-rs-workspace.yml
  • packages/rs-platform-wallet/src/wallet/platform_wallet.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Wallet test coverage

Layer / File(s) Summary
Wallet workflow test selection
.github/workflows/tests-rs-wallet.yml, .github/workflows/tests-rs-workspace.yml
Wallet tests now include shielded tests where required. Nextest commands fail when filters select no tests.
Regression test assertion inputs
packages/rs-platform-wallet/src/wallet/platform_wallet.rs
The regression test derives dust and usable balances from runtime values and reuses them in capacity assertions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 5377a

This change restores wallet test coverage, prevents empty filtered runs from passing, and updates a stale boundary fixture without changing product or runtime behavior; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: quantumexplorer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (2 skipped: 2 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary CI change: enabling the shielded wallet suite and correcting an overly broad shield test filter.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/wallet-ci-shield-filter

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.

@lklimek
lklimek marked this pull request as ready for review September 1, 2026 13:02
@thepastaclaw

thepastaclaw commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

🕓 Ready for review — 18 ahead in queue (commit 5377ad6)
Queue position: 19/52 · 2 reviews active
ETA: start ~23:57 UTC · complete ~00:52 UTC (median 54m across 30 recent reviews; 2 slots)
Queued 2d 2h ago · Last checked: 2026-09-03 15:40 UTC

@lklimek lklimek changed the title fix(ci): run the shielded wallet suite and stop the shield filter sweeping up unrelated tests ci: run the shielded wallet suite and stop the shield filter sweeping up unrelated tests Sep 1, 2026
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.91%. Comparing base (0c2f337) to head (5377ad6).

Additional details and impacted files
@@            Coverage Diff            @@
##           v4.2-dev    #4579   +/-   ##
=========================================
  Coverage     86.91%   86.91%           
=========================================
  Files          2756     2756           
  Lines        360329   360329           
=========================================
+ Hits         313162   313163    +1     
+ Misses        47167    47166    -1     
Components Coverage Δ
dpp 88.29% <ø> (ø)
drive 85.52% <ø> (ø)
drive-abci 89.67% <ø> (+<0.01%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.92% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 41.10% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Claudius-Maginificent

Copy link
Copy Markdown
Collaborator Author

Reviewed this against two findings from the #3968 review that were deferred here. One is fully closed by this PR; the other is not, and this PR slightly widens it.

Closed. The two wallet CI paths no longer disagree on the shielded filter. tests-rs-wallet.yml and tests-rs-workspace.yml are aligned, and the test(~shield) substring trap is documented at both sites — including the point that the filter matched ~47 tests that merely contain the word rather than the wallet::shielded:: suite. That was the whole finding; nothing left to do.

Still open: no cargo-nextest version floor for --no-tests fail.

--no-tests fail is only accepted from cargo-nextest 0.9.85 onward. This PR now uses it in both workflows, but neither establishes a floor:

  • tests-rs-wallet.yml has no nextest install step at all. It runs cargo nextest run on a [self-hosted, macOS, ARM64] runner and relies on whatever binary happens to be on that host.
  • tests-rs-workspace.yml:162-167 installs nextest only when it is missing:
    if ! cargo nextest --version >/dev/null 2>&1; then
      cargo install cargo-nextest --locked
    fi
    
    An already-present stale binary is never upgraded.

On a runner carrying a pre-0.9.85 nextest, the step dies with a clap usage error naming an argument — a red build whose diagnosis looks nothing like a test failure, on a self-hosted runner whose toolchain state nobody in this repo controls. The failure mode is worse than the bug it guards against, because a maintainer reading the log sees a broken workflow rather than a broken filter.

Cheap fixes, either is fine:

  1. Add a floor to the existing guard: cargo nextest --version parsed against 0.9.85, reinstalling when below it, rather than only when absent.
  2. Or pin explicitly with taiki-e/install-action@v2 (tool: cargo-nextest@0.9.85) in both workflows, which also gives the wallet workflow the install step it currently lacks.

Not a blocker for the filter work here — it just needs to not be forgotten, since this PR is what makes --no-tests fail load-bearing in two places instead of one.

🤖 Reported by Claudius the Magnificent AI Agent, from the #3968 review triage

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.

3 participants