Search Apps Documentation Source Content File Folder Download Copy Actions Download

valopers_test.gno

22.06 Kb · 699 lines
  1package valopers
  2
  3import (
  4	"chain"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/bptree/v0"
 10	"gno.land/p/nt/ownable/v0/exts/authorizable"
 11	"gno.land/p/nt/testutils/v0"
 12	"gno.land/p/nt/uassert/v0"
 13	"gno.land/p/nt/ufmt/v0"
 14)
 15
 16// Test-local fee constant. Production reads register_fee from sysparams;
 17// these tests use this value as a reference Coin for OriginSend setup
 18// and (when needed) seed it via testing.SetSysParamUint64.
 19var minFee = chain.NewCoin("ugnot", 20*1_000_000)
 20
 21// cur is a zero-value realm used as a placeholder when forwarding to
 22// uassert dispatch helpers that gained an `rlm realm` param. These tests
 23// pass `func()` callbacks (no crossing inside the callback), so rlm is
 24// ignored — a nil realm here is safe.
 25var cur realm
 26
 27// resetState clears realm-level state and zeroes the valoper sys-params
 28// so subtests don't leak through valopers (operator slots),
 29// signingRegistry (signing-address uniqueness), or sys-param values.
 30func resetState() {
 31	valopers = avl.NewTree()
 32	signingRegistry = bptree.NewBPTree32()
 33	testing.SetSysParamUint64("node", "valoper", "register_fee", 0)
 34	testing.SetSysParamUint64("node", "valoper", "rotation_fee", 0)
 35	testing.SetSysParamInt64("node", "valoper", "rotation_period_blocks", 600)
 36}
 37
 38// enableRegisterFee seeds register_fee = minFee.Amount in sysparams
 39// so the Register fee path fires. Subtests that exercise fee
 40// rejection or sufficient-fee acceptance call this after resetState.
 41func enableRegisterFee() {
 42	testing.SetSysParamUint64("node", "valoper", "register_fee", uint64(minFee.Amount))
 43}
 44
 45func validValidatorInfo(t *testing.T) struct {
 46	Moniker     string
 47	Description string
 48	ServerType  string
 49	Address     address
 50	PubKey      string
 51} {
 52	t.Helper()
 53
 54	return struct {
 55		Moniker     string
 56		Description string
 57		ServerType  string
 58		Address     address
 59		PubKey      string
 60	}{
 61		Moniker:     "test-1",
 62		Description: "test-1's description",
 63		ServerType:  ServerTypeOnPrem,
 64		Address:     address("g1sp8v98h2gadm5jggtzz9w5ksexqn68ympsd68h"),
 65		PubKey:      "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqwpdwpd0f9fvqla089ndw5g9hcsufad77fml2vlu73fk8q8sh8v72cza5p",
 66	}
 67}
 68
 69func TestValopers_Register(cur realm, t *testing.T) {
 70	t.Run("already a valoper", func(cur realm, t *testing.T) {
 71		resetState()
 72
 73		info := validValidatorInfo(t)
 74		testing.SetRealm(testing.NewUserRealm(info.Address))
 75
 76		v := Valoper{
 77			Moniker:         info.Moniker,
 78			Description:     info.Description,
 79			ServerType:      info.ServerType,
 80			OperatorAddress: info.Address,
 81			SigningPubKey:   info.PubKey,
 82			KeepRunning:     true,
 83		}
 84
 85		// Add the valoper directly to the slot.
 86		valopers.Set(v.OperatorAddress.String(), v)
 87
 88		// Send coins.
 89		testing.SetOriginSend(chain.Coins{minFee})
 90
 91		uassert.AbortsWithMessage(t, cur, ErrValoperExists.Error(), func() {
 92			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
 93		})
 94	})
 95
 96	t.Run("no coins deposited", func(cur realm, t *testing.T) {
 97		resetState()
 98		enableRegisterFee()
 99
100		info := validValidatorInfo(t)
101		testing.SetRealm(testing.NewUserRealm(info.Address))
102
103		// Send no coins.
104		testing.SetOriginSend(chain.Coins{chain.NewCoin("ugnot", 0)})
105
106		uassert.AbortsWithMessage(t, cur, ufmt.Sprintf("payment must not be less than %d%s", minFee.Amount, minFee.Denom), func() {
107			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
108		})
109	})
110
111	t.Run("insufficient coins amount deposited", func(cur realm, t *testing.T) {
112		resetState()
113		enableRegisterFee()
114
115		info := validValidatorInfo(t)
116		testing.SetRealm(testing.NewUserRealm(info.Address))
117
118		// Send invalid coins.
119		testing.SetOriginSend(chain.Coins{chain.NewCoin("ugnot", minFee.Amount-1)})
120
121		uassert.AbortsWithMessage(t, cur, ufmt.Sprintf("payment must not be less than %d%s", minFee.Amount, minFee.Denom), func() {
122			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
123		})
124	})
125
126	t.Run("coin amount deposited is not ugnot", func(cur realm, t *testing.T) {
127		resetState()
128		enableRegisterFee()
129
130		info := validValidatorInfo(t)
131		testing.SetRealm(testing.NewUserRealm(info.Address))
132
133		// Send invalid coins.
134		testing.SetOriginSend(chain.Coins{chain.NewCoin("gnogno", minFee.Amount)})
135
136		uassert.AbortsWithMessage(t, cur, "incompatible coin denominations: gnogno, ugnot", func() {
137			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
138		})
139	})
140
141	t.Run("squat guard rejects mismatched OriginCaller", func(cur realm, t *testing.T) {
142		resetState()
143
144		info := validValidatorInfo(t)
145		// Caller is NOT info.Address: post-genesis squat guard fires.
146		testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("attacker")))
147		testing.SetOriginSend(chain.Coins{minFee})
148
149		uassert.AbortsWithMessage(t, cur, ErrOperatorSquatGuard.Error(), func() {
150			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
151		})
152	})
153
154	t.Run("successful registration", func(cur realm, t *testing.T) {
155		resetState()
156
157		info := validValidatorInfo(t)
158		testing.SetRealm(testing.NewUserRealm(info.Address))
159
160		// Send coins.
161		testing.SetOriginSend(chain.Coins{minFee})
162
163		uassert.NotAborts(t, cur, func() {
164			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
165		})
166
167		uassert.NotPanics(t, cur, func() {
168			valoper := GetByAddr(info.Address)
169
170			uassert.Equal(t, info.Moniker, valoper.Moniker)
171			uassert.Equal(t, info.Description, valoper.Description)
172			uassert.Equal(t, info.ServerType, valoper.ServerType)
173			uassert.Equal(t, info.Address, valoper.OperatorAddress)
174			uassert.Equal(t, info.PubKey, valoper.SigningPubKey)
175			uassert.Equal(t, true, valoper.KeepRunning)
176
177			// SigningAddress is derived from the pubkey and present.
178			derived, err := chain.PubKeyAddress(info.PubKey)
179			uassert.NoError(t, err)
180			uassert.Equal(t, derived, valoper.SigningAddress)
181
182			// signingRegistry tracks the active entry.
183			_, exists := signingRegistry.Get(derived.String())
184			uassert.True(t, exists, "signingRegistry must contain the active entry")
185		})
186	})
187
188	t.Run("signing-key reuse rejected", func(cur realm, t *testing.T) {
189		resetState()
190
191		info := validValidatorInfo(t)
192		testing.SetRealm(testing.NewUserRealm(info.Address))
193		testing.SetOriginSend(chain.Coins{minFee})
194
195		// First registration succeeds.
196		uassert.NotAborts(t, cur, func() {
197			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
198		})
199
200		// Second attempt with a different operator addr but the same
201		// pubkey must fail signingRegistry uniqueness.
202		other := testutils.TestAddress("other-op")
203		testing.SetRealm(testing.NewUserRealm(other))
204		testing.SetOriginSend(chain.Coins{minFee})
205
206		uassert.AbortsWithMessage(t, cur, ErrSigningKeyTaken.Error(), func() {
207			Register(cross(cur), info.Moniker, info.Description, info.ServerType, other, info.PubKey)
208		})
209	})
210
211	t.Run("front-running guard rejects post-genesis if signing addr already validates", func(cur realm, t *testing.T) {
212		resetState()
213
214		info := validValidatorInfo(t)
215		// Seed v3's valset:current with the very signing address that
216		// `info.PubKey` derives to (g1sp8v98...). Any post-genesis
217		// Register attempting the same pubkey now trips
218		// `ChainHeight()>0 && validators.IsValidator(signingAddr)`.
219		testing.SetSysParamStrings("node", "valset", "current", []string{info.PubKey + ":1"})
220
221		testing.SetRealm(testing.NewUserRealm(info.Address))
222		testing.SetOriginSend(chain.Coins{minFee})
223
224		uassert.AbortsWithMessage(t, cur, ErrFrontrunValidator.Error(), func() {
225			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
226		})
227
228		// Cleanup: clear the seeded valset to avoid leaking into
229		// later subtests if the package state isn't reset between
230		// them in this test runner mode.
231		testing.SetSysParamStrings("node", "valset", "current", []string{})
232	})
233}
234
235func TestValopers_Register_AuthOwnerIsOperatorAddress(cur realm, t *testing.T) {
236	// Pin: the Authorizable owner is bound to the OperatorAddress
237	// (the addr arg), NOT to OriginCaller. This matters in the
238	// genesis-mode deployer pattern: one signer (e.g., the hardfork
239	// ceremony deployer) registers profiles for many operators.
240	// Each operator must end up on their own profile's auth list
241	// so they can manage it post-genesis without depending on the
242	// deployer.
243	t.Run("genesis deployer pattern: operator (not deployer) is owner", func(cur realm, t *testing.T) {
244		resetState()
245
246		info := validValidatorInfo(t)
247		deployer := testutils.TestAddress("deployer")
248
249		// Genesis mode: ChainHeight()==0 bypasses the squat guard so
250		// deployer (OriginCaller) can register a profile for a
251		// different operator addr.
252		testing.SetHeight(0)
253		testing.SetRealm(testing.NewUserRealm(deployer))
254		testing.SetOriginCaller(deployer)
255		testing.SetOriginSend(chain.Coins{minFee})
256
257		uassert.NotPanics(t, cur, func() {
258			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
259		})
260
261		// Auth owner must be the operator addr (info.Address), NOT
262		// the deployer.
263		v := GetByAddr(info.Address)
264		uassert.Equal(t, info.Address.String(), v.Auth().Owner().String(),
265			"auth owner must equal OperatorAddress, not OriginCaller")
266
267		// Operator can manage their own profile post-genesis.
268		testing.SetHeight(100)
269		testing.SetRealm(testing.NewUserRealm(info.Address))
270		testing.SetOriginCaller(info.Address)
271		uassert.NotPanics(t, cur, func() {
272			UpdateMoniker(cross(cur), info.Address, "operator-renamed")
273		})
274		uassert.Equal(t, "operator-renamed", GetByAddr(info.Address).Moniker)
275
276		// Deployer cannot manage the operator's profile (not on the
277		// auth list).
278		testing.SetRealm(testing.NewUserRealm(deployer))
279		testing.SetOriginCaller(deployer)
280		uassert.AbortsContains(t, cur, "caller is not in authorized list", func() {
281			UpdateMoniker(cross(cur), info.Address, "deployer-attempt")
282		})
283	})
284
285	t.Run("post-genesis self-Register: operator is owner", func(cur realm, t *testing.T) {
286		// At H>0 the squat guard forces OriginCaller==addr, so owner
287		// would be the same regardless. This subtest pins that the
288		// post-genesis behavior is unchanged.
289		resetState()
290
291		info := validValidatorInfo(t)
292		testing.SetHeight(100)
293		testing.SetRealm(testing.NewUserRealm(info.Address))
294		testing.SetOriginCaller(info.Address)
295		testing.SetOriginSend(chain.Coins{minFee})
296
297		uassert.NotPanics(t, cur, func() {
298			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
299		})
300
301		v := GetByAddr(info.Address)
302		uassert.Equal(t, info.Address.String(), v.Auth().Owner().String())
303	})
304}
305
306func TestValopers_UpdateAuthMembers(cur realm, t *testing.T) {
307	test2Address := testutils.TestAddress("test2")
308
309	t.Run("unauthorized member adds member", func(cur realm, t *testing.T) {
310		resetState()
311
312		info := validValidatorInfo(t)
313		testing.SetRealm(testing.NewUserRealm(info.Address))
314		testing.SetOriginSend(chain.Coins{minFee})
315
316		// Add the valoper.
317		uassert.NotPanics(t, cur, func() {
318			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
319		})
320
321		// A different caller (not on the auth list) tries to add a member.
322		testing.SetRealm(testing.NewUserRealm(test2Address))
323
324		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func() {
325			AddToAuthList(cross(cur), info.Address, test2Address)
326		})
327	})
328
329	t.Run("unauthorized member deletes member", func(cur realm, t *testing.T) {
330		resetState()
331
332		info := validValidatorInfo(t)
333		testing.SetRealm(testing.NewUserRealm(info.Address))
334		testing.SetOriginSend(chain.Coins{minFee})
335
336		uassert.NotPanics(t, cur, func() {
337			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
338		})
339
340		uassert.NotPanics(t, cur, func() {
341			AddToAuthList(cross(cur), info.Address, test2Address)
342		})
343
344		// A different caller tries to delete a member.
345		testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("attacker")))
346
347		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotSuperuser.Error(), func() {
348			DeleteFromAuthList(cross(cur), info.Address, test2Address)
349		})
350	})
351
352	t.Run("authorized member adds member", func(cur realm, t *testing.T) {
353		resetState()
354
355		info := validValidatorInfo(t)
356		testing.SetRealm(testing.NewUserRealm(info.Address))
357		testing.SetOriginSend(chain.Coins{minFee})
358
359		uassert.NotPanics(t, cur, func() {
360			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
361		})
362
363		uassert.NotPanics(t, cur, func() {
364			AddToAuthList(cross(cur), info.Address, test2Address)
365		})
366
367		testing.SetRealm(testing.NewUserRealm(test2Address))
368
369		newMoniker := "new moniker"
370		uassert.NotPanics(t, cur, func() {
371			UpdateMoniker(cross(cur), info.Address, newMoniker)
372		})
373
374		uassert.NotPanics(t, cur, func() {
375			valoper := GetByAddr(info.Address)
376			uassert.Equal(t, newMoniker, valoper.Moniker)
377		})
378	})
379}
380
381func TestValopers_UpdateMoniker(cur realm, t *testing.T) {
382	test2Address := testutils.TestAddress("test2")
383
384	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
385		resetState()
386
387		info := validValidatorInfo(t)
388
389		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
390			UpdateMoniker(cross(cur), info.Address, "new moniker")
391		})
392	})
393
394	t.Run("invalid caller", func(cur realm, t *testing.T) {
395		resetState()
396
397		info := validValidatorInfo(t)
398		testing.SetRealm(testing.NewUserRealm(info.Address))
399		testing.SetOriginSend(chain.Coins{minFee})
400
401		uassert.NotPanics(t, cur, func() {
402			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
403		})
404
405		// Change the caller to someone not on the auth list.
406		testing.SetRealm(testing.NewUserRealm(test2Address))
407
408		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
409			UpdateMoniker(cross(cur), info.Address, "new moniker")
410		})
411	})
412
413	t.Run("invalid moniker", func(cur realm, t *testing.T) {
414		resetState()
415
416		info := validValidatorInfo(t)
417		testing.SetRealm(testing.NewUserRealm(info.Address))
418		testing.SetOriginSend(chain.Coins{minFee})
419
420		uassert.NotPanics(t, cur, func() {
421			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
422		})
423
424		invalidMonikers := []string{
425			"",     // Empty
426			"    ", // Whitespace
427			"a",    // Too short
428			"a very long moniker that is longer than 32 characters", // Too long
429			"!@#$%^&*()+{}|:<>?/.,;'",                               // Invalid characters
430			" space in front",
431			"space in back ",
432		}
433
434		for _, invalidMoniker := range invalidMonikers {
435			uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func() {
436				UpdateMoniker(cross(cur), info.Address, invalidMoniker)
437			})
438		}
439	})
440
441	t.Run("too long moniker", func(cur realm, t *testing.T) {
442		resetState()
443
444		info := validValidatorInfo(t)
445		testing.SetRealm(testing.NewUserRealm(info.Address))
446		testing.SetOriginSend(chain.Coins{minFee})
447
448		uassert.NotPanics(t, cur, func() {
449			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
450		})
451
452		uassert.AbortsWithMessage(t, cur, ErrInvalidMoniker.Error(), func() {
453			UpdateMoniker(cross(cur), info.Address, strings.Repeat("a", MonikerMaxLength+1))
454		})
455	})
456
457	t.Run("successful update", func(cur realm, t *testing.T) {
458		resetState()
459
460		info := validValidatorInfo(t)
461		testing.SetRealm(testing.NewUserRealm(info.Address))
462		testing.SetOriginSend(chain.Coins{minFee})
463
464		uassert.NotPanics(t, cur, func() {
465			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
466		})
467
468		newMoniker := "new moniker"
469		uassert.NotPanics(t, cur, func() {
470			UpdateMoniker(cross(cur), info.Address, newMoniker)
471		})
472
473		uassert.NotPanics(t, cur, func() {
474			valoper := GetByAddr(info.Address)
475			uassert.Equal(t, newMoniker, valoper.Moniker)
476		})
477	})
478}
479
480func TestValopers_UpdateDescription(cur realm, t *testing.T) {
481	test2Address := testutils.TestAddress("test2")
482
483	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
484		resetState()
485
486		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
487			UpdateDescription(cross(cur), validValidatorInfo(t).Address, "new description")
488		})
489	})
490
491	t.Run("invalid caller", func(cur realm, t *testing.T) {
492		resetState()
493
494		info := validValidatorInfo(t)
495		testing.SetRealm(testing.NewUserRealm(info.Address))
496		testing.SetOriginSend(chain.Coins{minFee})
497
498		uassert.NotPanics(t, cur, func() {
499			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
500		})
501
502		testing.SetRealm(testing.NewUserRealm(test2Address))
503
504		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
505			UpdateDescription(cross(cur), info.Address, "new description")
506		})
507	})
508
509	t.Run("empty description", func(cur realm, t *testing.T) {
510		resetState()
511
512		info := validValidatorInfo(t)
513		testing.SetRealm(testing.NewUserRealm(info.Address))
514		testing.SetOriginSend(chain.Coins{minFee})
515
516		uassert.NotPanics(t, cur, func() {
517			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
518		})
519
520		uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func() {
521			UpdateDescription(cross(cur), info.Address, "")
522		})
523	})
524
525	t.Run("too long description", func(cur realm, t *testing.T) {
526		resetState()
527
528		info := validValidatorInfo(t)
529		testing.SetRealm(testing.NewUserRealm(info.Address))
530		testing.SetOriginSend(chain.Coins{minFee})
531
532		uassert.NotPanics(t, cur, func() {
533			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
534		})
535
536		uassert.AbortsWithMessage(t, cur, ErrInvalidDescription.Error(), func() {
537			UpdateDescription(cross(cur), info.Address, strings.Repeat("a", DescriptionMaxLength+1))
538		})
539	})
540
541	t.Run("successful update", func(cur realm, t *testing.T) {
542		resetState()
543
544		info := validValidatorInfo(t)
545		testing.SetRealm(testing.NewUserRealm(info.Address))
546		testing.SetOriginSend(chain.Coins{minFee})
547
548		uassert.NotPanics(t, cur, func() {
549			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
550		})
551
552		newDescription := "new description"
553		uassert.NotPanics(t, cur, func() {
554			UpdateDescription(cross(cur), info.Address, newDescription)
555		})
556
557		uassert.NotPanics(t, cur, func() {
558			valoper := GetByAddr(info.Address)
559			uassert.Equal(t, newDescription, valoper.Description)
560		})
561	})
562}
563
564func TestValopers_UpdateKeepRunning(cur realm, t *testing.T) {
565	test2Address := testutils.TestAddress("test2")
566
567	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
568		resetState()
569
570		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
571			UpdateKeepRunning(cross(cur), validValidatorInfo(t).Address, false)
572		})
573	})
574
575	t.Run("invalid caller", func(cur realm, t *testing.T) {
576		resetState()
577
578		info := validValidatorInfo(t)
579		testing.SetRealm(testing.NewUserRealm(info.Address))
580		testing.SetOriginSend(chain.Coins{minFee})
581
582		uassert.NotPanics(t, cur, func() {
583			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
584		})
585
586		testing.SetRealm(testing.NewUserRealm(test2Address))
587
588		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
589			UpdateKeepRunning(cross(cur), info.Address, false)
590		})
591	})
592
593	t.Run("successful update", func(cur realm, t *testing.T) {
594		resetState()
595
596		info := validValidatorInfo(t)
597		testing.SetRealm(testing.NewUserRealm(info.Address))
598		testing.SetOriginSend(chain.Coins{minFee})
599
600		uassert.NotPanics(t, cur, func() {
601			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
602		})
603
604		uassert.NotPanics(t, cur, func() {
605			UpdateKeepRunning(cross(cur), info.Address, false)
606		})
607
608		uassert.NotPanics(t, cur, func() {
609			valoper := GetByAddr(info.Address)
610			uassert.Equal(t, false, valoper.KeepRunning)
611		})
612	})
613}
614
615func TestValopers_UpdateServerType(cur realm, t *testing.T) {
616	test2Address := testutils.TestAddress("test2")
617
618	t.Run("non-existing valoper", func(cur realm, t *testing.T) {
619		resetState()
620
621		uassert.AbortsWithMessage(t, cur, ErrValoperMissing.Error(), func() {
622			UpdateServerType(cross(cur), validValidatorInfo(t).Address, ServerTypeCloud)
623		})
624	})
625
626	t.Run("invalid caller", func(cur realm, t *testing.T) {
627		resetState()
628
629		info := validValidatorInfo(t)
630		testing.SetRealm(testing.NewUserRealm(info.Address))
631		testing.SetOriginSend(chain.Coins{minFee})
632
633		uassert.NotPanics(t, cur, func() {
634			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
635		})
636
637		testing.SetRealm(testing.NewUserRealm(test2Address))
638
639		uassert.AbortsWithMessage(t, cur, authorizable.ErrNotInAuthList.Error(), func() {
640			UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
641		})
642	})
643
644	t.Run("invalid server type", func(cur realm, t *testing.T) {
645		resetState()
646
647		info := validValidatorInfo(t)
648		testing.SetRealm(testing.NewUserRealm(info.Address))
649		testing.SetOriginSend(chain.Coins{minFee})
650
651		uassert.NotPanics(t, cur, func() {
652			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
653		})
654
655		invalidServerTypes := []string{
656			"",
657			"invalid",
658			"Cloud",      // case sensitive
659			"ON-PREM",    // case sensitive
660			"datacenter", // wrong format
661		}
662
663		for _, invalidType := range invalidServerTypes {
664			uassert.AbortsWithMessage(t, cur, ErrInvalidServerType.Error(), func() {
665				UpdateServerType(cross(cur), info.Address, invalidType)
666			})
667		}
668	})
669
670	t.Run("successful update", func(cur realm, t *testing.T) {
671		resetState()
672
673		info := validValidatorInfo(t)
674		testing.SetRealm(testing.NewUserRealm(info.Address))
675		testing.SetOriginSend(chain.Coins{minFee})
676
677		uassert.NotPanics(t, cur, func() {
678			Register(cross(cur), info.Moniker, info.Description, info.ServerType, info.Address, info.PubKey)
679		})
680
681		uassert.NotPanics(t, cur, func() {
682			UpdateServerType(cross(cur), info.Address, ServerTypeCloud)
683		})
684
685		uassert.NotPanics(t, cur, func() {
686			valoper := GetByAddr(info.Address)
687			uassert.Equal(t, ServerTypeCloud, valoper.ServerType)
688		})
689
690		uassert.NotPanics(t, cur, func() {
691			UpdateServerType(cross(cur), info.Address, ServerTypeDataCenter)
692		})
693
694		uassert.NotPanics(t, cur, func() {
695			valoper := GetByAddr(info.Address)
696			uassert.Equal(t, ServerTypeDataCenter, valoper.ServerType)
697		})
698	})
699}