Skip to content

Migrate test suites to quasar-test framework#113

Merged
mikemaccana merged 14 commits into
mainfrom
claude/quasar-0.1.0-release-updates-vdiqrx
Jul 23, 2026
Merged

Migrate test suites to quasar-test framework#113
mikemaccana merged 14 commits into
mainfrom
claude/quasar-0.1.0-release-updates-vdiqrx

Conversation

@mikemaccana

Copy link
Copy Markdown
Collaborator

This PR migrates integration test suites across the repository from the low-level QuasarSvm harness to the higher-level quasar-test framework with #[quasar_test] fixtures and CPI instruction builders.

Summary

The migration standardizes test infrastructure across all Quasar programs by replacing manual SVM setup, account creation, and instruction building with declarative test fixtures and type-safe CPI builders. This reduces boilerplate, improves test readability, and centralizes test utilities.

Key Changes

  • Test Framework Migration: Replaced QuasarSvm harness with quasar_test::prelude::* in 40+ test files across:

    • Finance programs (token-swap, lending, order-book, perpetual-futures, prop-amm, escrow, betting-market, token-fundraiser, vault-strategy)
    • Token programs (create-token, pda-mint-authority, external-delegate-token-master, token-extensions)
    • Basics examples (counter, account-data, checking-accounts, close-account, create-account, favorites, hello-solana, pda-rent-payer, processing-instructions, program-derived-addresses, pyth, realloc, rent, repository-layout, transfer-sol)
    • Compression programs (cnft-vault, cutils)
  • Instruction Building: Replaced manual Instruction construction with typed CPI builders from crate::cpi::* (e.g., CreatePoolInstruction, DepositLiquidityInstruction, SwapTokensInstruction)

  • Account Management: Removed manual account factory functions (signer(), token_account(), etc.) in favor of fixture-based account provisioning

  • Removed Boilerplate:

    • Eliminated extern crate std declarations
    • Removed manual setup() functions that loaded ELF binaries
    • Removed low-level QuasarSvm initialization code
    • Removed manual ProgramError mapping helpers
  • Cargo.toml Updates: Added lib crate-type alongside cdylib in affected programs to support test compilation

  • Quasar.toml Simplification: Removed verbose toolchain configuration from project manifests

  • Changelog Entries: Added migration notes to affected program changelogs (dated 2026-07-22)

Notable Implementation Details

  • Two lending test scenarios that require slot warping retain the low-level QuasarSvm harness (see slot_warp comment in lending tests)
  • Programs depending on removed upstream crates (quasar-metadata) are marked as "NOT migrated" with explanatory comments
  • The mul_div helper function for ratio math is preserved in token-swap tests as it mirrors program-side computation
  • CPI instruction builders are imported from crate::cpi::* modules, centralizing instruction construction logic
  • State types and PDA helpers are imported from crate::state::* and crate::* respectively

https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB

claude added 14 commits July 22, 2026 21:47
The main matrix now installs quasar-cli from the 0.1.0-release branch head
(rev be60fca, matching the rev pinned in every migrated Cargo.toml) and
runs cargo generate-lockfile before quasar build, because the 0.1.0 IDL
step runs `cargo metadata --locked` and no Quasar example commits a
lockfile.

tokens/{token-minter,nft-minter,nft-operations} depend on quasar-metadata,
which was removed upstream before 0.1.0 with no replacement. They move to
.github/.ghaignore (excluded from the main matrix) and build in a new
legacy-metadata-examples job that installs the pre-0.1.0 CLI (rev
3d6fb0d8), which still parses their pre-0.1.0 Quasar.toml schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
First example on the 0.1.0-release line (rev be60fca): Quasar.toml
rewritten to the 0.1.0 schema, idl-build feature and "lib" crate-type
added for the new IDL build, and tests rewritten from the direct
QuasarSVM harness to quasar-test (#[quasar_test], Wallet fixture,
crate::cpi instruction builders, typed test.read state assertions).
The quasar-svm git dev-dependency and the generated-client path
dev-dependency are gone; quasar-test pulls the published quasar-svm
0.1.0 from crates.io.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
Same migration as the counter pilot for the 15 remaining basics projects
(16 program dirs; cross-program-invocation carries hand/ and lever/):
deps repinned to 0.1.0-release rev be60fca (pyth was floating and is now
pinned), Quasar.toml on the 0.1.0 schema, idl-build feature and "lib"
crate-type added, and every test suite rewritten to quasar-test with all
scenarios preserved.

