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

forge_test.gno

6.66 Kb · 181 lines
  1package forge
  2
  3import (
  4	"testing"
  5
  6	fg "gno.land/p/moul/forge/v0"
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10// TestLifecycle drives the realm exactly as a user would: one signed call at a
 11// time, each from a different address.
 12func TestLifecycle(cur realm, t *testing.T) {
 13	reset()
 14
 15	testing.SetRealm(testing.NewUserRealm(alice))
 16	CreateRepo(cross(cur), demoID, "a demo repo", "")
 17	uassert.True(t, HasRepo(demoID))
 18	uassert.Equal(t, 1, RepoCount())
 19	uassert.Equal(t, "", LogHead(demoID), "a fresh repo has an empty log")
 20
 21	// The creator is the owner, so roles are theirs to hand out.
 22	SetMember(cross(cur), demoID, carol, "writer")
 23	SetMember(cross(cur), demoID, bob, "maintainer")
 24	SetMirrors(cross(cur), demoID, "https://github.com/moul/demo.git, ipfs://bafyfakefakefakefakefakefakefakefakefake")
 25	uassert.Equal(t, 2, len(f.Repo(demoID).Mirrors), "comma-separated lists are how a tx passes a slice")
 26
 27	// A writer creates the branch, then moves it by compare-and-swap.
 28	testing.SetRealm(testing.NewUserRealm(carol))
 29	SetRef(cross(cur), demoID, "refs/heads/main", "", oid("a"), "initial import")
 30	uassert.Equal(t, oid("a"), RefOID(demoID, "refs/heads/main"))
 31	head := LogHead(demoID)
 32	uassert.NotEqual(t, "", head)
 33
 34	testing.SetRealm(testing.NewUserRealm(carol))
 35	SetRef(cross(cur), demoID, "refs/heads/main", oid("a"), oid("b"), "second commit")
 36	uassert.NotEqual(t, head, LogHead(demoID), "each entry advances the chain digest")
 37
 38	// A stale expectation aborts: this is the lost-push case, caught.
 39	testing.SetRealm(testing.NewUserRealm(carol))
 40	uassert.AbortsContains(t, cur, "stale ref", func() {
 41		SetRef(cross(cur), demoID, "refs/heads/main", oid("a"), oid("c"), "")
 42	})
 43
 44	// A stranger cannot move a ref at all.
 45	testing.SetRealm(testing.NewUserRealm(eve))
 46	uassert.AbortsContains(t, cur, "unauthorized", func() {
 47		SetRef(cross(cur), demoID, "refs/heads/main", oid("b"), oid("c"), "")
 48	})
 49
 50	// ...but anyone may file an issue and propose a change.
 51	testing.SetRealm(testing.NewUserRealm(eve))
 52	issueID := OpenIssue(cross(cur), demoID, "the log needs a UI", "hard to read over RPC", "ux,help wanted")
 53	uassert.Equal(t, int64(0), issueID)
 54
 55	testing.SetRealm(testing.NewUserRealm(eve))
 56	changeID := OpenChange(cross(cur), demoID, "add a log view", "", "eve/demo", "refs/heads/logview", oid("c"), "refs/heads/main")
 57	uassert.Equal(t, int64(0), changeID)
 58
 59	// A maintainer's approval satisfies the default policy; the merge moves the
 60	// ref and lands in the same log as every other move.
 61	testing.SetRealm(testing.NewUserRealm(bob))
 62	ReviewChange(cross(cur), demoID, changeID, "approve", "lgtm")
 63	testing.SetRealm(testing.NewUserRealm(bob))
 64	MergeChange(cross(cur), demoID, changeID, oid("b"), oid("d"), "merge change 0")
 65
 66	uassert.Equal(t, oid("d"), RefOID(demoID, "refs/heads/main"))
 67	r := f.Repo(demoID)
 68	uassert.Equal(t, 3, r.LogSize())
 69	uassert.Equal(t, fg.KindMerge, r.LogEntryAt(2).Kind)
 70	uassert.Equal(t, bob.String(), r.LogEntryAt(2).Actor.String(), "the log records who merged, not who proposed")
 71	ok, _ := r.VerifyLog()
 72	uassert.True(t, ok, "the digest chain verifies end to end")
 73
 74	// Issue triage: the maintainer closes what the reporter opened.
 75	testing.SetRealm(testing.NewUserRealm(bob))
 76	CloseIssue(cross(cur), demoID, issueID)
 77	uassert.Equal(t, 0, r.OpenIssueCount())
 78}
 79
 80// TestHeightsComeFromTheChain checks the realm stamps entries with the block it
 81// ran in. SkipHeights is relative and there is no absolute height getter, so the
 82// assertion is on the delta.
 83func TestHeightsComeFromTheChain(cur realm, t *testing.T) {
 84	reset()
 85
 86	testing.SetRealm(testing.NewUserRealm(alice))
 87	CreateRepo(cross(cur), heightsID, "", "")
 88	testing.SetRealm(testing.NewUserRealm(alice))
 89	SetRef(cross(cur), heightsID, "refs/heads/main", "", oid("a"), "")
 90
 91	testing.SkipHeights(7)
 92
 93	testing.SetRealm(testing.NewUserRealm(alice))
 94	SetRef(cross(cur), heightsID, "refs/heads/main", oid("a"), oid("b"), "")
 95
 96	r := f.Repo(heightsID)
 97	first := r.LogEntryAt(0).Height
 98	second := r.LogEntryAt(1).Height
 99	uassert.Equal(t, int64(7), second-first, "seven blocks passed between the two writes")
100}
101
102// TestUnknownRepoAborts: every write resolves the repo first, so a typo fails
103// loudly instead of creating something.
104func TestUnknownRepoAborts(cur realm, t *testing.T) {
105	reset()
106
107	testing.SetRealm(testing.NewUserRealm(alice))
108	uassert.AbortsContains(t, cur, "repo not found", func() {
109		SetRef(cross(cur), ghostID, "refs/heads/main", "", oid("a"), "")
110	})
111	uassert.Equal(t, "", RefOID(ghostID, "refs/heads/main"))
112	uassert.Equal(t, "", LogHead(ghostID))
113	uassert.False(t, HasRepo(ghostID))
114}
115
116// TestRoleParsing: the role argument is a string on the wire, so a typo must
117// abort rather than silently grant nothing.
118func TestRoleParsing(cur realm, t *testing.T) {
119	reset()
120
121	testing.SetRealm(testing.NewUserRealm(alice))
122	CreateRepo(cross(cur), rolesID, "", "")
123	testing.SetRealm(testing.NewUserRealm(alice))
124	uassert.AbortsContains(t, cur, "invalid role", func() {
125		SetMember(cross(cur), rolesID, bob, "commiter")
126	})
127	testing.SetRealm(testing.NewUserRealm(alice))
128	SetMember(cross(cur), rolesID, bob, "maintainer")
129	uassert.Equal(t, "maintainer", f.Repo(rolesID).RoleOf(bob).String())
130}
131
132func TestSplitList(t *testing.T) {
133	cases := []struct {
134		in   string
135		want int
136	}{
137		{"", 0},
138		{"   ", 0},
139		{"a", 1},
140		{"a,b", 2},
141		{" a , b ,, c ", 3},
142	}
143	for _, tc := range cases {
144		uassert.Equal(t, tc.want, len(splitList(tc.in)), tc.in)
145	}
146}
147
148// TestNamespaceOwnership: a namespace is an r/sys/users name you hold or your
149// own address, and nothing else. No name is registered in a unit test, so the
150// name path is exercised through its rejection.
151func TestNamespaceOwnership(cur realm, t *testing.T) {
152	reset()
153
154	testing.SetRealm(testing.NewUserRealm(alice))
155	CreateRepo(cross(cur), demoID, "under alice's own address", "")
156	uassert.True(t, HasRepo(demoID))
157
158	testing.SetRealm(testing.NewUserRealm(alice))
159	uassert.AbortsContains(t, cur, "namespace is not yours", func() {
160		CreateRepo(cross(cur), bob.String()+"/squat", "", "")
161	}, "an address namespace belongs to that address")
162
163	testing.SetRealm(testing.NewUserRealm(alice))
164	uassert.AbortsContains(t, cur, "namespace is not yours", func() {
165		CreateRepo(cross(cur), "moul/forge", "", "")
166	}, "an unregistered name belongs to nobody")
167
168	testing.SetRealm(testing.NewUserRealm(bob))
169	uassert.AbortsContains(t, cur, "namespace is not yours", func() {
170		Fork(cross(cur), demoID, alice.String()+"/fork")
171	}, "a fork lands in the forker's namespace, not the parent's")
172
173	testing.SetRealm(testing.NewUserRealm(bob))
174	Fork(cross(cur), demoID, bob.String()+"/fork")
175	uassert.True(t, HasRepo(bob.String()+"/fork"))
176
177	testing.SetRealm(testing.NewUserRealm(alice))
178	uassert.AbortsContains(t, cur, "invalid repo id", func() {
179		CreateRepo(cross(cur), "nonamespace", "", "")
180	})
181}