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}