diff --git a/gov/staker/clean_delegation_stat_history_test.gno b/gov/staker/clean_delegation_stat_history_test.gno new file mode 100644 index 000000000..1d05132bd --- /dev/null +++ b/gov/staker/clean_delegation_stat_history_test.gno @@ -0,0 +1,84 @@ +package staker + +import ( + "std" + "testing" + + "gno.land/p/demo/avl" + "gno.land/p/demo/testutils" +) + +var ( + testAddr1 = testutils.TestAddress("test1") + testAddr = testutils.TestAddress("test") +) + +type mockEnv struct { + height uint64 + isAdmin bool +} + +func (m *mockEnv) GetHeight() int64 { + return int64(m.height) +} + +func (m *mockEnv) IsAdmin() bool { + return m.isAdmin +} + +func TestCleanDelegationStatHistory(t *testing.T) { + mock := &mockEnv{height: 1000, isAdmin: true} + std.TestSetOrigCaller(testAddr1) + delegationSnapShotHistory = avl.NewTree() + + addr := testAddr.String() + history := []DelegationSnapShotHistory{ + {updatedBlock: 500}, // Old + {updatedBlock: 900}, // Within threshold + {updatedBlock: 950}, // Latest + } + delegationSnapShotHistory.Set(addr, history) + + tests := []struct { + name string + setupHeight uint64 + lastCleaned uint64 + threshold int64 + expectedLen int + }{ + { + name: "no clean needed", + setupHeight: 1000, + lastCleaned: 999, + threshold: 100, + expectedLen: 3, + }, + { + name: "clean old records", + setupHeight: 1000, + lastCleaned: 800, + threshold: 100, + expectedLen: 3, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + mock.height = tc.setupHeight + lastCleanedHeight = tc.lastCleaned + thresholdVotingWeightBlockHeight = tc.threshold + + cleanDelegationStatHistory() + + value, exists := delegationSnapShotHistory.Get(addr) + if !exists { + t.Fatal("history should exist") + } + + history := value.([]DelegationSnapShotHistory) + if len(history) != tc.expectedLen { + t.Errorf("expected history length %d, got %d", tc.expectedLen, len(history)) + } + }) + } +} diff --git a/gov/staker/delegate_undelegate_test.gno b/gov/staker/delegate_undelegate_test.gno index 3498b5cb5..7e1c5e2b1 100644 --- a/gov/staker/delegate_undelegate_test.gno +++ b/gov/staker/delegate_undelegate_test.gno @@ -3,101 +3,109 @@ package staker import ( "std" "testing" - "time" "gno.land/p/demo/avl" "gno.land/p/demo/testutils" ) -func TestGetDelegatedCumulative(t *testing.T) { - delegationSnapShotHistory = avl.NewTree() - - addr1 := testutils.TestAddress("test1") - now := uint64(time.Now().Unix()) - - tests := []struct { - name string - setupHistory []DelegationSnapShotHistory - delegator std.Address - endTimestamp uint64 - expectAmount uint64 - expectPanic bool - }{ - { - name: "no history returns zero", - delegator: addr1, - endTimestamp: now, - expectAmount: 0, - }, - { - name: "single history before timestamp", - setupHistory: []DelegationSnapShotHistory{ - { - to: addr1, - amount: 100, - updatedBlock: 1, - updatedAt: now - 100, - }, - }, - delegator: addr1, - endTimestamp: now, - expectAmount: 100, - }, - { - name: "multiple histories returns latest before timestamp", - setupHistory: []DelegationSnapShotHistory{ - { - to: addr1, - amount: 100, - updatedBlock: 1, - updatedAt: now - 200, - }, - { - to: addr1, - amount: 150, - updatedBlock: 2, - updatedAt: now - 100, - }, - { - to: addr1, - amount: 200, - updatedBlock: 3, - updatedAt: now + 100, // Future update - }, - }, - delegator: addr1, - endTimestamp: now, - expectAmount: 150, - }, - { - name: "future timestamp panics", - delegator: addr1, - endTimestamp: now + 1000, - expectPanic: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - delegationSnapShotHistory = avl.NewTree() - - if len(tt.setupHistory) > 0 { - delegationSnapShotHistory.Set(tt.delegator.String(), tt.setupHistory) - } +func TestDelegate(t *testing.T) { + t.Skip("Must running separately. because this test depends on TestUndelegate's state") + std.TestSetOrigCaller(testAddr1) + resetState() + + addr1 := testAddr1 + + t.Run("success - first delegation", func(t *testing.T) { + delegate(addr1, 100) + + if totalDelegated != 100 { + t.Errorf("expected totalDelegated 100, got %d", totalDelegated) + } + + value, exists := delegatorAmount.Get(addr1.String()) + if !exists || value.(uint64) != 100 { + t.Error("delegator amount not updated correctly") + } + + innerTree, exists := delegatedFromTo.Get(addr1.String()) + if !exists { + t.Error("delegatedFromTo not updated") + } + + inner := innerTree.(*avl.Tree) + delegatedAmount, exists := inner.Get(addr1.String()) + if !exists { + t.Error("delegatedFromTo amount incorrect") + } + + if delegatedAmount.(uint64) != 100 { + t.Error("delegatedFromTo amount incorrect") + } + }) + + t.Run("success - additional delegation", func(t *testing.T) { + resetState() + delegate(addr1, 100) + delegate(addr1, 50) - if tt.expectPanic { - defer func() { - if r := recover(); r == nil { - t.Errorf("expected panic but got none") - } - }() + if totalDelegated != 150 { + t.Errorf("expected totalDelegated 150, got %d", totalDelegated) + } + }) +} + +func TestUndelegate(t *testing.T) { + t.Skip("Must running separately. because this test depends on TestUndelegate's state") + addr1 := testAddr1 + std.TestSetOrigCaller(addr1) + resetState() + + t.Run("fail - no delegation", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("expected panic for no delegation") } + }() + undelegate(addr1, 100) + }) + + t.Run("fail - insufficient amount", func(t *testing.T) { + delegate(addr1, 50) - result := GetDelegatedCumulative(tt.delegator, tt.endTimestamp) - - if !tt.expectPanic && result != tt.expectAmount { - t.Errorf("expected amount %d but got %d", tt.expectAmount, result) + defer func() { + if r := recover(); r == nil { + t.Error("expected panic for insufficient amount") } - }) - } -} \ No newline at end of file + }() + undelegate(addr1, 100) + }) + + t.Run("success - partial undelegate", func(t *testing.T) { + resetState() + delegate(addr1, 100) + undelegate(addr1, 30) + + if totalDelegated != 70 { + t.Errorf("expected totalDelegated 70, got %d", totalDelegated) + } + }) + + t.Run("success - full undelegate", func(t *testing.T) { + resetState() + delegate(addr1, 100) + undelegate(addr1, 100) + + if totalDelegated != 0 { + t.Errorf("expected totalDelegated 0, got %d", totalDelegated) + } + }) +} + +func resetState() { + totalDelegated = 0 + delegatorAmount = avl.NewTree() + delegatedFromTo = avl.NewTree() + delegatedTo = avl.NewTree() + delegationHistory = avl.NewTree() + delegationSnapShotHistory = avl.NewTree() +} diff --git a/gov/staker/history_test.gno b/gov/staker/history_test.gno new file mode 100644 index 000000000..3498b5cb5 --- /dev/null +++ b/gov/staker/history_test.gno @@ -0,0 +1,103 @@ +package staker + +import ( + "std" + "testing" + "time" + + "gno.land/p/demo/avl" + "gno.land/p/demo/testutils" +) + +func TestGetDelegatedCumulative(t *testing.T) { + delegationSnapShotHistory = avl.NewTree() + + addr1 := testutils.TestAddress("test1") + now := uint64(time.Now().Unix()) + + tests := []struct { + name string + setupHistory []DelegationSnapShotHistory + delegator std.Address + endTimestamp uint64 + expectAmount uint64 + expectPanic bool + }{ + { + name: "no history returns zero", + delegator: addr1, + endTimestamp: now, + expectAmount: 0, + }, + { + name: "single history before timestamp", + setupHistory: []DelegationSnapShotHistory{ + { + to: addr1, + amount: 100, + updatedBlock: 1, + updatedAt: now - 100, + }, + }, + delegator: addr1, + endTimestamp: now, + expectAmount: 100, + }, + { + name: "multiple histories returns latest before timestamp", + setupHistory: []DelegationSnapShotHistory{ + { + to: addr1, + amount: 100, + updatedBlock: 1, + updatedAt: now - 200, + }, + { + to: addr1, + amount: 150, + updatedBlock: 2, + updatedAt: now - 100, + }, + { + to: addr1, + amount: 200, + updatedBlock: 3, + updatedAt: now + 100, // Future update + }, + }, + delegator: addr1, + endTimestamp: now, + expectAmount: 150, + }, + { + name: "future timestamp panics", + delegator: addr1, + endTimestamp: now + 1000, + expectPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + delegationSnapShotHistory = avl.NewTree() + + if len(tt.setupHistory) > 0 { + delegationSnapShotHistory.Set(tt.delegator.String(), tt.setupHistory) + } + + if tt.expectPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected panic but got none") + } + }() + } + + result := GetDelegatedCumulative(tt.delegator, tt.endTimestamp) + + if !tt.expectPanic && result != tt.expectAmount { + t.Errorf("expected amount %d but got %d", tt.expectAmount, result) + } + }) + } +} \ No newline at end of file