1package handles23import (4 "testing"56 "gno.land/p/nt/avl/v0"7 "gno.land/p/nt/testutils/v0"8)910var (11 alice = testutils.TestAddress("alice")12 bob = testutils.TestAddress("bob")13)1415// reset clears package state between tests so cases stay independent.16func reset() {17 byHandle = avl.Tree{}18 byOwner = avl.Tree{}19}2021func TestValidHandle(t *testing.T) {22 cases := map[string]bool{23 "moul": true,24 "m": false, // too short25 "ab": false, // too short26 "Moul": false, // uppercase27 "1moul": false, // starts with digit28 "moul1": true,29 "moul-1": false, // hyphen30 "averylonghandlethatistoolong12": false, // too long31 }32 for h, want := range cases {33 if got := validHandle(h); got != want {34 t.Errorf("validHandle(%q) = %v, want %v", h, got, want)35 }36 }37}3839func TestRegisterAndLookup(cur realm, t *testing.T) {40 reset()4142 testing.SetRealm(testing.NewUserRealm(alice))43 Register(cross(cur), "moul")4445 owner, ok := OwnerOf("moul")46 if !ok || owner != alice {47 t.Fatalf("OwnerOf(moul) = %v, %v; want %v, true", owner, ok, alice)48 }49 handle, ok := HandleOf(alice)50 if !ok || handle != "moul" {51 t.Fatalf("HandleOf(alice) = %q, %v; want moul, true", handle, ok)52 }53 if got := Count(); got != 1 {54 t.Fatalf("Count() = %d, want 1", got)55 }56}5758func TestRegisterRejectsTakenHandle(cur realm, t *testing.T) {59 reset()6061 testing.SetRealm(testing.NewUserRealm(alice))62 Register(cross(cur), "moul")6364 testing.SetRealm(testing.NewUserRealm(bob))65 defer func() {66 if r := recover(); r == nil {67 t.Errorf("expected panic registering a taken handle")68 }69 }()70 Register(cur, "moul")71}7273func TestRegisterRejectsSecondHandleForSameOwner(cur realm, t *testing.T) {74 reset()7576 testing.SetRealm(testing.NewUserRealm(alice))77 Register(cross(cur), "moul")7879 testing.SetRealm(testing.NewUserRealm(alice))80 defer func() {81 if r := recover(); r == nil {82 t.Errorf("expected panic when the same owner registers twice")83 }84 }()85 Register(cur, "second")86}8788func TestTransferAndRelease(cur realm, t *testing.T) {89 reset()9091 testing.SetRealm(testing.NewUserRealm(alice))92 Register(cross(cur), "moul")9394 testing.SetRealm(testing.NewUserRealm(alice))95 Transfer(cross(cur), bob)9697 if _, ok := HandleOf(alice); ok {98 t.Fatalf("alice should no longer own a handle after transfer")99 }100 handle, ok := HandleOf(bob)101 if !ok || handle != "moul" {102 t.Fatalf("HandleOf(bob) = %q, %v; want moul, true", handle, ok)103 }104105 testing.SetRealm(testing.NewUserRealm(bob))106 Release(cross(cur))107108 if Count() != 0 {109 t.Fatalf("Count() = %d, want 0 after release", Count())110 }111 if _, ok := OwnerOf("moul"); ok {112 t.Fatalf("moul should be unclaimed after release")113 }114}115116func TestSetBioAndRender(cur realm, t *testing.T) {117 reset()118119 testing.SetRealm(testing.NewUserRealm(alice))120 Register(cross(cur), "moul")121 SetBio(cross(cur), "building gno stuff")122123 out := Render("/moul")124 if !contains(out, "moul") || !contains(out, "building gno stuff") {125 t.Fatalf("Render(/moul) missing expected content: %s", out)126 }127128 index := Render("")129 if !contains(index, "moul") {130 t.Fatalf("Render('') missing handle in index: %s", index)131 }132133 missing := Render("/nobody")134 if !contains(missing, "No handle named") {135 t.Fatalf("Render(/nobody) should report missing handle: %s", missing)136 }137}138139func contains(s, substr string) bool {140 for i := 0; i+len(substr) <= len(s); i++ {141 if s[i:i+len(substr)] == substr {142 return true143 }144 }145 return false146}147Count() int
HandleOf(addr string) (string, bool)
OwnerOf(handle string) (string, bool)
Register(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}, handle string)
Release(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})
Render(path string) string
SetBio(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}, bio string)
Transfer(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}, to 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.