الاختبار المبني على الوقت

يوضّح كيفية استخدام WarpToSlot وSetClock لاختبار منطق البرنامج المعتمد على الوقت.

معالجة الساعة الأساسية

package mytest
import (
"testing"
litesvm "github.com/LiteSVM/litesvm-go"
)
func TestClockWarp(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
// Read the initial clock.
initial, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("initial state:")
t.Logf(" slot: %d", initial.Slot)
t.Logf(" epoch: %d", initial.Epoch)
t.Logf(" unix timestamp: %d", initial.UnixTimestamp)
// Warp to slot 1_000.
if err := svm.WarpToSlot(1_000); err != nil {
t.Fatal(err)
}
after, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("after warpToSlot(1000):")
t.Logf(" slot: %d", after.Slot)
t.Logf(" epoch: %d", after.Epoch)
// Warp further.
if err := svm.WarpToSlot(100_000); err != nil {
t.Fatal(err)
}
after2, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("after warpToSlot(100000):")
t.Logf(" slot: %d", after2.Slot)
t.Logf(" epoch: %d", after2.Epoch)
}

اختبار المنطق المقيّد بالوقت

package mytest
import (
"encoding/binary"
"testing"
litesvm "github.com/LiteSVM/litesvm-go"
solana "github.com/gagliardetto/solana-go"
)
func TestTimeLockedVault(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
// Setters are infallible in practice; discarding the return keeps the
// example readable.
_ = svm.SetSigverify(false)
_ = svm.SetBlockhashCheck(false)
_ = svm.SetSysvars()
// Fund a payer.
priv, err := solana.NewRandomPrivateKey()
if err != nil {
t.Fatal(err)
}
payer := priv.PublicKey()
if err := svm.Airdrop(payer, 10_000_000_000); err != nil {
t.Fatal(err)
}
// Set up a time-locked vault account.
// Layout: [u8 discriminator, u64 unlock_slot, u64 amount]
const unlockSlot = uint64(10_000)
const lockedAmount = uint64(5_000_000_000)
vaultData := make([]byte, 1+8+8)
vaultData[0] = 0x01
binary.LittleEndian.PutUint64(vaultData[1:9], unlockSlot)
binary.LittleEndian.PutUint64(vaultData[9:17], lockedAmount)
programID := solana.NewWallet().PublicKey()
vaultAddr := solana.NewWallet().PublicKey()
minBalance, err := svm.MinimumBalanceForRentExemption(len(vaultData))
if err != nil {
t.Fatal(err)
}
acct, err := litesvm.NewAccount(minBalance+lockedAmount, vaultData, programID, false, 0)
if err != nil {
t.Fatal(err)
}
defer acct.Close()
if err := svm.SetAccount(vaultAddr, acct); err != nil {
t.Fatal(err)
}
t.Logf("vault created: unlock slot=%d, locked=%.3f SOL",
unlockSlot, float64(lockedAmount)/1e9)
// --- Test 1: Before unlock ---
cBefore, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("current slot: %d", cBefore.Slot)
// In a real test you would build a withdraw instruction here and assert
// it fails because the current slot < unlock_slot.
t.Log("withdrawal attempt: EXPECTED TO FAIL (too early)")
// --- Test 2: After unlock ---
if err := svm.WarpToSlot(unlockSlot + 100); err != nil {
t.Fatal(err)
}
cAfter, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("current slot: %d", cAfter.Slot)
// Build and submit the withdraw instruction; assert it succeeds.
t.Log("withdrawal attempt: EXPECTED TO SUCCEED (after unlock)")
// Verify vault state.
updated := svm.GetAccount(vaultAddr)
if updated == nil {
t.Fatal("vault missing")
}
defer updated.Close()
t.Logf("vault balance: %d lamports", updated.Lamports())
_ = payer // payer would be referenced when building the withdraw ix
}

قراءة جدول epoch

func TestReadEpochSchedule(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
es, err := svm.EpochSchedule()
if err != nil {
t.Fatal(err)
}
t.Logf("slots per epoch: %d", es.SlotsPerEpoch)
t.Logf("first normal epoch: %d", es.FirstNormalEpoch)
t.Logf("first normal slot: %d", es.FirstNormalSlot)
}

WarpToSlot يُقدّم حقل Slot في الساعة لكنه لا يُحدّث Epoch أو UnixTimestamp تلقائيًا. استخدم SetClock للتحكم الكامل في حقول الساعة.

معالجة الساعة يدويًا

عندما يقرأ برنامجك Epoch أو UnixTimestamp من متغير النظام Clock، قم بتعيين تلك الحقول مباشرةً باستخدام SetClock:

func TestSetClock(t *testing.T) {
svm, err := litesvm.New()
if err != nil {
t.Fatal(err)
}
defer svm.Close()
c, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
c.Slot = 50_000
c.Epoch = 5
c.UnixTimestamp = 1_700_000_000
if err := svm.SetClock(c); err != nil {
t.Fatal(err)
}
after, err := svm.Clock()
if err != nil {
t.Fatal(err)
}
t.Logf("slot: %d", after.Slot) // 50000
t.Logf("epoch: %d", after.Epoch) // 5
t.Logf("unix: %d", after.UnixTimestamp) // 1700000000
}

يعكس هذا نمط set_sysvar(&clock) في Rust. استخدم WarpToSlot للتقدم البسيط في slot، وSetClock عندما تحتاج إلى التحكم في epoch أو الطابع الزمني.

حالات الاستخدام

معالجة الوقت مفيدة لاختبار:

السيناريوالنهج
استحقاق الرموزالتقدم إلى ما بعد حد الاستحقاق / المراحل
نهايات المزاداتالتقدم إلى ما بعد slot انتهاء المزاد
مكافآت التخزيناستخدام SetClock لتعيين epoch
السحوبات المقيّدة بالوقتالتقدم إلى ما بعد slot إلغاء القفل
تحديد المعدلالتقدم بين الفترات المسموح بها
الأوامر منتهية الصلاحيةالتقدم إلى ما بعد انتهاء صلاحية الأمر

WarpToSlot يغيّر slot فقط. قد لا يتحدث الطابع الزمني Unix بشكل متناسب - استخدم SetClock إذا كان برنامجك يقرأ UnixTimestamp.

النقاط الرئيسية

  1. متغيرات النظام الافتراضية مفعّلة - litesvm.New() تُعيد مقبضًا مع تهيئة متغيرات النظام؛ استدعِ SetSysvars() لإعادة الضبط إلى الافتراضيات إذا كنت قد عدّلتها.
  2. التقدم للأمام - WarpToSlot(slot) يُقدّم حقل slot فقط.
  3. التحكم الكامل - SetClock(Clock{...}) يتيح لك تعيين Epoch وUnixTimestamp والحقول الأخرى.
  4. القراءة - svm.Clock() يقرأ القيم الحالية.
  5. نمط الاختبار - اختبر قبل حدود الوقت وبعدها؛ توقع الفشل على أحد الجانبين والنجاح على الجانب الآخر.

Is this page helpful?

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

تعديل الصفحة