Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}