Genuine 0.1.0 API breaks fixed along the way:
- realloc: zeropod 0.3.3 rejects String<1024> with the default 1-byte
  length prefix; the message fields become String<1024, 2>.
- pda-rent-payer: Seed left the prelude; it now imports
  quasar_lang::cpi::Seed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
cnft-burn, cnft-vault, and cutils move from floating git deps to the
pinned 0.1.0-release rev be60fca, get the 0.1.0 Quasar.toml schema,
idl-build feature and "lib" crate-type, and quasar-test dev-deps.
cnft-vault's six test scenarios are fully ported (Bubblegum
borsh/keccak helpers kept verbatim; harness layer now uses Program
fixtures and crate::cpi builders); cnft-burn and cutils keep their
placeholder test files, reworded for quasar-test.

0.1.0 API break fixed: quasar_lang::pda::based_try_find_program_address
was renamed to try_find_program_address (cutils bubblegum_types).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
create-token, external-delegate-token-master, pda-mint-authority, and
transfer-tokens move to the 0.1.0-release rev be60fca with the 0.1.0
Quasar.toml schema and quasar-test suites (Mint/TokenAccount fixtures,
has_tokens/has_supply balance assertions, typed state reads). Tests that
decode raw SPL accounts use spl-token 9, which shares quasar-test's
solana-pubkey major.

Genuine 0.1.0 API breaks fixed:
- quasar_spl::initialize_mint2 free function is gone; mints initialize
  via the TokenCpi trait method on the token_program handle.
- Seed left the prelude (import from quasar_lang::cpi).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
token-minter, nft-minter, and nft-operations depend on quasar-metadata,
which was removed upstream before the 0.1.0 release with no replacement.
Their manifests and changelogs now say so explicitly: they stay on the
pre-0.1.0 pins (quasar 623bb70 / quasar-svm cb7565d) and build in the
legacy-metadata-examples CI job with the older CLI until upstream ships
a metadata story for 0.1.x.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
All 11 token-extensions examples (basics, cpi-guard,
default-account-state, group, immutable-owner, interest-bearing,
memo-transfer, mint-close-authority, non-transferable,
permanent-delegate, transfer-fee) move to the 0.1.0-release rev be60fca:
0.1.0 Quasar.toml schema, idl-build feature, "lib" crate-type, and
quasar-test suites with Token-2022 fixtures
(.token_program(TokenProgram::Token2022)). Every scenario ported;
several bare is_ok assertions strengthened to has_tokens/has_supply and
account-layout checks. No program-source changes were needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
All 7 transfer-hook examples (account-data-as-seed,
allow-block-list-token, counter, hello-world, transfer-cost,
transfer-switch, whitelist) move to the 0.1.0-release rev be60fca: the
crates.io quasar-svm = "0.1" dev-dep is deleted in favor of quasar-test,
Quasar.toml moves to the 0.1.0 schema, and the flow tests are rewritten
to #[quasar_test] with crate::cpi builders. Two negative assertions were
strengthened from bare is_err() to the exact ProgramError the handlers
return.

Genuine 0.1.0 API break fixed across 6 of the 7 programs:
Address::find_program_address no longer exists; PDA derivation now uses
quasar_lang::pda::try_find_program_address.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
All 9 finance examples (10 program dirs; vault-strategy carries
mock-swap-router) move to the 0.1.0-release rev be60fca with the 0.1.0
Quasar.toml schema, idl-build feature, and quasar-test suites. All ~78
scenarios ported with their domain-math helpers and exact-amount
assertions preserved; byte-offset state checks became typed test.read
assertions except where byte layout is the tested behavior.

