Move every example onto Anchor v2's LiteSVM test harness - #133
Merged
Conversation
Anchor v2 ships its own LiteSVM wrapper, `anchor-v2-testing`. Tests that call `LiteSVM::new()` directly still run and still pass, but they record nothing, so `anchor test --profile`, `anchor debugger` and `anchor coverage` all come back empty. Going through `anchor_v2_testing::svm()` is what connects a test to those three tools; it is `LiteSVM::new()` until the `profile` feature turns tracing on, so the default path costs nothing. Per program crate, the dev-dependencies drop `litesvm`, `solana-signer` and `solana-keypair`, which the harness re-exports, and gain the harness plus a `profile` feature forwarding to it. Tests swap `LiteSVM::new()` for `anchor_v2_testing::svm()` and their imports follow. The execution model does not change: same in-process LiteSVM, same solana-kite helpers, which take the same `LiteSVM` value. The harness is published only from git; crates.io has no `anchor-v2-testing`, and a git dependency is what the v2 testing documentation prescribes. It is pinned to a revision rather than tracking `anchor-next`: Cargo.lock is not tracked in this repository, so CI resolves fresh on every run, and a branch would let an upstream push change a build with no commit here to explain it. Bumping the rev is then a deliberate, reviewable change. Two things stay as they were. The one test using `solana_transaction::Transaction` keeps that dependency, because the harness re-exports `VersionedTransaction` and not `Transaction`. `mock-swap-router` has no tests, so it gains nothing. Verified locally before the full sweep: `basics/counter` passes its 3 tests and `anchor coverage` resolves 294 of 312 program counters to source across 19 files; `finance/lending`, eight test binaries over a shared harness, passes all 26.
The first pass inserted `anchor_v2_testing::{...}` wherever the first
alphabetically-later entry appeared, without checking how deep in the `use`
tree that entry was. In a block like
use {
anchor_lang::{
solana_program::instruction::Instruction, system_program, ...
},
...
};
the first later entry is `solana_program`, nested one level inside
`anchor_lang`, so the import landed there and every affected crate failed to
compile with `unresolved import anchor_lang::anchor_v2_testing`. The insertion
point is now pinned to depth exactly 1, the top level of the outer block.
Five test files also named the replaced crates by full path rather than
importing them, mostly in helper signatures like
`fn setup() -> (LiteSVM, solana_keypair::Keypair)`. Dropping the
dev-dependencies left those paths dangling, so they now resolve through the
harness too.
A local build-and-test sweep caught both: 44 of the 51 projects it reached
failed on the first, two on the second. The three shapes are re-verified:
account-data (nested block import), processing-instructions and
repository-layout (full-path references) all build and pass.
rustfmt writes `use anchor_v2_testing::Signer;`, not
`use anchor_v2_testing::{Signer};`. Three lending test files import only
`Signer` from the harness and got the braced form from the migration.
`cargo fmt --all --check` is clean across the workspace again.
`anchor test` generates the IDL as well, so the `--no-idl` exemption added in #132 only covered half the problem: the eight projects it exempts got past `anchor build` and then failed at `anchor test` with the same upstream error, Error: missing field `variants` at line 1 column 6579 which is anchor#4947, `#[derive(IdlType)]` emitting `fields` where the IDL spec requires `variants` for enums. `betting-market` hit this on the harness-migration PR. I have not established why the same call sequence passed on the `main` run that introduced the skip; the build cache is the likeliest difference, since this branch touches every program's manifest and so cannot reuse it. Either way the exemption belongs on both commands, and adding it cannot break a project that was already passing. Removed together with the rest of the workaround when an Anchor release carrying the fix ships.
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.
Anchor v2 ships its own LiteSVM wrapper,
anchor-v2-testing. Tests that callLiteSVM::new()directly still run and still pass, but they record nothing, soanchor test --profile,anchor debuggerandanchor coverageall come back empty. Going throughanchor_v2_testing::svm()is what connects a test to those three tools, and it isLiteSVM::new()until theprofilefeature turns tracing on, so the default path costs nothing.What changes
Per program crate, the dev-dependencies drop
litesvm,solana-signerandsolana-keypair, which the harness re-exports, and gain the harness plus aprofilefeature forwarding to it:Tests swap
LiteSVM::new()foranchor_v2_testing::svm()and their imports follow. The execution model does not change: same in-process LiteSVM, samesolana-kitehelpers, which take the sameLiteSVMvalue.Why a git dependency, and why a pinned rev
crates.io has no
anchor-v2-testing; a git dependency is what the v2 testing documentation prescribes. That documentation saysbranch = "anchor-next". This pins a revision instead, becauseCargo.lockis not tracked here, so CI resolves fresh on every run and a branch would let an upstream push change a build with no commit in this repository to explain it. Bumping the rev then becomes a deliberate, reviewable change. Happy to switch to the branch if you would rather track it.What this unlocks
None of these work today, because nothing records traces:
anchor test --profilewrites per-test SBF traces and renders flamegraphsanchor debuggeropens an instruction stepper with DWARF source mapping and CPI framesanchor coverageemits LCOV attarget/coverage/sbf.lcovVerified on
basics/counter:anchor coverageresolved 294 of 312 program counters to source across 19 files, 91 lines covered, with per-line hit counts in the LCOV.Verification
A full local build-and-test sweep over all 55 Anchor projects: 54 pass, 286 tests.
The one failure,
finance/vault-strategy'stest_add_asset_enforces_max_assets, is not caused by this change. It fails identically on unmigratedorigin/mainwithProgramLoad("Entrypoint out of bounds")while loadingmock_swap_router.so, and it passes in CI, so it is an artefact of the locally-builtcargo-build-sbf4.2.0 rather than the Solana 3.1.14 toolchain CI installs. Flagging it rather than folding a fix into this PR.The sweep earned its keep. The first pass of this migration failed on 44 of 51 projects with
unresolved import anchor_lang::anchor_v2_testing: the import was being inserted at the first alphabetically-later entry without regard to nesting depth, so in a block likeit landed inside
anchor_lang. Five further files named the replaced crates by full path rather than importing them, mostly in helper signatures likefn setup() -> (LiteSVM, solana_keypair::Keypair), and dropping the dev-dependencies left those dangling. Both are fixed in the second commit.Not included
The one test importing
solana_transaction::Transactionkeeps that dependency, because the harness re-exportsVersionedTransactionand notTransaction.mock-swap-routerhas no tests, so it gains nothing.Generated by Claude Code