Ein benutzerdefiniertes Solana-Programm aus Go heraus zu testen bedeutet, die kompilierte .so-Datei in die SVM zu laden, die erwarteten Konten einzurichten und Anweisungen dagegen zu senden. litesvm-go übernimmt all das prozessintern – kein validator erforderlich.
Ein Programm laden
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()))}
Vollständiges Programm-Test-Setup
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.}
Standardprogramme verwenden
Für Tests, die Standardprogramme benötigen (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)}
Erstellen Sie Ihr Solana-Programm mit cargo build-sbf, um die .so-Datei zu generieren.
Das Muster ist immer dasselbe:
- Konfiguration – Sysvars, Builtins und Precompiles nach Bedarf aktivieren.
- Programm laden –
AddProgramFromFile(oderAddProgramfür In-Memory-Bytes). - Konten einrichten – Datenkonten mit
SetAccountvorbelegen. - Senden – Anweisungen mit
solana-goerstellen, marshallen und überSendLegacyTransactionübermitteln. - Verifizierung – Konten mit
GetAccountauslesen und Logs, Compute Units sowie den Endzustand prüfen.
Sobald dieses Grundgerüst funktioniert, kann es durch weitere Anweisungen, mehr Konten und einen Helfer für den Boilerplate-Code erweitert werden.
Is this page helpful?