From 7783d2b2c8fd335eab34de57a3837c1108a8f8be Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 5 Jun 2026 08:53:20 +0100 Subject: [PATCH] Migrate JS tests to a Kit client and LiteSVM --- Makefile | 12 +-- clients/js/README.md | 11 +-- clients/js/package.json | 2 + clients/js/pnpm-lock.yaml | 147 ++++++++++++++++++++++++++++-- clients/js/test/_setup.ts | 38 ++++++++ clients/js/test/example.test.ts | 16 +++- scripts/restart-test-validator.sh | 44 --------- 7 files changed, 200 insertions(+), 70 deletions(-) create mode 100644 clients/js/test/_setup.ts delete mode 100755 scripts/restart-test-validator.sh diff --git a/Makefile b/Makefile index e2ce4f1..548e337 100644 --- a/Makefile +++ b/Makefile @@ -77,9 +77,6 @@ build-doc-%: test-doc-%: cargo $(nightly) test --doc --all-features --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) -test-%: - SBF_OUT_DIR=$(PWD)/target/deploy cargo $(nightly) test --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) - format-check-js-%: cd $(call make-path,$*) && pnpm install && pnpm format $(ARGS) @@ -87,15 +84,10 @@ lint-js-%: cd $(call make-path,$*) && pnpm install && pnpm lint $(ARGS) test-js-%: - make restart-test-validator cd $(call make-path,$*) && pnpm install && pnpm build && pnpm test $(ARGS) - make stop-test-validator -restart-test-validator: - ./scripts/restart-test-validator.sh - -stop-test-validator: - pkill -f solana-test-validator +test-%: + SBF_OUT_DIR=$(PWD)/target/deploy cargo $(nightly) test --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) generate-idl-%: pnpm generate:idl $(call make-path,$*) $(ARGS) diff --git a/clients/js/README.md b/clients/js/README.md index c59558a..305ce14 100644 --- a/clients/js/README.md +++ b/clients/js/README.md @@ -4,24 +4,19 @@ A generated JavaScript library for the Feature Gate program. ## Getting started -To build and test your JavaScript client from the root of the repository, you may use the following command. +The JS client tests use [LiteSVM](https://github.com/LiteSVM/litesvm) in-process, so no local validator is needed. To build and test your JavaScript client from the root of the repository, you may use the following command. ```sh -pnpm clients:js:test +make test-js-clients-js ``` -This will start a new local validator, if one is not already running, and run the tests for your JavaScript client. +This installs dependencies, builds the client, and runs the test suite. ## Available client scripts. Alternatively, you can go into the client directory and run the tests directly. ```sh -# Build your programs and start the validator. -pnpm programs:build -pnpm validator:restart - -# Go into the client directory and run the tests. cd clients/js pnpm install pnpm build diff --git a/clients/js/package.json b/clients/js/package.json index 453c16c..8b2ec59 100644 --- a/clients/js/package.json +++ b/clients/js/package.json @@ -40,6 +40,8 @@ "devDependencies": { "@solana/eslint-config-solana": "^3.0.3", "@solana/kit": "^6.4.0", + "@solana/kit-plugin-litesvm": "^0.10.0", + "@solana/kit-plugin-signer": "^0.10.0", "@solana/webcrypto-ed25519-polyfill": "3.0.3", "@types/node": "^24", "@typescript-eslint/eslint-plugin": "^7.16.1", diff --git a/clients/js/pnpm-lock.yaml b/clients/js/pnpm-lock.yaml index f001e9e..6cc2540 100644 --- a/clients/js/pnpm-lock.yaml +++ b/clients/js/pnpm-lock.yaml @@ -14,6 +14,12 @@ importers: '@solana/kit': specifier: ^6.4.0 version: 6.9.0(typescript@5.9.3) + '@solana/kit-plugin-litesvm': + specifier: ^0.10.0 + version: 0.10.0(@solana/kit@6.9.0(typescript@5.9.3))(typescript@5.9.3) + '@solana/kit-plugin-signer': + specifier: ^0.10.0 + version: 0.10.0(@solana/kit@6.9.0(typescript@5.9.3)) '@solana/webcrypto-ed25519-polyfill': specifier: 3.0.3 version: 3.0.3(typescript@5.9.3) @@ -521,6 +527,16 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@solana-program/system@0.12.2': + resolution: {integrity: sha512-MaBeOxlvTruQhA7UYkOb3hVTEHPPagOtd+PvTm6a8rGgvEAP0kD4BbC37NceOaR4ABNqdaCmD5OMVRKgrE6KAg==} + peerDependencies: + '@solana/kit': ^6.4.0 + + '@solana-program/token@0.13.0': + resolution: {integrity: sha512-/Apjrd5lwOJGrPB0J5Rv7EBeclvyEBQPAGA85Scm7wBH+GpkbdLDM9uK3TNg8jjFKyWQYai/JtPHbrx7VgFLSg==} + peerDependencies: + '@solana/kit': ^6.5.0 + '@solana/accounts@6.9.0': resolution: {integrity: sha512-g36AJreJrgf9AAjOfbdFHEFUTymBgzbWHoEDElZ+fDKvqBINDiUVKzDApwc7C7kGPMFqQBaoEHnQRxf2IqfKZQ==} engines: {node: '>=20.18.0'} @@ -673,6 +689,21 @@ packages: typescript: optional: true + '@solana/kit-plugin-instruction-plan@0.10.0': + resolution: {integrity: sha512-h99B+1Vp5QWU/M/q0UXlTxKJt781vWw5aAopnbgVCy0F3hNaSDZ/gio126Oz0/cipGOnyaJf3NnV79GvxaEqZA==} + peerDependencies: + '@solana/kit': ^6.8.0 + + '@solana/kit-plugin-litesvm@0.10.0': + resolution: {integrity: sha512-ks/sbFyYJOpa4BrdzEpp2karA66N4b1DFnWol3bui/UzJm+YJZ65xnOyeX9U8PC8uF/TPVkgf6oYvsxyLqrxIw==} + peerDependencies: + '@solana/kit': ^6.8.0 + + '@solana/kit-plugin-signer@0.10.0': + resolution: {integrity: sha512-3Gw3R6nQg/2r2+4cSaUZHj5zdbtb0SuA1mkDMd6uH7B+fdH1Ouxnulv1/sN8J0VxBo7tS+YDQA5t0arxX5gj+w==} + peerDependencies: + '@solana/kit': ^6.8.0 + '@solana/kit@6.9.0': resolution: {integrity: sha512-k7BRz7Akfv8wiRtlCR/xUyDLfuMfYMelMR1+AC5KgwaRRJReDF0BucMLNN1In7WoI+KuWwr1OKv4na/oKpyeAQ==} engines: {node: '>=20.18.0'} @@ -1562,6 +1593,46 @@ packages: linkify-it@5.0.1: resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + litesvm-darwin-arm64@1.1.0: + resolution: {integrity: sha512-SjcivEOOjBk65U6TgIeMJ7CCnHNKQXHx0qf6K6GIFZC1aHTg7ePrEi+WhAQD6VUBMdDHIMCVKC/uXnXPi6EKIw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + litesvm-darwin-x64@1.1.0: + resolution: {integrity: sha512-hTs+eZ9sHVZXhjggpnn/8A/E+Nt/E6Gf8E2ejdWWL9bBQKmq1Y0VcrDpORbIvqqRpTLHXqbxCuH1wQB2C8frJg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + litesvm-linux-arm64-gnu@1.1.0: + resolution: {integrity: sha512-6EjJ6+E+1SUXdJmCyeyhvlKhNncccqQNH241+P8d4E72rE3zuFxeCtLHhusCQk2p/Xau3dBI0qTLogZ1F1IGSA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-arm64-musl@1.1.0: + resolution: {integrity: sha512-mNuBOfX6GnDFT2i/kYPWud7eZGe57dDP0u4lwiSTQPRE0BxQbGZT2aEwX8LTwbonhbc6HSt50LamaZZzK4h4ig==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-x64-gnu@1.1.0: + resolution: {integrity: sha512-Ot8RgUVlMKzKJi2nVDxaHVo0hjB5vtYTomYNIf26mIA32DOy0+dQfwOqUhynhvvSMxN3VFec3r/OtCnk6lRBrw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm-linux-x64-musl@1.1.0: + resolution: {integrity: sha512-6kmneOIsTBSActELRTwxIYVJOVaLm3P6uwlmkqc9BUtDAQ7bRdRmwREWSbM8XxKBGw2LjiUfgRJ5WJGYo8fUFg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm@1.1.0: + resolution: {integrity: sha512-UOlMIEst50gSUyPnC2pGjGLygH8iC/GOqnNXQIHc8iGwD76m44ReeA/0h0vu/AIieZ2zG5/ERLxFV0kdNxkNsA==} + engines: {node: '>= 20'} + load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1789,8 +1860,8 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - semver@7.8.1: - resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + semver@7.8.2: + resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} engines: {node: '>=10'} hasBin: true @@ -2400,6 +2471,15 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@solana-program/system@0.12.2(@solana/kit@6.9.0(typescript@5.9.3))': + dependencies: + '@solana/kit': 6.9.0(typescript@5.9.3) + + '@solana-program/token@0.13.0(@solana/kit@6.9.0(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.12.2(@solana/kit@6.9.0(typescript@5.9.3)) + '@solana/kit': 6.9.0(typescript@5.9.3) + '@solana/accounts@6.9.0(typescript@5.9.3)': dependencies: '@solana/addresses': 6.9.0(typescript@5.9.3) @@ -2540,6 +2620,25 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/kit-plugin-instruction-plan@0.10.0(@solana/kit@6.9.0(typescript@5.9.3))': + dependencies: + '@solana/kit': 6.9.0(typescript@5.9.3) + + '@solana/kit-plugin-litesvm@0.10.0(@solana/kit@6.9.0(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@solana/kit': 6.9.0(typescript@5.9.3) + '@solana/kit-plugin-instruction-plan': 0.10.0(@solana/kit@6.9.0(typescript@5.9.3)) + litesvm: 1.1.0(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@solana/kit-plugin-signer@0.10.0(@solana/kit@6.9.0(typescript@5.9.3))': + dependencies: + '@solana/kit': 6.9.0(typescript@5.9.3) + '@solana/kit@6.9.0(typescript@5.9.3)': dependencies: '@solana/accounts': 6.9.0(typescript@5.9.3) @@ -2988,7 +3087,7 @@ snapshots: debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.8.1 + semver: 7.8.2 tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -3003,7 +3102,7 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.8.1 + semver: 7.8.2 ts-api-utils: 1.4.3(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -3020,7 +3119,7 @@ snapshots: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.3) eslint: 8.57.1 eslint-scope: 5.1.1 - semver: 7.8.1 + semver: 7.8.2 transitivePeerDependencies: - supports-color - typescript @@ -3567,6 +3666,42 @@ snapshots: dependencies: uc.micro: 2.1.0 + litesvm-darwin-arm64@1.1.0: + optional: true + + litesvm-darwin-x64@1.1.0: + optional: true + + litesvm-linux-arm64-gnu@1.1.0: + optional: true + + litesvm-linux-arm64-musl@1.1.0: + optional: true + + litesvm-linux-x64-gnu@1.1.0: + optional: true + + litesvm-linux-x64-musl@1.1.0: + optional: true + + litesvm@1.1.0(typescript@5.9.3): + dependencies: + '@solana-program/system': 0.12.2(@solana/kit@6.9.0(typescript@5.9.3)) + '@solana-program/token': 0.13.0(@solana/kit@6.9.0(typescript@5.9.3)) + '@solana/kit': 6.9.0(typescript@5.9.3) + optionalDependencies: + litesvm-darwin-arm64: 1.1.0 + litesvm-darwin-x64: 1.1.0 + litesvm-linux-arm64-gnu: 1.1.0 + litesvm-linux-arm64-musl: 1.1.0 + litesvm-linux-x64-gnu: 1.1.0 + litesvm-linux-x64-musl: 1.1.0 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + load-tsconfig@0.2.5: {} locate-path@6.0.0: @@ -3794,7 +3929,7 @@ snapshots: dependencies: queue-microtask: 1.2.3 - semver@7.8.1: {} + semver@7.8.2: {} shebang-command@2.0.0: dependencies: diff --git a/clients/js/test/_setup.ts b/clients/js/test/_setup.ts new file mode 100644 index 0000000..f1298e5 --- /dev/null +++ b/clients/js/test/_setup.ts @@ -0,0 +1,38 @@ +import path from 'node:path'; + +import { createClient, lamports } from '@solana/kit'; +import { litesvm } from '@solana/kit-plugin-litesvm'; +import { airdropSigner, generatedSigner } from '@solana/kit-plugin-signer'; + +import { + SOLANA_FEATURE_GATE_PROGRAM_ADDRESS, + solanaFeatureGateProgram, +} from '../src'; + +const SOLANA_FEATURE_GATE_BINARY_PATH = path.resolve( + __dirname, + '..', + '..', + '..', + 'target', + 'deploy', + 'solana_feature_gate_program.so' +); + +export const createTestClient = () => { + return createClient() + .use(generatedSigner()) + .use(litesvm()) + .use(airdropSigner(lamports(1_000_000_000n))) + .use((client) => { + // Load the feature-gate program into the LiteSVM instance from its + // compiled `.so` file. This must run after the `litesvm()` plugin so + // that `client.svm` is available. + client.svm.addProgramFromFile( + SOLANA_FEATURE_GATE_PROGRAM_ADDRESS, + SOLANA_FEATURE_GATE_BINARY_PATH + ); + return client; + }) + .use(solanaFeatureGateProgram()); +}; diff --git a/clients/js/test/example.test.ts b/clients/js/test/example.test.ts index 70f4514..78e4d97 100644 --- a/clients/js/test/example.test.ts +++ b/clients/js/test/example.test.ts @@ -1,5 +1,17 @@ import { expect, it } from 'vitest'; -it('runs an example test', () => { - expect(true).toBe(true); +import { createTestClient } from './_setup'; + +it('sets up a LiteSVM client with the feature-gate program', async () => { + // Given a test client whose payer is funded with SOL. + const client = await createTestClient(); + + // Then the client exposes the feature-gate program plugin. + expect(client.solanaFeatureGate).toBeDefined(); + + // And the payer was funded via LiteSVM. + const { value: balance } = await client.rpc + .getBalance(client.payer.address) + .send(); + expect(balance).toBe(1_000_000_000n); }); diff --git a/scripts/restart-test-validator.sh b/scripts/restart-test-validator.sh deleted file mode 100755 index 985c3c2..0000000 --- a/scripts/restart-test-validator.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -here="$(dirname "$0")" -src_root="$(readlink -f "${here}/..")" -cd "${src_root}" - -ARGS=( - -r - -q - --bpf-program Feature111111111111111111111111111111111111 ./target/deploy/solana_feature_gate_program.so -) -PORT=8899 -PID=$(lsof -t -i:$PORT) - -if [ -n "$PID" ]; then - echo "Detected test validator running on PID $PID. Restarting..." - kill "$PID" - sleep 1 -fi - -echo "Starting Solana test validator..." -solana-test-validator "${ARGS[@]}" & -VALIDATOR_PID=$! - -# Wait for test validator to move past slot 0. -echo -n "Waiting for validator to stabilize" -for i in {1..8}; do - if ! kill -0 "$VALIDATOR_PID" 2>/dev/null; then - echo -e "\nTest validator exited early." - exit 1 - fi - - SLOT=$(solana slot -ul 2>/dev/null) - if [[ "$SLOT" =~ ^[0-9]+$ ]] && [ "$SLOT" -gt 8 ]; then - echo -e "\nTest validator is ready. Slot: $SLOT" - exit 0 - fi - - echo -n "." - sleep 1 -done - -echo -e "\nTimed out waiting for test validator to stabilize." -exit 1