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

registry_test.gno

8.62 Kb · 217 lines
  1package gnopm
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7)
  8
  9const pkgA = "gno.land/p/moul/md/v1"
 10
 11func TestRegisterAndRead(t *testing.T) {
 12	r := New()
 13	uassert.Equal(t, 0, r.Size())
 14	uassert.Equal(t, 0, r.Claims())
 15
 16	c, err := r.Register(alice, 100, pkgA, "https://github.com/moul/gno-contracts", sha1Zero, "p/moul/md", "refs/heads/main")
 17	uassert.NoError(t, err)
 18	uassert.Equal(t, pkgA, c.PkgPath)
 19	uassert.Equal(t, int64(100), c.Height)
 20	uassert.Equal(t, int64(100), c.UpdatedAt)
 21	uassert.Equal(t, 1, r.Size())
 22	uassert.Equal(t, 1, r.Claims())
 23
 24	p := r.Package(pkgA)
 25	uassert.True(t, p != nil, "package exists")
 26	uassert.Equal(t, 1, p.Count())
 27	uassert.True(t, p.Claim(alice) != nil, "alice's claim is there")
 28	uassert.True(t, p.Claim(bob) == nil, "bob has not claimed")
 29	uassert.True(t, r.Package("gno.land/p/moul/nope/v0") == nil, "unclaimed path is nil")
 30}
 31
 32// A claimant re-registering must move their own commit and nothing else, and
 33// must not restart the clock: Height dates the claim, UpdatedAt dates the edit,
 34// and telling them apart is how a reader spots a claim nobody has touched since
 35// the first deploy.
 36func TestReregisterUpdatesInPlace(t *testing.T) {
 37	r := New()
 38	r.Register(alice, 100, pkgA, "https://github.com/moul/gno-contracts", sha1Zero, "p/moul/md", "")
 39	c, err := r.Register(alice, 250, pkgA, "https://github.com/moul/gno-contracts", sha1One, "p/moul/md", "refs/tags/v1")
 40	uassert.NoError(t, err)
 41
 42	uassert.Equal(t, sha1One, c.Commit, "commit moved")
 43	uassert.Equal(t, "refs/tags/v1", c.Ref, "ref moved")
 44	uassert.Equal(t, int64(100), c.Height, "height still dates the original claim")
 45	uassert.Equal(t, int64(250), c.UpdatedAt, "updated at dates the edit")
 46	uassert.Equal(t, 1, r.Claims(), "still one claim, not two")
 47	uassert.Equal(t, 1, r.Package(pkgA).Count())
 48}
 49
 50// Open but tagged: a second address may claim the same path. The registry has
 51// no opinion about which is true, and storing both is the point.
 52func TestSeveralClaimantsCoexist(t *testing.T) {
 53	r := New()
 54	r.Register(alice, 100, pkgA, "https://github.com/moul/gno-contracts", sha1Zero, "p/moul/md", "")
 55	r.Register(bob, 110, pkgA, "https://github.com/someone/fork", sha1One, "md", "")
 56
 57	p := r.Package(pkgA)
 58	uassert.Equal(t, 2, p.Count())
 59	uassert.Equal(t, 2, r.Claims())
 60	uassert.Equal(t, 1, r.Size(), "still one package path")
 61	uassert.Equal(t, sha1Zero, p.Claim(alice).Commit)
 62	uassert.Equal(t, sha1One, p.Claim(bob).Commit)
 63}
 64
 65func TestMaxClaimants(t *testing.T) {
 66	r := New()
 67	// Fill the path to the cap with distinct claimants.
 68	base := "g1ubwjz3jmrjwmpuvrsjzgy5qkxf5wrgphzmd2t"
 69	suffix := "023456789acdefgh"
 70	for i := 0; i < MaxClaimants; i++ {
 71		a := address(base + string(suffix[i]))
 72		_, err := r.Register(a, int64(100+i), pkgA, "https://example.com/x", sha1Zero, "", "")
 73		uassert.NoError(t, err, "claimant within the cap")
 74	}
 75	uassert.Equal(t, MaxClaimants, r.Package(pkgA).Count())
 76
 77	_, err := r.Register(carol, 999, pkgA, "https://example.com/x", sha1Zero, "", "")
 78	uassert.ErrorIs(t, err, ErrTooManyClaimants, "one past the cap")
 79
 80	// An address already at the cap can still update: the cap bounds how many
 81	// claimants a path carries, never how often one of them corrects itself.
 82	existing := address(base + string(suffix[0]))
 83	_, err = r.Register(existing, 999, pkgA, "https://example.com/x", sha1One, "", "")
 84	uassert.NoError(t, err, "an existing claimant updates at the cap")
 85}
 86
 87func TestWithdraw(t *testing.T) {
 88	r := New()
 89	r.Register(alice, 100, pkgA, "https://example.com/x", sha1Zero, "", "")
 90	r.Register(bob, 101, pkgA, "https://example.com/y", sha1One, "", "")
 91
 92	uassert.ErrorIs(t, r.Withdraw(carol, pkgA), ErrClaimNotFound, "withdrawing what you never said")
 93	uassert.ErrorIs(t, r.Withdraw(alice, "gno.land/p/moul/nope/v0"), ErrClaimNotFound, "unknown path")
 94
 95	uassert.NoError(t, r.Withdraw(alice, pkgA))
 96	uassert.Equal(t, 1, r.Claims())
 97	uassert.Equal(t, 1, r.Size(), "bob's claim keeps the path alive")
 98	uassert.True(t, r.Package(pkgA).Claim(alice) == nil, "alice is gone")
 99	uassert.True(t, r.Package(pkgA).Claim(bob) != nil, "bob is untouched")
100
101	uassert.NoError(t, r.Withdraw(bob, pkgA))
102	uassert.Equal(t, 0, r.Claims())
103	uassert.Equal(t, 0, r.Size(), "the last withdrawal removes the path")
104	uassert.True(t, r.Package(pkgA) == nil)
105}
106
107func TestRegisterRejectsInvalidInput(t *testing.T) {
108	good := func() (string, string, string, string, string) {
109		return pkgA, "https://github.com/moul/gno-contracts", sha1Zero, "p/moul/md", "refs/heads/main"
110	}
111	cases := []struct {
112		name string
113		mut  func(p, repo, commit, dir, ref *string)
114		want error
115	}{
116		{"bad path", func(p, _, _, _, _ *string) { *p = "nonsense" }, ErrInvalidPkgPath},
117		{"bad repo scheme", func(_, repo, _, _, _ *string) { *repo = "javascript:alert(1)" }, ErrInvalidRepo},
118		{"abbreviated commit", func(_, _, c, _, _ *string) { *c = "0123456" }, ErrInvalidCommit},
119		{"absolute dir", func(_, _, _, d, _ *string) { *d = "/etc" }, ErrInvalidDir},
120		{"unqualified ref", func(_, _, _, _, rf *string) { *rf = "main" }, ErrInvalidRef},
121	}
122	for _, tc := range cases {
123		r := New()
124		p, repo, commit, dir, ref := good()
125		tc.mut(&p, &repo, &commit, &dir, &ref)
126		_, err := r.Register(alice, 100, p, repo, commit, dir, ref)
127		uassert.ErrorIs(t, err, tc.want, tc.name)
128		uassert.Equal(t, 0, r.Claims(), tc.name+": nothing stored")
129	}
130}
131
132func TestOwnedBy(t *testing.T) {
133	names := resolver(map[string]address{"moul": alice})
134	r := New()
135
136	c, _ := r.Register(alice, 100, pkgA, "https://example.com/x", sha1Zero, "", "")
137	uassert.True(t, c.OwnedBy(names), "the namespace holder claiming their own package")
138
139	c2, _ := r.Register(bob, 101, pkgA, "https://example.com/y", sha1One, "", "")
140	uassert.False(t, c2.OwnedBy(names), "a stranger claiming someone else's package")
141
142	// An address namespace belongs to that account with nothing registered, so
143	// it resolves with no name lookup at all.
144	own := "gno.land/r/" + addrNS + "/scratch/v0"
145	c3, err := r.Register(address(addrNS), 102, own, "https://example.com/z", sha1Zero, "", "")
146	uassert.NoError(t, err)
147	uassert.True(t, c3.OwnedBy(nil), "an address namespace needs no resolver")
148
149	c4, _ := r.Register(bob, 103, own, "https://example.com/w", sha1One, "", "")
150	uassert.False(t, c4.OwnedBy(nil), "a stranger in someone's address namespace")
151
152	// An unregistered name is held by nobody, so nobody owns it.
153	other := "gno.land/p/nobody/thing/v0"
154	c5, _ := r.Register(alice, 104, other, "https://example.com/v", sha1Zero, "", "")
155	uassert.False(t, c5.OwnedBy(names), "an unheld namespace is owned by no one")
156	uassert.False(t, c5.OwnedBy(nil), "and a nil resolver never invents ownership")
157
158	// A resolver may return an address AND report the name as not held: that is
159	// what r/sys/users does for a name whose record survives but is no longer
160	// current. The address matching is not enough, the name has to still be
161	// held, and dropping the second half of that test is a silent promotion of
162	// a stranger's claim.
163	stale := func(string) (address, bool) { return alice, false }
164	uassert.False(t, c.OwnedBy(stale), "a resolved but unheld name confers nothing")
165}
166
167func TestSourceURL(t *testing.T) {
168	r := New()
169	c, _ := r.Register(alice, 100, pkgA, "https://github.com/moul/gno-contracts", sha1Zero, "p/moul/md", "")
170	uassert.Equal(t, "https://github.com/moul/gno-contracts/tree/"+sha1Zero+"/p/moul/md", c.SourceURL())
171
172	c2, _ := r.Register(bob, 100, pkgA, "https://github.com/x/y/", sha1One, "", "")
173	uassert.Equal(t, "https://github.com/x/y/tree/"+sha1One, c2.SourceURL(), "root package, trailing slash trimmed")
174}
175
176func TestIteration(t *testing.T) {
177	r := New()
178	paths := []string{
179		"gno.land/p/alice/one/v0",
180		"gno.land/p/moul/md/v1",
181		"gno.land/p/moul/ulist/v0",
182		"gno.land/r/moul/home",
183		"gno.land/p/zed/last/v0",
184	}
185	for i, p := range paths {
186		_, err := r.Register(alice, int64(100+i), p, "https://example.com/x", sha1Zero, "", "")
187		uassert.NoError(t, err, p)
188	}
189
190	var seen []string
191	r.IteratePackages(0, 10, func(p *Package) bool {
192		seen = append(seen, p.PkgPath)
193		return false
194	})
195	uassert.Equal(t, 5, len(seen))
196	uassert.Equal(t, "gno.land/p/alice/one/v0", seen[0], "lexical order")
197	uassert.Equal(t, "gno.land/r/moul/home", seen[4], "r sorts after p")
198
199	// A namespace walk must stop at the prefix boundary rather than running to
200	// the end of the tree: "moul" is followed by "zed" and by the r/ half.
201	var ns []string
202	r.IterateNamespace("gno.land", "p", "moul", func(p *Package) bool {
203		ns = append(ns, p.PkgPath)
204		return false
205	})
206	uassert.Equal(t, 2, len(ns), "only p/moul")
207	uassert.Equal(t, "gno.land/p/moul/md/v1", ns[0])
208	uassert.Equal(t, "gno.land/p/moul/ulist/v0", ns[1])
209
210	// Stopping early stops.
211	count := 0
212	r.IteratePackages(0, 10, func(p *Package) bool {
213		count++
214		return true
215	})
216	uassert.Equal(t, 1, count, "returning true stops the walk")
217}