اختبار البرامج

يعني اختبار برنامج سولانا مخصص من Go تحميل ملف .so المُجمَّع الخاص به إلى SVM، وإعداد الحسابات التي يتوقعها، وإرسال التعليمات إليه. litesvm-go يتولى كل هذا داخل العملية ذاتها - دون الحاجة إلى validator.

تحميل برنامج

package mytest
import (
"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()))
}

إعداد اختبار برنامج كامل

package mytest
import (
"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.
}

استخدام البرامج الافتراضية

للاختبارات التي تحتاج إلى برامج قياسية (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 mint
t.Fatal(err)
}

قم ببناء برنامج سولانا الخاص بك باستخدام cargo build-sbf لإنشاء ملف .so.

النمط دائمًا واحد:

  1. الإعداد - تفعيل sysvars وbuiltins وprecompiles حسب الحاجة.
  2. تحميل البرنامج - AddProgramFromFile (أو AddProgram للبايتات الموجودة في الذاكرة).
  3. إعداد الحسابات - ملء حسابات البيانات مسبقًا باستخدام SetAccount.
  4. الإرسال - بناء التعليمة باستخدام solana-go، وتحويلها إلى تنسيق ثنائي، وإرسالها عبر SendLegacyTransaction.
  5. التحقق - قراءة الحسابات مجددًا باستخدام GetAccount والتحقق من السجلات ووحدات الحوسبة والحالة النهائية.

بمجرد أن يعمل هذا الهيكل الأساسي، يمكنك التوسع بإضافة المزيد من التعليمات والحسابات ودالة مساعدة للتعامل مع الأجزاء المتكررة.

Is this page helpful?

جدول المحتويات

تعديل الصفحة