user_test.gno
16.73 Kb · 474 lines
1package user
2
3import (
4 "testing"
5 "time"
6
7 "gno.land/p/nt/bptree/v0"
8 "gno.land/p/nt/testutils/v0"
9 "gno.land/p/nt/uassert/v0"
10 "gno.land/r/akkadia/beta/admin"
11)
12
13var (
14 testAdmin = testutils.TestAddress("testAdmin")
15 regularUser = testutils.TestAddress("regularUser")
16 badUser = testutils.TestAddress("badUser")
17 anotherUser = testutils.TestAddress("anotherUser")
18 setupDone = false
19)
20
21func setupUserTests(cur realm, t *testing.T) {
22 t.Helper()
23 users = bptree.BPTree{}
24 listLimit = 100
25 isFrozen = true
26
27 if setupDone {
28 return
29 }
30 originalAdmin := admin.GetAdmin()
31 testing.SetOriginCaller(originalAdmin)
32 testing.SetRealm(testing.NewUserRealm(originalAdmin))
33 admin.TransferAdmin(cross(cur), testAdmin)
34
35 testing.SetOriginCaller(testAdmin)
36 testing.SetRealm(testing.NewUserRealm(testAdmin))
37 admin.AcceptAdmin(cross(cur))
38 setupDone = true
39}
40
41func createUserForTest(cur realm, addr address) {
42 testing.SetOriginCaller(testAdmin)
43 testing.SetRealm(testing.NewUserRealm(testAdmin))
44 AdminCreateUser(cross(cur), addr)
45}
46
47func TestParsePropertiesCSV(cur realm, t *testing.T) {
48 props := parsePropertiesCSV("nickname,introduction", "Alice,hello%2C%20world")
49 uassert.Equal(t, "Alice", props["nickname"])
50 uassert.Equal(t, "hello, world", props["introduction"])
51
52 props = parsePropertiesCSV("nickname,introduction", "Alice,")
53 uassert.Equal(t, "", props["introduction"])
54
55 uassert.PanicsWithMessage(t, cur, "properties keys must be not empty", func() {
56 parsePropertiesCSV("", "")
57 })
58 uassert.PanicsWithMessage(t, cur, "properties key duplicated: nickname", func() {
59 parsePropertiesCSV("nickname,nickname", "Alice,Bob")
60 })
61 uassert.PanicsWithMessage(t, cur, "properties keys and values count mismatch", func() {
62 parsePropertiesCSV("nickname,introduction", "Alice")
63 })
64 uassert.PanicsWithMessage(t, cur, "properties value URL decode failed: introduction", func() {
65 parsePropertiesCSV("nickname,introduction", "Alice,%zz")
66 })
67}
68
69func TestCreateUser(cur realm, t *testing.T) {
70 setupUserTests(cross(cur), t)
71 testing.SetOriginCaller(testAdmin)
72 testing.SetRealm(testing.NewUserRealm(testAdmin))
73 SetFrozen(cross(cur), false)
74 testing.SetOriginCaller(regularUser)
75 testing.SetRealm(testing.NewUserRealm(regularUser))
76 CreateUser(cross(cur))
77 uassert.True(t, HasUser(regularUser))
78 uassert.False(t, HasUser(testAdmin))
79 uassert.Equal(t, 1, GetTotalUserSize())
80 createdAt := ListUsersByAddresses(regularUser)[0][createdAtKey]
81 uassert.True(t, createdAt != "")
82 uassert.AbortsWithMessage(t, cur, "user already exists: "+regularUser.String(), func() {
83 CreateUser(cross(cur))
84 })
85 uassert.Equal(t, createdAt, ListUsersByAddresses(regularUser)[0][createdAtKey])
86 // Operator delivery after self-registration remains a safe no-op.
87 createUserForTest(cross(cur), regularUser)
88 uassert.Equal(t, 1, GetTotalUserSize())
89 uassert.Equal(t, createdAt, ListUsersByAddresses(regularUser)[0][createdAtKey])
90}
91
92func TestCreateUserRejectsCodeRealm(cur realm, t *testing.T) {
93 setupUserTests(cross(cur), t)
94 testing.SetOriginCaller(testAdmin)
95 testing.SetRealm(testing.NewUserRealm(testAdmin))
96 SetFrozen(cross(cur), false)
97 testing.SetOriginCaller(regularUser)
98 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_relay"))
99 uassert.AbortsWithMessage(t, cur, "contract calls not allowed: function must be called directly by user", func() {
100 CreateUser(cross(cur))
101 })
102 uassert.Equal(t, 0, GetTotalUserSize())
103}
104
105func TestAdminCreateUser(cur realm, t *testing.T) {
106 setupUserTests(cross(cur), t)
107
108 createUserForTest(cross(cur), regularUser)
109 uassert.True(t, HasUser(regularUser))
110 uassert.Equal(t, 1, GetTotalUserSize())
111 usersByAddr := ListUsersByAddresses(regularUser)
112 uassert.Equal(t, 1, len(usersByAddr))
113 uassert.Equal(t, regularUser.String(), usersByAddr[0]["address"])
114 uassert.True(t, usersByAddr[0][createdAtKey] != "")
115
116 createdAt := usersByAddr[0][createdAtKey]
117 ctx := testing.GetContext()
118 ctx.Time = ctx.Time.Add(time.Hour)
119 testing.SetContext(ctx)
120 createUserForTest(cross(cur), regularUser)
121 uassert.Equal(t, 1, GetTotalUserSize())
122 uassert.Equal(t, createdAt, ListUsersByAddresses(regularUser)[0][createdAtKey])
123}
124
125func TestAdminCreateUserOperator(cur realm, t *testing.T) {
126 setupUserTests(cross(cur), t)
127 testing.SetOriginCaller(testAdmin)
128 testing.SetRealm(testing.NewUserRealm(testAdmin))
129 admin.SetOperator(cross(cur), anotherUser)
130 testing.SetOriginCaller(anotherUser)
131 testing.SetRealm(testing.NewUserRealm(anotherUser))
132 AdminCreateUser(cross(cur), regularUser)
133 uassert.True(t, HasUser(regularUser))
134 uassert.False(t, HasUser(anotherUser))
135 uassert.Equal(t, 1, GetTotalUserSize())
136 testing.SetOriginCaller(testAdmin)
137 testing.SetRealm(testing.NewUserRealm(testAdmin))
138 admin.UnsetOperator(cross(cur), anotherUser)
139}
140
141func TestAdminCreateUserRejectsUnauthorizedAndInvalidAddress(cur realm, t *testing.T) {
142 setupUserTests(cross(cur), t)
143 createUserForTest(cross(cur), regularUser)
144 testing.SetOriginCaller(regularUser)
145 testing.SetRealm(testing.NewUserRealm(regularUser))
146 uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() {
147 AdminCreateUser(cross(cur), anotherUser)
148 })
149 // Authorization must run even when the target already exists.
150 uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() {
151 AdminCreateUser(cross(cur), regularUser)
152 })
153 testing.SetOriginCaller(testAdmin)
154 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_relay"))
155 uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() {
156 AdminCreateUser(cross(cur), anotherUser)
157 })
158 testing.SetRealm(testing.NewUserRealm(testAdmin))
159 uassert.AbortsWithMessage(t, cur, "invalid address: "+address("").String(), func() {
160 AdminCreateUser(cross(cur), address(""))
161 })
162 uassert.True(t, HasUser(regularUser))
163 uassert.False(t, HasUser(anotherUser))
164 uassert.Equal(t, 1, GetTotalUserSize())
165}
166
167func TestDeleteUser(cur realm, t *testing.T) {
168 setupUserTests(cross(cur), t)
169 testing.SetOriginCaller(testAdmin)
170 testing.SetRealm(testing.NewUserRealm(testAdmin))
171 SetFrozen(cross(cur), false)
172 createUserForTest(cross(cur), regularUser)
173
174 testing.SetOriginCaller(regularUser)
175 testing.SetRealm(testing.NewUserRealm(regularUser))
176 DeleteUser(cross(cur))
177 uassert.False(t, HasUser(regularUser))
178 uassert.Equal(t, 0, GetTotalUserSize())
179
180 uassert.AbortsWithMessage(t, cur, "user not found: "+regularUser.String(), func() {
181 DeleteUser(cross(cur))
182 })
183}
184
185func TestAdminDeleteUser(cur realm, t *testing.T) {
186 setupUserTests(cross(cur), t)
187 createUserForTest(cross(cur), regularUser)
188
189 testing.SetOriginCaller(regularUser)
190 testing.SetRealm(testing.NewUserRealm(regularUser))
191 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
192 AdminDeleteUser(cross(cur), regularUser)
193 })
194
195 testing.SetOriginCaller(testAdmin)
196 testing.SetRealm(testing.NewUserRealm(testAdmin))
197 AdminDeleteUser(cross(cur), regularUser)
198 uassert.False(t, HasUser(regularUser))
199
200 uassert.AbortsWithMessage(t, cur, "user not found: "+regularUser.String(), func() {
201 AdminDeleteUser(cross(cur), regularUser)
202 })
203}
204
205func TestAdminUpdateUser(cur realm, t *testing.T) {
206 setupUserTests(cross(cur), t)
207 createUserForTest(cross(cur), regularUser)
208 createdAt := ListUsersByAddresses(regularUser)[0][createdAtKey]
209
210 testing.SetOriginCaller(testAdmin)
211 testing.SetRealm(testing.NewUserRealm(testAdmin))
212 AdminUpdateUser(cross(cur), regularUser, "nickname,introduction", "Alice,hello%2C%20world")
213 listed := ListUsersByAddresses(regularUser)[0]
214 uassert.Equal(t, "Alice", listed["nickname"])
215 uassert.Equal(t, "hello, world", listed["introduction"])
216 uassert.Equal(t, createdAt, listed[createdAtKey])
217
218 AdminUpdateUser(cross(cur), regularUser, createdAtKey, "1")
219 uassert.Equal(t, "1", ListUsersByAddresses(regularUser)[0][createdAtKey])
220 uassert.Equal(t, "Alice", ListUsersByAddresses(regularUser)[0]["nickname"])
221}
222
223func TestAdminUpdateUserRejectsMissingAndUnauthorized(cur realm, t *testing.T) {
224 setupUserTests(cross(cur), t)
225
226 testing.SetOriginCaller(testAdmin)
227 testing.SetRealm(testing.NewUserRealm(testAdmin))
228 uassert.AbortsWithMessage(t, cur, "user not found: "+regularUser.String(), func() {
229 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
230 })
231
232 createUserForTest(cross(cur), regularUser)
233 testing.SetOriginCaller(regularUser)
234 testing.SetRealm(testing.NewUserRealm(regularUser))
235 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
236 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
237 })
238
239 testing.SetOriginCaller(testAdmin)
240 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_admin_relay"))
241 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
242 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
243 })
244}
245
246func TestListUsersAddressOrder(cur realm, t *testing.T) {
247 setupUserTests(cross(cur), t)
248 createUserForTest(cross(cur), regularUser)
249 createUserForTest(cross(cur), anotherUser)
250
251 listed := ListUsers(1, 10)
252 uassert.Equal(t, 2, len(listed))
253 if regularUser.String() < anotherUser.String() {
254 uassert.Equal(t, regularUser.String(), listed[0])
255 uassert.Equal(t, anotherUser.String(), listed[1])
256 } else {
257 uassert.Equal(t, anotherUser.String(), listed[0])
258 uassert.Equal(t, regularUser.String(), listed[1])
259 }
260
261 firstPage := ListUsers(1, 1)
262 uassert.Equal(t, 1, len(firstPage))
263 uassert.Equal(t, listed[0], firstPage[0])
264 secondPage := ListUsers(2, 1)
265 uassert.Equal(t, listed[1], secondPage[0])
266}
267
268func TestListUsersByAddresses(cur realm, t *testing.T) {
269 setupUserTests(cross(cur), t)
270 createUserForTest(cross(cur), regularUser)
271 testing.SetOriginCaller(testAdmin)
272 testing.SetRealm(testing.NewUserRealm(testAdmin))
273 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
274
275 listed := ListUsersByAddresses(regularUser, badUser)
276 uassert.Equal(t, 1, len(listed))
277 uassert.Equal(t, "Alice", listed[0]["nickname"])
278 listed[0]["nickname"] = "mutated"
279 again := ListUsersByAddresses(regularUser)
280 uassert.Equal(t, "Alice", again[0]["nickname"])
281}
282
283func TestListInputValidation(cur realm, t *testing.T) {
284 setupUserTests(cross(cur), t)
285 uassert.PanicsWithMessage(t, cur, "page must be at least 1", func() {
286 ListUsers(0, 1)
287 })
288 uassert.PanicsWithMessage(t, cur, "count must be at least 1", func() {
289 ListUsers(1, 0)
290 })
291 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 100)", func() {
292 ListUsers(1, 101)
293 })
294 uassert.PanicsWithMessage(t, cur, "invalid address: "+address("").String(), func() {
295 HasUser(address(""))
296 })
297 uassert.Equal(t, 100, GetListLimit())
298}
299
300func TestSetListLimit(cur realm, t *testing.T) {
301 setupUserTests(cross(cur), t)
302
303 testing.SetOriginCaller(testAdmin)
304 testing.SetRealm(testing.NewUserRealm(testAdmin))
305 SetListLimit(cross(cur), 50)
306 uassert.Equal(t, 50, GetListLimit())
307
308 SetListLimit(cross(cur), 1)
309 uassert.PanicsWithMessage(t, cur, "addrs exceeds listLimit (max: 1)", func() {
310 ListUsersByAddresses(regularUser, badUser)
311 })
312 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 1)", func() {
313 ListUsers(1, 2)
314 })
315
316 uassert.AbortsWithMessage(t, cur, "limit must be at least 1", func() {
317 SetListLimit(cross(cur), 0)
318 })
319 uassert.AbortsWithMessage(t, cur, "limit must be at least 1", func() {
320 SetListLimit(cross(cur), -1)
321 })
322
323 testing.SetOriginCaller(regularUser)
324 testing.SetRealm(testing.NewUserRealm(regularUser))
325 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
326 SetListLimit(cross(cur), 50)
327 })
328
329 testing.SetOriginCaller(testAdmin)
330 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_admin_relay"))
331 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
332 SetListLimit(cross(cur), 50)
333 })
334}
335
336func TestSelfServiceFrozen(cur realm, t *testing.T) {
337 uassert.True(t, isFrozen)
338 setupUserTests(cross(cur), t)
339 createUserForTest(cross(cur), regularUser)
340 testing.SetOriginCaller(testAdmin)
341 testing.SetRealm(testing.NewUserRealm(testAdmin))
342 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
343 before := ListUsersByAddresses(regularUser)
344
345 testing.SetOriginCaller(anotherUser)
346 testing.SetRealm(testing.NewUserRealm(anotherUser))
347 uassert.AbortsWithMessage(t, cur, "user self-service is frozen", func() {
348 CreateUser(cross(cur))
349 })
350 uassert.False(t, HasUser(anotherUser))
351 testing.SetOriginCaller(regularUser)
352 testing.SetRealm(testing.NewUserRealm(regularUser))
353 uassert.AbortsWithMessage(t, cur, "user self-service is frozen", func() {
354 DeleteUser(cross(cur))
355 })
356 uassert.Equal(t, before[0][createdAtKey], ListUsersByAddresses(regularUser)[0][createdAtKey])
357 uassert.Equal(t, "Alice", ListUsersByAddresses(regularUser)[0]["nickname"])
358 uassert.Equal(t, 1, GetTotalUserSize())
359
360 testing.SetOriginCaller(testAdmin)
361 testing.SetRealm(testing.NewUserRealm(testAdmin))
362 SetFrozen(cross(cur), false)
363 testing.SetOriginCaller(anotherUser)
364 testing.SetRealm(testing.NewUserRealm(anotherUser))
365 CreateUser(cross(cur))
366 uassert.True(t, HasUser(anotherUser))
367 DeleteUser(cross(cur))
368 uassert.False(t, HasUser(anotherUser))
369
370 testing.SetOriginCaller(testAdmin)
371 testing.SetRealm(testing.NewUserRealm(testAdmin))
372 SetFrozen(cross(cur), true)
373 testing.SetOriginCaller(anotherUser)
374 testing.SetRealm(testing.NewUserRealm(anotherUser))
375 uassert.AbortsWithMessage(t, cur, "user self-service is frozen", func() {
376 CreateUser(cross(cur))
377 })
378 testing.SetOriginCaller(regularUser)
379 testing.SetRealm(testing.NewUserRealm(regularUser))
380 uassert.AbortsWithMessage(t, cur, "user self-service is frozen", func() {
381 DeleteUser(cross(cur))
382 })
383 uassert.Equal(t, before[0][createdAtKey], ListUsersByAddresses(regularUser)[0][createdAtKey])
384 uassert.Equal(t, "Alice", ListUsersByAddresses(regularUser)[0]["nickname"])
385 uassert.Equal(t, 1, GetTotalUserSize())
386}
387
388func TestSetFrozenRejectsUnauthorized(cur realm, t *testing.T) {
389 setupUserTests(cross(cur), t)
390 testing.SetOriginCaller(testAdmin)
391 testing.SetRealm(testing.NewUserRealm(testAdmin))
392 admin.SetOperator(cross(cur), anotherUser)
393
394 for _, frozen := range []bool{true, false} {
395 testing.SetOriginCaller(testAdmin)
396 testing.SetRealm(testing.NewUserRealm(testAdmin))
397 SetFrozen(cross(cur), frozen)
398 for _, caller := range []address{regularUser, anotherUser} {
399 testing.SetOriginCaller(caller)
400 testing.SetRealm(testing.NewUserRealm(caller))
401 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
402 SetFrozen(cross(cur), !frozen)
403 })
404 uassert.Equal(t, frozen, isFrozen)
405 }
406 testing.SetOriginCaller(testAdmin)
407 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_admin_relay"))
408 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
409 SetFrozen(cross(cur), !frozen)
410 })
411 uassert.Equal(t, frozen, isFrozen)
412 }
413 testing.SetOriginCaller(testAdmin)
414 testing.SetRealm(testing.NewUserRealm(testAdmin))
415 admin.UnsetOperator(cross(cur), anotherUser)
416 SetFrozen(cross(cur), true)
417}
418
419func TestDeleteAll(cur realm, t *testing.T) {
420 for _, frozen := range []bool{true, false} {
421 setupUserTests(cross(cur), t)
422 createUserForTest(cross(cur), regularUser)
423 createUserForTest(cross(cur), anotherUser)
424 testing.SetOriginCaller(testAdmin)
425 testing.SetRealm(testing.NewUserRealm(testAdmin))
426 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
427 SetListLimit(cross(cur), 7)
428 SetFrozen(cross(cur), frozen)
429
430 DeleteAll(cross(cur))
431 uassert.Equal(t, 0, GetTotalUserSize())
432 uassert.False(t, HasUser(regularUser))
433 uassert.False(t, HasUser(anotherUser))
434 uassert.Equal(t, 0, len(ListUsers(1, 7)))
435 uassert.Equal(t, 0, len(ListUsersByAddresses(regularUser, anotherUser)))
436 uassert.Equal(t, 7, GetListLimit())
437 uassert.Equal(t, frozen, isFrozen)
438 // Empty resets are safe, and re-registration must not restore old properties.
439 DeleteAll(cross(cur))
440 AdminCreateUser(cross(cur), regularUser)
441 uassert.Equal(t, 1, GetTotalUserSize())
442 uassert.True(t, HasUser(regularUser))
443 props := ListUsersByAddresses(regularUser)[0]
444 uassert.Equal(t, "", props["nickname"])
445 uassert.True(t, props[createdAtKey] != "")
446 }
447}
448
449func TestDeleteAllAuthority(cur realm, t *testing.T) {
450 setupUserTests(cross(cur), t)
451 createUserForTest(cross(cur), regularUser)
452 testing.SetOriginCaller(testAdmin)
453 testing.SetRealm(testing.NewUserRealm(testAdmin))
454 AdminUpdateUser(cross(cur), regularUser, "nickname", "Alice")
455 admin.SetOperator(cross(cur), anotherUser)
456 for _, caller := range []address{regularUser, anotherUser} {
457 testing.SetOriginCaller(caller)
458 testing.SetRealm(testing.NewUserRealm(caller))
459 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
460 DeleteAll(cross(cur))
461 })
462 uassert.Equal(t, 1, GetTotalUserSize())
463 uassert.Equal(t, "Alice", ListUsersByAddresses(regularUser)[0]["nickname"])
464 }
465 testing.SetOriginCaller(testAdmin)
466 testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/user_admin_relay"))
467 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
468 DeleteAll(cross(cur))
469 })
470 uassert.Equal(t, 1, GetTotalUserSize())
471 uassert.Equal(t, "Alice", ListUsersByAddresses(regularUser)[0]["nickname"])
472 testing.SetRealm(testing.NewUserRealm(testAdmin))
473 admin.UnsetOperator(cross(cur), anotherUser)
474}