Testare un programma Solana personalizzato da Go significa caricare il file .so compilato
nell'SVM, configurare gli account che si aspetta e inviare istruzioni
contro di esso. litesvm-go gestisce tutto questo in-process - nessun validator necessario.
Caricamento di un Programma
package mytestimport ("os""testing"litesvm "github.com/LiteSVM/litesvm-go"solana "github.com/gagliardetto/solana-go")func TestLoadProgram(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()// Configure for program testing. Setters are infallible in practice;// discarding the return keeps the example readable._ = svm.SetSigverify(false)_ = svm.SetBlockhashCheck(false)_ = svm.SetSysvars()_ = svm.SetBuiltins()// Load program from .so file.programID := solana.NewWallet().PublicKey()soPath := "./target/deploy/my_program.so"if _, err := os.Stat(soPath); err != nil {t.Skipf("program file not found: %s (build with: cargo build-sbf)", soPath)}if err := svm.AddProgramFromFile(programID, soPath); err != nil {t.Fatal(err)}t.Logf("program loaded: %s", programID)// Verify the program account.acct := svm.GetAccount(programID)if acct == nil {t.Fatal("program account missing after load")}defer acct.Close()t.Logf(" executable: %v", acct.Executable())t.Logf(" data size: %d bytes", len(acct.Data()))}
Configurazione Completa del Test del Programma
package mytestimport ("os""testing"litesvm "github.com/LiteSVM/litesvm-go"solana "github.com/gagliardetto/solana-go")func TestProgramEndToEnd(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()// Wide-open config: faster tests, less plumbing. Setters are infallible// in practice; discarding the return keeps the example readable._ = svm.SetSigverify(false)_ = svm.SetBlockhashCheck(false)_ = svm.SetSysvars()_ = svm.SetBuiltins()_ = svm.SetPrecompiles()_ = svm.SetTransactionHistory(100)// Load the program.programID := solana.NewWallet().PublicKey()soPath := "./target/deploy/my_program.so"if _, err := os.Stat(soPath); err != nil {t.Skipf("missing %s", soPath)}if err := svm.AddProgramFromFile(programID, soPath); err != nil {t.Fatal(err)}// Seed any data accounts the program expects.dataAddr := solana.NewWallet().PublicKey()dataPayload := make([]byte, 100)rent, err := svm.MinimumBalanceForRentExemption(len(dataPayload))if err != nil {t.Fatal(err)}acct, err := litesvm.NewAccount(rent, dataPayload, programID, false, 0)if err != nil {t.Fatal(err)}defer acct.Close()if err := svm.SetAccount(dataAddr, acct); err != nil {t.Fatal(err)}// Fund a payer that will sign the test instruction.priv, err := solana.NewRandomPrivateKey()if err != nil {t.Fatal(err)}payer := priv.PublicKey()if err := svm.Airdrop(payer, 1_000_000_000); err != nil {t.Fatal(err)}// Build an instruction against the program.bh, err := svm.LatestBlockhash()if err != nil {t.Fatal(err)}ix := &solana.GenericInstruction{ProgID: programID,AccountValues: solana.AccountMetaSlice{{PublicKey: dataAddr, IsWritable: true, IsSigner: false},{PublicKey: payer, IsWritable: true, IsSigner: true},},DataBytes: []byte{0x01 /* discriminator + args */},}tx, err := solana.NewTransaction([]solana.Instruction{ix},bh,solana.TransactionPayer(payer),)if err != nil {t.Fatal(err)}if _, err := tx.Sign(func(k solana.PublicKey) *solana.PrivateKey {if k.Equals(payer) {return &priv}return nil}); err != nil {t.Fatal(err)}txBytes, err := tx.MarshalBinary()if err != nil {t.Fatal(err)}// Execute and inspect.out, err := svm.SendLegacyTransaction(txBytes)if err != nil {t.Fatal(err)}defer out.Close()if !out.IsOk() {t.Fatalf("program failed: %s\nlogs: %v", out.Error(), out.Logs())}t.Logf("logs: %v", out.Logs())t.Logf("compute: %d CU", out.ComputeUnits())// Verify state changes by reading the data account back.updated := svm.GetAccount(dataAddr)if updated == nil {t.Fatal("data account missing after execution")}defer updated.Close()// Decode `updated.Data()` and assert as needed.}
Utilizzo dei Programmi Predefiniti
Per i test che richiedono programmi standard (SPL Token, Memo, ...):
svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()if err := svm.SetDefaultPrograms(); err != nil { // SPL Token, Memo, etc.t.Fatal(err)}if err := svm.WithNativeMints(); err != nil { // wrapped-SOL mintt.Fatal(err)}
Compila il tuo programma Solana con cargo build-sbf per generare il file .so.
Lo schema è sempre lo stesso:
- Configurazione - abilita sysvars, builtin e precompilazioni secondo necessità.
- Caricamento del programma -
AddProgramFromFile(oAddProgramper byte in memoria). - Configurazione degli account - precompila gli account dati con
SetAccount. - Invio - costruisci l'istruzione con
solana-go, serializza e invia tramiteSendLegacyTransaction. - Verifica - rileggi gli account con
GetAccounte verifica log, unità di calcolo e stato finale.
Una volta che questo schema di base funziona, amplialo aggiungendo più istruzioni, più account e un helper per il codice ripetitivo.
Is this page helpful?