Special cases:
- lending: its interest math reads Clock::get()?.slot and quasar-test
  has no slot warp, so the two slot-warp scenarios stay on a trimmed
  low-level harness with a crates.io quasar-svm = "=0.1.0" dev-dep
  (commented in Cargo.toml); the .so is read at runtime, not
  include_bytes.
- prop-amm: the stale-price scenario pins the Clock sysvar account at
  slot 1000 via test.set_account.
- betting-market: the self-referential Bet PDA constraint
  (address = Bet::seeds(&bet.outcome, ...)) is inexpressible in 0.1.0;
  the handlers now make the identical canonical-PDA check explicitly
  with Bet::find_address.

Recurring 0.1.0 API breaks fixed: Seed imports move to
quasar_lang::cpi, and initialize_mint2/initialize_account3 free
functions become TokenCpi trait methods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
Upstream removed the quasar-metadata crate before the 0.1.0 release with
no replacement, which had left token-minter/nft-minter/nft-operations
frozen on pre-0.1.0 pins behind a separate legacy CI job. This vendors
the crate instead: tokens/quasar-metadata is a copy of metadata/ from
blueshift-gg/quasar rev 623bb70f (the last revision shipping it, MIT
licensed), adapted to compile against the be60fca pin:

- AccountInit::init impls take the trait's new R: RentAccess parameter
- based_try_find_program_address renamed to try_find_program_address
- Seed imported from quasar_lang::cpi (left the prelude)
- CpiDynamic::set_data_len became unsafe; both call sites wrapped with
  SAFETY comments (the preceding raw-pointer writes initialize exactly
  data[0..offset])

Provenance and the local diff are documented in the crate's README and
CHANGELOG. With the three metadata examples migrating onto this crate,
the legacy-metadata-examples CI job and the .ghaignore entries are gone:
the whole matrix builds with the one 0.1.0 CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
…adata

token-minter, nft-minter, and nft-operations join the 0.1.0-release pin
(be60fca) like every other example, depending on the vendored
tokens/quasar-metadata crate as a path dependency. Standard migration
otherwise: 0.1.0 Quasar.toml schema, idl-build feature, quasar-test
suites with every scenario preserved (nft-operations still drives the
full create-collection/mint/verify lifecycle against the real
mpl_token_metadata fixture, loaded at runtime). The only program-source
break was the recurring Seed-left-the-prelude import in nft-operations'
three instruction files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
CI caught immutable-owner and memo-transfer failing at runtime:
Outcome::has_tokens decodes with the strict 165-byte base spl-token
layout, which rejects Token-2022 accounts carrying extension TLV data
(170 and 300 bytes here). Assert the zero balance from the amount field
bytes (64..72, identical in both layouts) instead. The other
has_tokens/has_supply sites in the repo operate on fixture-created
base-layout accounts or classic SPL token accounts and are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
Quasar 0.1.0 clears ctx.data after decoding declared instruction args
("close the instruction argument zero-copy boundary"), so the pre-0.1.0
pattern of reading raw trailing bytes through ctx.data now sees an empty
slice and every such handler failed with InvalidInstructionData. The
cnft-vault withdraw handlers (caught by CI), plus the latent cnft-burn
and cutils handlers whose placeholder tests hid the same break, now
declare their payloads as typed instruction arguments (roots, hashes,
nonce, index, proof lengths; cutils' URI as String<256, 2>) — only the
variable-length proofs stay dynamic, as remaining accounts. The
cnft-vault tests build instructions through the typed fields instead of
appending raw bytes.

betting-market's full-lifecycle test exhausted the 1.4M CU budget: the
migration had replaced the inexpressible self-referential
`address = Bet::seeds(&bet.outcome, ...)` constraint with the generated
Bet::find_address helper, which is a const-context/client function whose
software SHA-256 implementation (const_crypto) is ruinous on-chain. The
claim_refund/claim_winnings/close_losing_bet handlers now verify the
canonical bet PDA against the account's stored bump with
quasar_lang::pda::verify_program_address — one sha256 syscall, same
rejection behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UhmBvDSQb4zPB5k4eueNB
@mikemaccana
mikemaccana merged commit a058181 into main Jul 23, 2026
40 checks passed
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.

2 participants