Kind Realm (renderable)
Name lotto
Namespace akkadia / beta
Exported functions 5
Module gno.land/r/akkadia/beta/lotto
gno 0.9 lotto_test.gno gno
⧉
1 package lotto 2 3 import ( 4 "strconv" 5 "testing" 6 7 "gno.land/p/akkadia/beta/accesscontrol" 8 AddNumber (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}, owner string, number uint64)
DeleteAll (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})
GetNumberCount (number uint64, owner string) uint64
ListOwnersByNumber (number uint64, afterOwner string, limit int) []map[string]string
Render (path 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.
"gno.land/p/nt/testutils/v0"
9 "gno.land/p/nt/uassert/v0"
10 "gno.land/r/akkadia/beta/admin"
11 )
12
13 func TestAddNumber(cur realm, t *testing.T) {
14 setupLotto(cross(cur))
15 for _, caller := range []address{testAdmin, testOperator} {
16 testing.SetOriginCaller(caller)
17 testing.SetRealm(testing.NewUserRealm(caller))
18 for i := 0 ; i < 3 ; i++ {
19 AddNumber(cross(cur), testOwner, 123456 )
20 }
21 }
22 uassert.Equal(t, GetNumberCount( 123456 , testOwner), uint64 ( 6 ))
23 uassert.Equal(t, holdings.Size(), 1 )
24 testing.SetRealm(testing.NewUserRealm(testAdmin))
25 AddNumber(cross(cur), testOther, 123456 )
26 AddNumber(cross(cur), testOwner, 654321 )
27 uassert.Equal(t, GetNumberCount( 123456 , testOwner), uint64 ( 6 ))
28 uassert.Equal(t, GetNumberCount( 123456 , testOther), uint64 ( 1 ))
29 uassert.Equal(t, GetNumberCount( 654321 , testOwner), uint64 ( 1 ))
30 uassert.Equal(t, GetNumberCount( 654321 , testOther), uint64 ( 0 ))
31 uassert.Equal(t, holdings.Size(), 3 )
32 page := ListOwnersByNumber( 123456 , "" , 100 )
33 uassert.Equal(t, len(page), 2 )
34 uassert.Equal(t, page[ 0 ][ "nextOwner" ], "" )
35 for _, item := range page {
36 uassert.Equal(t, item[ "count" ], strconv.FormatUint(GetNumberCount( 123456 , address(item[ "owner" ])), 10 ))
37 }
38 }
39
40 func TestAddNumberAuthority(cur realm, t *testing.T) {
41 setupLotto(cross(cur))
42 testing.SetRealm(testing.NewUserRealm(testAdmin))
43 AddNumber(cross(cur), testOwner, 1 )
44 for _, caller := range []address{testOwner, testOther} {
45 testing.SetOriginCaller(caller)
46 testing.SetRealm(testing.NewUserRealm(caller))
47 uassert.AbortsWithMessage(t, cur, "admin or operator access required" , func () {
48 AddNumber(cross(cur), testOwner, 1 )
49 })
50 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 1 ))
51 }
52 testing.SetOriginCaller(testAdmin)
53 testing.SetRealm(testing.NewUserRealm(testAdmin))
54 admin.UnsetOperator(cross(cur), testOperator)
55 testing.SetOriginCaller(testOperator)
56 testing.SetRealm(testing.NewUserRealm(testOperator))
57 uassert.AbortsWithMessage(t, cur, "admin or operator access required" , func () {
58 AddNumber(cross(cur), testOwner, 1 )
59 })
60 testing.SetRealm(testing.NewUserRealm(testAdmin))
61 admin.SetOperator(cross(cur), testOperator)
62 for _, signer := range []address{testAdmin, testOperator} {
63 testing.SetOriginCaller(signer)
64 testing.SetRealm(testing.NewCodeRealm( "gno.land/r/example/lotto_relay" ))
65 uassert.AbortsWithMessage(t, cur, "admin or operator access required" , func () {
66 AddNumber(cross(cur), testOwner, 1 )
67 })
68 }
69 // Probe the actual helper with a foreign realm, without cross(stale) masking
70 // its spoofed-realm assertion with a builtin crossing failure.
71 spoofed := cur.Previous()
72 uassert.PanicsWithMessage(t, cur, "spoofed realm" , func () {
73 accesscontrol.MustGetAdminOrOperatorCaller( 0 , spoofed, admin.IsAdmin, admin.IsOperator)
74 })
75 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 1 ))
76 uassert.Equal(t, holdings.Size(), 1 )
77 }
78
79 func TestAddNumberValidation(cur realm, t *testing.T) {
80 setupLotto(cross(cur))
81 for _, number := range [] uint64 { 0 , ^ uint64 ( 0 )} {
82 testing.SetRealm(testing.NewUserRealm(testAdmin))
83 AddNumber(cross(cur), testOwner, number)
84 uassert.Equal(t, GetNumberCount(number, testOwner), uint64 ( 1 ))
85 uassert.Equal(t, ListOwnersByNumber(number, "" , 1 )[ 0 ][ "owner" ], testOwner.String())
86 }
87 for _, owner := range []address{ "" , "invalid" , address(testOwner.String() + "x" )} {
88 testing.SetRealm(testing.NewUserRealm(testAdmin))
89 uassert.AbortsWithMessage(t, cur, "invalid address" , func () {
90 AddNumber(cross(cur), owner, 1 )
91 })
92 uassert.PanicsWithMessage(t, cur, "invalid address" , func () { GetNumberCount( 1 , owner) })
93 if owner != "" {
94 uassert.PanicsWithMessage(t, cur, "invalid address" , func () {
95 ListOwnersByNumber( 1 , owner.String(), 1 )
96 })
97 }
98 }
99 uassert.Equal(t, holdings.Size(), 2 )
100 // Overflow cannot be reached economically through repeated public calls.
101 holdings.Set( "1:" +testOwner.String(), ^ uint64 ( 0 ))
102 testing.SetRealm(testing.NewUserRealm(testAdmin))
103 uassert.AbortsWithMessage(t, cur, "number count overflow" , func () {
104 AddNumber(cross(cur), testOwner, 1 )
105 })
106 uassert.Equal(t, GetNumberCount( 1 , testOwner), ^ uint64 ( 0 ))
107 uassert.Equal(t, ListOwnersByNumber( 1 , "" , 1 )[ 0 ][ "count" ], "18446744073709551615" )
108 uassert.Equal(t, holdings.Size(), 3 )
109 }
110
111 func TestListOwnersByNumber(cur realm, t *testing.T) {
112 setupLotto(cross(cur))
113 uassert.Equal(t, len(ListOwnersByNumber( 1 , "" , 100 )), 0 )
114
115 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 0 ))
116 uassert.Equal(t, holdings.Size(), 0 )
117 for _, limit := range [] int {- 1 , 0 , 101 } {
118 uassert.PanicsWithMessage(t, cur, "limit must be between 1 and 100" , func () {
119 ListOwnersByNumber( 1 , "" , limit)
120 })
121 }
122 for _, number := range [] uint64 { 1 , 10 , 100 } {
123 for i := 0 ; i < 105 ; i++ {
124 testing.SetRealm(testing.NewUserRealm(testAdmin))
125 owner := testutils.TestAddress( "lottoPage" + strconv.FormatUint(number, 10 ) + ":" + strconv.Itoa(i))
126 AddNumber(cross(cur), owner, number)
127 if i% 2 == 0 {
128 AddNumber(cross(cur), owner, number)
129 }
130 }
131 }
132 for _, number := range [] uint64 { 1 , 10 , 100 } {
133 for _, limit := range [] int { 1 , 7 , 100 } {
134 after := ""
135 seen := 0
136 for {
137 page := ListOwnersByNumber(number, after, limit)
138 for _, item := range page {
139 uassert.True(t, item[ "owner" ] > after)
140 uassert.Equal(t, item[ "nextOwner" ], page[ 0 ][ "nextOwner" ])
141 uassert.Equal(t, item[ "count" ], strconv.FormatUint(GetNumberCount(number, address(item[ "owner" ])), 10 ))
142 after = item[ "owner" ]
143 seen++
144 }
145 if len(page) == 0 || page[ 0 ][ "nextOwner" ] == "" {
146 break
147 }
148 uassert.Equal(t, len(page), limit)
149 uassert.Equal(t, page[ 0 ][ "nextOwner" ], after)
150 }
151 uassert.Equal(t, seen, 105 )
152 uassert.Equal(t, len(ListOwnersByNumber(number, after, limit)), 0 )
153 }
154 }
155 uassert.Equal(t, holdings.Size(), 315 )
156 uassert.Equal(t, len(ListOwnersByNumber( 1000 , "" , 100 )), 0 )
157 // A valid cursor between stored owners does not have to be stored itself.
158 missing := testutils.TestAddress( "lottoMissing" ).String()
159 uassert.Equal(t, GetNumberCount( 1 , address(missing)), uint64 ( 0 ))
160 first := ListOwnersByNumber( 1 , "" , 100 )
161 all := append(first, ListOwnersByNumber( 1 , first[ 0 ][ "nextOwner" ], 100 )...)
162 expected := [] map [ string ] string {}
163 for _, item := range all {
164 if item[ "owner" ] > missing {
165 expected = append(expected, item)
166 }
167 }
168 page := ListOwnersByNumber( 1 , missing, 100 )
169 actual := page
170 if len(page) > 0 && page[ 0 ][ "nextOwner" ] != "" {
171 actual = append(actual, ListOwnersByNumber( 1 , page[ 0 ][ "nextOwner" ], 100 )...)
172 }
173 uassert.Equal(t, len(actual), len(expected))
174 for i, item := range actual {
175 uassert.Equal(t, item[ "owner" ], expected[i][ "owner" ])
176 uassert.Equal(t, item[ "count" ], expected[i][ "count" ])
177 }
178 fresh := ListOwnersByNumber( 1 , "" , 1 )
179 original := map [ string ] string { "owner" : fresh[ 0 ][ "owner" ], "count" : fresh[ 0 ][ "count" ]}
180 fresh[ 0 ][ "owner" ] = testOther.String()
181 fresh[ 0 ][ "count" ] = "999"
182 fresh[ 0 ][ "nextOwner" ] = "changed"
183 uassert.Equal(t, ListOwnersByNumber( 1 , "" , 1 )[ 0 ][ "owner" ], original[ "owner" ])
184 uassert.Equal(t, ListOwnersByNumber( 1 , "" , 1 )[ 0 ][ "count" ], original[ "count" ])
185 uassert.Equal(t, strconv.FormatUint(GetNumberCount( 1 , address(original[ "owner" ])), 10 ), original[ "count" ])
186 uassert.Equal(t, holdings.Size(), 315 )
187 }
188
189 func TestDeleteAll(cur realm, t *testing.T) {
190 setupLotto(cross(cur))
191 testing.SetOriginCaller(testAdmin)
192 testing.SetRealm(testing.NewUserRealm(testAdmin))
193 AddNumber(cross(cur), testOwner, 1 )
194 AddNumber(cross(cur), testOwner, 1 )
195 AddNumber(cross(cur), testOther, 1 )
196 AddNumber(cross(cur), testOwner, 2 )
197
198 DeleteAll(cross(cur))
199 uassert.Equal(t, holdings.Size(), 0 )
200 for _, number := range [] uint64 { 1 , 2 } {
201 for _, owner := range []address{testOwner, testOther} {
202 uassert.Equal(t, GetNumberCount(number, owner), uint64 ( 0 ))
203 }
204 uassert.Equal(t, len(ListOwnersByNumber(number, "" , 100 )), 0 )
205 }
206 DeleteAll(cross(cur))
207 // Operators can register again, starting from one instead of the old count.
208 testing.SetOriginCaller(testOperator)
209 testing.SetRealm(testing.NewUserRealm(testOperator))
210 AddNumber(cross(cur), testOwner, 1 )
211 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 1 ))
212 uassert.Equal(t, holdings.Size(), 1 )
213 uassert.Equal(t, len(ListOwnersByNumber( 1 , "" , 100 )), 1 )
214 }
215
216 func TestDeleteAllAuthority(cur realm, t *testing.T) {
217 setupLotto(cross(cur))
218 testing.SetOriginCaller(testAdmin)
219 testing.SetRealm(testing.NewUserRealm(testAdmin))
220 AddNumber(cross(cur), testOwner, 1 )
221 for _, caller := range []address{testOwner, testOperator} {
222 testing.SetOriginCaller(caller)
223 testing.SetRealm(testing.NewUserRealm(caller))
224 uassert.AbortsWithMessage(t, cur, "admin access required" , func () {
225 DeleteAll(cross(cur))
226 })
227 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 1 ))
228 uassert.Equal(t, holdings.Size(), 1 )
229 }
230 testing.SetOriginCaller(testAdmin)
231 testing.SetRealm(testing.NewCodeRealm( "gno.land/r/example/lotto_relay" ))
232 uassert.AbortsWithMessage(t, cur, "admin access required" , func () {
233 DeleteAll(cross(cur))
234 })
235 uassert.Equal(t, GetNumberCount( 1 , testOwner), uint64 ( 1 ))
236 uassert.Equal(t, holdings.Size(), 1 )
237 }
238