Tester un programme Solana personnalisé depuis Go consiste à charger son fichier .so compilé
dans la SVM, à configurer les comptes attendus et à lui envoyer des instructions.
litesvm-go gère tout cela en cours de processus - aucun validator nécessaire.
Chargement d'un programme
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()))}
Configuration complète d'un test de programme
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.}
Utiliser les programmes par défaut
Pour les tests nécessitant des programmes 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)}
Compilez votre programme Solana avec cargo build-sbf pour générer le fichier .so.
Le schéma est toujours le même :
- Configuration - activer les sysvars, les builtins et les précompilations selon les besoins.
- Chargement du programme -
AddProgramFromFile(ouAddProgrampour les octets en mémoire). - Configuration des comptes - pré-remplir les comptes de données avec
SetAccount. - Envoi - construire l'instruction avec
solana-go, la sérialiser et la soumettre viaSendLegacyTransaction. - Vérification - relire les comptes avec
GetAccountet vérifier les logs, les unités de calcul et l'état final.
Une fois ce squelette opérationnel, montez en charge en ajoutant davantage d'instructions, de comptes et un utilitaire pour le code répétitif.
Is this page helpful?