Kind Realm (renderable)
Name v2
Namespace sys / validators
Exported functions 6
Module gno.land/r/sys/validators/v2
gno 0.9 validators_test.gno gno
⧉
1 package validators 2 3 import ( 4 "chain/runtime" 5 "math" 6 "strings" 7 "testing" 8 GetChanges (from int64, to int64) []gno.land/p/sys/validators/v0.Validator
GetValidator (addr string) struct{Address .uverse.address; PubKey string; VotingPower uint64}
GetValidators () []gno.land/p/sys/validators/v0.Validator
IsValidator (addr string) bool
NewPropRequest (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, changesFn func() []gno.land/p/sys/validators/v0.Validator, title string, description string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
Render (string) string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
9 "gno.land/p/nt/bptree/v0"
10 "gno.land/p/nt/poa/v0"
11 "gno.land/p/nt/testutils/v0"
12 "gno.land/p/nt/uassert/v0"
13 "gno.land/p/nt/ufmt/v0"
14 "gno.land/p/sys/validators/v0"
15 )
16
17 // cur is a zero-value realm used as a placeholder when forwarding to
18 // uassert/urequire dispatch helpers that gained an `rlm realm` param.
19 // These tests pass `func()` callbacks (no crossing inside the callback),
20 // so rlm is ignored — a nil realm here is safe.
21 var cur realm
22
23 // generateTestValidators generates a dummy validator set
24 func generateTestValidators(count int ) []validators.Validator {
25 vals := make([]validators.Validator, 0 , count)
26
27 for i := 0 ; i < count; i++ {
28 val := validators.Validator{
29 Address: testutils.TestAddress(ufmt.Sprintf( "%d" , i)),
30 PubKey: "public-key" ,
31 VotingPower: 10 ,
32 }
33
34 vals = append(vals, val)
35 }
36
37 return vals
38 }
39
40 func TestValidators_AddRemove(t *testing.T) {
41 // Clear any changes
42 changes = bptree.NewBPTree32()
43
44 var (
45 vals = generateTestValidators( 100 )
46 initialHeight = int64 ( 123 )
47 )
48
49 // Add in the validators
50 for _, val := range vals {
51 addValidator(val)
52
53 // Make sure the validator is added
54 uassert.True(t, vp.IsValidator(val.Address))
55
56 testing.SkipHeights( 1 )
57 }
58
59 for i := initialHeight; i < initialHeight+ int64 (len(vals)); i++ {
60 // Make sure the changes are saved
61 chs := GetChanges(i, initialHeight+ int64 (len(vals)))
62
63 // We use the funky index calculation to make sure
64 // changes are properly handled for each block span
65 uassert.Equal(t, initialHeight+ int64 (len(vals))-i, int64 (len(chs)))
66
67 for index, val := range vals[i-initialHeight:] {
68 // Make sure the changes are equal to the additions
69 ch := chs[index]
70
71 uassert.Equal(t, val.Address, ch.Address)
72 uassert.Equal(t, val.PubKey, ch.PubKey)
73 uassert.Equal(t, val.VotingPower, ch.VotingPower)
74 }
75 }
76
77 // Save the beginning height for the removal
78 initialRemoveHeight := runtime.ChainHeight()
79
80 // Clear any changes
81 changes = bptree.NewBPTree32()
82
83 // Remove the validators
84 for _, val := range vals {
85 removeValidator(val.Address)
86
87 // Make sure the validator is removed
88 uassert.False(t, vp.IsValidator(val.Address))
89
90 testing.SkipHeights( 1 )
91 }
92
93 for i := initialRemoveHeight; i < initialRemoveHeight+ int64 (len(vals)); i++ {
94 // Make sure the changes are saved
95 chs := GetChanges(i, initialRemoveHeight+ int64 (len(vals)))
96
97 // We use the funky index calculation to make sure
98 // changes are properly handled for each block span
99 uassert.Equal(t, initialRemoveHeight+ int64 (len(vals))-i, int64 (len(chs)))
100
101 for index, val := range vals[i-initialRemoveHeight:] {
102 // Make sure the changes are equal to the additions
103 ch := chs[index]
104
105 uassert.Equal(t, val.Address, ch.Address)
106 uassert.Equal(t, val.PubKey, ch.PubKey)
107 uassert.Equal(t, uint64 ( 0 ), ch.VotingPower)
108 }
109 }
110 }
111
112 // TestGetChanges_BoundedRange verifies that GetChanges(from, to) correctly
113 // returns only changes within the [from, to] block range.
114 func TestGetChanges_BoundedRange(t *testing.T) {
115 changes = bptree.NewBPTree32()
116 vp = poa.NewPoA()
117
118 vals := generateTestValidators( 3 )
119
120 // Store additions at block h1
121 h1 := runtime.ChainHeight()
122 for _, val := range vals {
123 addValidator(val)
124 }
125 testing.SkipHeights( 1 )
126
127 // Store removals at block h2
128 h2 := runtime.ChainHeight()
129 for _, val := range vals {
130 removeValidator(val.Address)
131 }
132 testing.SkipHeights( 1 )
133
134 // Query spanning both blocks returns all changes
135 all := GetChanges(h1, h2)
136 uassert.Equal(t, 6 , len(all))
137
138 // Query for h1 only returns additions
139 atH1 := GetChanges(h1, h1)
140 uassert.Equal(t, 3 , len(atH1))
141 for i, ch := range atH1 {
142 uassert.Equal(t, vals[i].Address, ch.Address)
143 uassert.True(t, ch.VotingPower > 0 )
144 }
145
146 // Query for h2 only returns removals
147 atH2 := GetChanges(h2, h2)
148 uassert.Equal(t, 3 , len(atH2))
149 for i, ch := range atH2 {
150 uassert.Equal(t, vals[i].Address, ch.Address)
151 uassert.Equal(t, uint64 ( 0 ), ch.VotingPower)
152 }
153
154 // Query beyond stored range returns empty
155 uassert.Equal(t, 0 , len(GetChanges(h2+ 1 , h2+ 1 )))
156 }
157
158 // TestRender_ShowsNewestWhenOverLimit verifies that Render displays the newest
159 // maxDisplay buckets when there are more change buckets than maxDisplay.
160 func TestRender_ShowsNewestWhenOverLimit(t *testing.T) {
161 changes = bptree.NewBPTree32()
162 vp = poa.NewPoA()
163
164 const total = 13
165 const maxDisplay = 10
166
167 vals := generateTestValidators(total)
168 base := runtime.ChainHeight()
169
170 for i := 0 ; i < total; i++ {
171 h := base + int64 (i)
172 changes.Set(getBlockID(h), []change{
173 {blockNum: h, validator: vals[i]},
174 })
175 }
176
177 output := Render( "" )
178
179 // Newest maxDisplay buckets must appear.
180 for i := total - maxDisplay; i < total; i++ {
181 h := base + int64 (i)
182 uassert.True(t, strings.Contains(output, ufmt.Sprintf( "#%d:" , h)),
183 ufmt.Sprintf( "expected block #%d in output" , h))
184 }
185
186 // Oldest (total - maxDisplay) buckets must NOT appear.
187 for i := 0 ; i < total-maxDisplay; i++ {
188 h := base + int64 (i)
189 uassert.False(t, strings.Contains(output, ufmt.Sprintf( "#%d:" , h)),
190 ufmt.Sprintf( "block #%d should be absent from output" , h))
191 }
192 }
193
194 // TestRender_ShowsAllWhenUnderLimit verifies that all buckets are displayed when
195 // there are fewer than maxDisplay, and that no panic occurs from a negative offset.
196 func TestRender_ShowsAllWhenUnderLimit(t *testing.T) {
197 changes = bptree.NewBPTree32()
198 vp = poa.NewPoA()
199
200 const total = 5 // less than maxDisplay=10
201
202 vals := generateTestValidators(total)
203 base := runtime.ChainHeight()
204
205 for i := 0 ; i < total; i++ {
206 h := base + int64 (i)
207 changes.Set(getBlockID(h), []change{
208 {blockNum: h, validator: vals[i]},
209 })
210 }
211
212 output := Render( "" )
213
214 for i := 0 ; i < total; i++ {
215 h := base + int64 (i)
216 uassert.True(t, strings.Contains(output, ufmt.Sprintf( "#%d:" , h)),
217 ufmt.Sprintf( "expected block #%d in output" , h))
218 }
219 }
220
221 func TestGetChanges_PanicsOnInvalidRange(cur realm, t *testing.T) {
222 uassert.PanicsWithMessage(t, cur, "invalid range: from must be <= to" , func () {
223 GetChanges( 10 , 5 )
224 })
225 }
226
227 func TestGetChanges_ClampsMaxInt64(t *testing.T) {
228 changes = bptree.NewBPTree32()
229
230 vals := generateTestValidators( 1 )
231
232 // Simulate a validator change at block math.MaxInt64-1 (the boundary value).
233 changes.Set(getBlockID(math.MaxInt64- 1 ), []change{
234 {blockNum: math.MaxInt64 - 1 , validator: vals[ 0 ]},
235 })
236
237 // Passing math.MaxInt64 as "to" means "get all updates from here onwards".
238 // The clamp (to = MaxInt64-1) must still include the boundary block.
239 result := GetChanges(math.MaxInt64- 1 , math.MaxInt64)
240 uassert.Equal(t, 1 , len(result))
241 uassert.Equal(t, vals[ 0 ].Address, result[ 0 ].Address)
242 }
243