From 7853408bc8252161a718dae25dbe5387fd24d242 Mon Sep 17 00:00:00 2001 From: 4rjunc Date: Mon, 1 Jun 2026 17:25:35 +0530 Subject: [PATCH] test: add litesvm ts testcases --- .../native/package.json | 5 +- .../native/pnpm-lock.yaml | 83 +++++++++++++ .../native/tests/test.ts | 111 +++++++++++++----- 3 files changed, 170 insertions(+), 29 deletions(-) diff --git a/basics/cross-program-invocation/native/package.json b/basics/cross-program-invocation/native/package.json index a6cdf69c9..10512f166 100644 --- a/basics/cross-program-invocation/native/package.json +++ b/basics/cross-program-invocation/native/package.json @@ -2,7 +2,7 @@ "type": "module", "scripts": { "test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts", - "build-and-test": "cargo build-sbf --sbf-out-dir=./tests/fixtures && pnpm test", + "build-and-test": "cargo build-sbf --sbf-out-dir=./tests/fixtures --manifest-path=programs/lever/Cargo.toml && cargo build-sbf --sbf-out-dir=./tests/fixtures --manifest-path=programs/hand/Cargo.toml && pnpm test", "build": "cargo build-sbf --sbf-out-dir=./program/target/so", "deploy": "solana program deploy ./program/target/so/program.so" }, @@ -10,7 +10,8 @@ "@solana/web3.js": "^1.98.4", "borsh": "^2.0.0", "buffer": "^6.0.3", - "fs": "^0.0.1-security" + "fs": "^0.0.1-security", + "litesvm": "0.8.0" }, "devDependencies": { "@types/bn.js": "^5.1.0", diff --git a/basics/cross-program-invocation/native/pnpm-lock.yaml b/basics/cross-program-invocation/native/pnpm-lock.yaml index 6206710d1..4f50b32f3 100644 --- a/basics/cross-program-invocation/native/pnpm-lock.yaml +++ b/basics/cross-program-invocation/native/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: fs: specifier: ^0.0.1-security version: 0.0.1-security + litesvm: + specifier: 0.8.0 + version: 0.8.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) devDependencies: '@types/bn.js': specifier: ^5.1.0 @@ -293,6 +296,9 @@ packages: fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + fastestsmallesttextencoderdecoder@1.0.22: + resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -408,6 +414,46 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + litesvm-darwin-arm64@0.8.0: + resolution: {integrity: sha512-XYa0oOA7FVbMYKvIDbBznWUjkHQD8J/fn5kPMBSX4QWf4yfFda/+L4JHeSngx+dBCM0LyEyLeakVrnmdR1KYPQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + litesvm-darwin-x64@0.8.0: + resolution: {integrity: sha512-lrxU6VXEqY7MHHIskdjB88xM9OiGR2ZcXf+fMESnetCk6rVUM6Llfb386wsMiqvi1+DowJwShxluBsCutWo2Sw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + litesvm-linux-arm64-gnu@0.8.0: + resolution: {integrity: sha512-D0pdYTQkoibPCfza2x1urSjIpHdwHVagHs0fBMHpzxSEvXND2qqDR3jXYf6xcE0sAq0knXjQmGz7WzP/HhFeFw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-arm64-musl@0.8.0: + resolution: {integrity: sha512-g7vgYPJZC6cT1gaWA1M2SbZu+Sngs9FuxsybDSE1Mmelmaejlguf7X/ymUuhehaSjEY+QSi+ydWtwzgE95JL0w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + litesvm-linux-x64-gnu@0.8.0: + resolution: {integrity: sha512-nn599p+XuOJoQN2XTSaY4Yz1ZqYxLNUbvt30kImuRUs2ZlIv5FANzM+VUsZgSzWV0phW8m6stX3mpdVv0iIuFQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm-linux-x64-musl@0.8.0: + resolution: {integrity: sha512-UH4v1GM4hNOjEIzx/dBZQ5mxWGFOd85qs7IBou070dLjH0vdZMh4avQCQyc127+A7aRfdc8+wK7t4SEIn4EEgw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + litesvm@0.8.0: + resolution: {integrity: sha512-P0Ly11FSr1f77bKwaRf0VQQZYdsw6Oi3OLKBxgBN5iJcB7W7lTjFB1tnVcJqdn1NMz6upqU/04rZFCC/JfYBWg==} + engines: {node: '>= 20'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -923,6 +969,8 @@ snapshots: fast-stable-stringify@1.0.0: {} + fastestsmallesttextencoderdecoder@1.0.22: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -1030,6 +1078,41 @@ snapshots: minimist: 1.2.8 optional: true + litesvm-darwin-arm64@0.8.0: + optional: true + + litesvm-darwin-x64@0.8.0: + optional: true + + litesvm-linux-arm64-gnu@0.8.0: + optional: true + + litesvm-linux-arm64-musl@0.8.0: + optional: true + + litesvm-linux-x64-gnu@0.8.0: + optional: true + + litesvm-linux-x64-musl@0.8.0: + optional: true + + litesvm@0.8.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10): + dependencies: + '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + fastestsmallesttextencoderdecoder: 1.0.22 + optionalDependencies: + litesvm-darwin-arm64: 0.8.0 + litesvm-darwin-x64: 0.8.0 + litesvm-linux-arm64-gnu: 0.8.0 + litesvm-linux-arm64-musl: 0.8.0 + litesvm-linux-x64-gnu: 0.8.0 + litesvm-linux-x64-musl: 0.8.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + locate-path@6.0.0: dependencies: p-locate: 5.0.0 diff --git a/basics/cross-program-invocation/native/tests/test.ts b/basics/cross-program-invocation/native/tests/test.ts index c9dc4249b..f18356a3f 100644 --- a/basics/cross-program-invocation/native/tests/test.ts +++ b/basics/cross-program-invocation/native/tests/test.ts @@ -1,70 +1,127 @@ import { Buffer } from "node:buffer"; +import { LiteSVM, TransactionMetadata } from "litesvm"; import { - Connection, Keypair, SystemProgram, - sendAndConfirmTransaction, Transaction, TransactionInstruction, } from "@solana/web3.js"; import * as borsh from "borsh"; +import * as path from "path"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import { describe, it, before } from "node:test"; -function createKeypairFromFile(path: string): Keypair { - return Keypair.fromSecretKey(Uint8Array.from(JSON.parse(require("node:fs").readFileSync(path, "utf-8")))); +const PowerStatusSchema = { struct: { is_on: "u8" } }; +const SetPowerStatusSchema = { struct: { name: "string" } }; + +function borshSerialize(schema: borsh.Schema, data: object): Buffer { + return Buffer.from(borsh.serialize(schema, data)); } -describe("CPI Example", () => { - const connection = new Connection("http://localhost:8899", "confirmed"); - const payer = createKeypairFromFile(`${require("node:os").homedir()}/.config/solana/id.json`); - const hand = createKeypairFromFile("./target/so/hand-keypair.json"); - const lever = createKeypairFromFile("./target/so/lever-keypair.json"); +describe("Native CPI Example", () => { + let svm: LiteSVM; + let payer: Keypair; + let handProgramId: Keypair; + let leverProgramId: Keypair; + let powerAccount: Keypair; + + before(() => { + svm = new LiteSVM(); + payer = Keypair.generate(); + + handProgramId = Keypair.fromSecretKey( + Uint8Array.from( + JSON.parse(fs.readFileSync("./tests/fixtures/cross_program_invocatio_native_hand-keypair.json", "utf-8")) + ) + ); + leverProgramId = Keypair.fromSecretKey( + Uint8Array.from( + JSON.parse(fs.readFileSync("./tests/fixtures/cross_program_invocatio_native_lever-keypair.json", "utf-8")) + ) + ); + + svm.airdrop(payer.publicKey, BigInt(10 * 1_000_000_000)); - const PowerStatusSchema = { struct: { is_on: "u8" } }; - const SetPowerStatusSchema = { struct: { name: "string" } }; + const native_hand = path.join("./tests/fixtures", "cross_program_invocatio_native_hand.so"); + const native_lever = path.join("./tests/fixtures", "cross_program_invocatio_native_lever.so"); - function borshSerialize(schema: borsh.Schema, data: object): Buffer { - return Buffer.from(borsh.serialize(schema, data)); - } + svm.addProgramFromFile( + handProgramId.publicKey, + native_hand + ); + svm.addProgramFromFile( + leverProgramId.publicKey, + native_lever + ); - const powerAccount = Keypair.generate(); + powerAccount = Keypair.generate(); + }); - it("Initialize the lever!", async () => { + it("Initialize the lever!", () => { const ix = new TransactionInstruction({ keys: [ { pubkey: powerAccount.publicKey, isSigner: true, isWritable: true }, { pubkey: payer.publicKey, isSigner: true, isWritable: true }, { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, ], - programId: lever.publicKey, - data: borshSerialize(PowerStatusSchema, { is_on: true }), + programId: leverProgramId.publicKey, + data: borshSerialize(PowerStatusSchema, { is_on: 1 }), }); - await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer, powerAccount]); + const tx = new Transaction(); + tx.recentBlockhash = svm.latestBlockhash(); + tx.feePayer = payer.publicKey; + tx.add(ix); + tx.sign(payer, powerAccount); + + const res = svm.sendTransaction(tx); + if (!(res instanceof TransactionMetadata)) { + throw new Error(`Transaction failed: ${res.meta().toString()}`); + } }); - it("Pull the lever!", async () => { + it("Pull the lever!", () => { const ix = new TransactionInstruction({ keys: [ { pubkey: powerAccount.publicKey, isSigner: false, isWritable: true }, - { pubkey: lever.publicKey, isSigner: false, isWritable: false }, + { pubkey: leverProgramId.publicKey, isSigner: false, isWritable: false }, ], - programId: hand.publicKey, + programId: handProgramId.publicKey, data: borshSerialize(SetPowerStatusSchema, { name: "Chris" }), }); - await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer]); + const tx = new Transaction(); + tx.recentBlockhash = svm.latestBlockhash(); + tx.feePayer = payer.publicKey; + tx.add(ix); + tx.sign(payer); + + const res = svm.sendTransaction(tx); + if (!(res instanceof TransactionMetadata)) { + throw new Error(`Transaction failed: ${res.meta().toString()}`); + } }); - it("Pull it again!", async () => { + it("Pull it again!", () => { const ix = new TransactionInstruction({ keys: [ { pubkey: powerAccount.publicKey, isSigner: false, isWritable: true }, - { pubkey: lever.publicKey, isSigner: false, isWritable: false }, + { pubkey: leverProgramId.publicKey, isSigner: false, isWritable: false }, ], - programId: hand.publicKey, + programId: handProgramId.publicKey, data: borshSerialize(SetPowerStatusSchema, { name: "Ashley" }), }); - await sendAndConfirmTransaction(connection, new Transaction().add(ix), [payer]); + const tx = new Transaction(); + tx.recentBlockhash = svm.latestBlockhash(); + tx.feePayer = payer.publicKey; + tx.add(ix); + tx.sign(payer); + + const res = svm.sendTransaction(tx); + if (!(res instanceof TransactionMetadata)) { + throw new Error(`Transaction failed: ${res.meta().toString()}`); + } }); });