govdao_test.gno
10.04 Kb · 311 lines
1package impl
2
3import (
4 "fmt"
5 "strings"
6 "testing"
7
8 "gno.land/p/nt/testutils/v0"
9 "gno.land/p/nt/urequire/v0"
10 "gno.land/r/gov/dao"
11 "gno.land/r/gov/dao/v3/memberstore"
12)
13
14func init() {
15 loadMembers()
16
17 dao.UpdateImpl(cross, dao.UpdateRequest{
18 DAO: govDAO,
19 AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"},
20 })
21}
22
23var (
24 m1 = testutils.TestAddress("m1")
25 m11 = testutils.TestAddress("m1.1")
26 m111 = testutils.TestAddress("m1.1.1")
27 m1111 = testutils.TestAddress("m1.1.1.1")
28 m2 = testutils.TestAddress("m2")
29 m3 = testutils.TestAddress("m3")
30 m4 = testutils.TestAddress("m4")
31 m5 = testutils.TestAddress("m5")
32 m6 = testutils.TestAddress("m6")
33
34 noMember = testutils.TestAddress("nm1")
35)
36
37func loadMembers() {
38 // This is needed because state is saved between unit tests,
39 // and we want to avoid having real members used on tests
40 mstore := memberstore.Get()
41 mstore.DeleteAll()
42
43 mstore.SetTier(memberstore.T1)
44 mstore.SetTier(memberstore.T2)
45 mstore.SetTier(memberstore.T3)
46
47 mstore.SetMember(memberstore.T1, m1, memberByTier(memberstore.T1))
48 mstore.SetMember(memberstore.T1, m11, memberByTier(memberstore.T1))
49 mstore.SetMember(memberstore.T1, m111, memberByTier(memberstore.T1))
50 mstore.SetMember(memberstore.T1, m1111, memberByTier(memberstore.T1))
51
52 mstore.SetMember(memberstore.T2, m2, memberByTier(memberstore.T2))
53 mstore.SetMember(memberstore.T2, m3, memberByTier(memberstore.T2))
54 mstore.SetMember(memberstore.T3, m4, memberByTier(memberstore.T3))
55 mstore.SetMember(memberstore.T3, m5, memberByTier(memberstore.T3))
56 mstore.SetMember(memberstore.T3, m6, memberByTier(memberstore.T3))
57}
58
59func TestCreateProposalAndVote(cur realm, t *testing.T) {
60 loadMembers()
61
62 portfolio := "# This is my portfolio:\n\n- THINGS"
63
64 testing.SetOriginCaller(noMember)
65 testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl"))
66
67 nm1 := testutils.TestAddress("nm1")
68
69 urequire.AbortsWithMessage(t, "Only T1 and T2 members can be added by proposal. To add a T3 member use AddMember function directly.", func(cur realm) {
70 dao.MustCreateProposal(cross, NewAddMemberRequest(cur, nm1, memberstore.T3, portfolio))
71 })
72
73 urequire.AbortsWithMessage(t, "proposer is not a member", func(cur realm) {
74 dao.MustCreateProposal(cross, NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio))
75 })
76
77 testing.SetOriginCaller(m1)
78 testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl"))
79
80 proposalRequest := NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio)
81
82 testing.SetOriginCaller(m1)
83 testing.SetRealm(testing.NewUserRealm(m1))
84 pid := dao.MustCreateProposal(cross, proposalRequest)
85 urequire.Equal(t, int(pid), 0)
86
87 // m1 votes yes because that member is interested on it
88 dao.MustVoteOnProposal(cross, dao.VoteRequest{
89 Option: dao.YesVote,
90 ProposalID: dao.ProposalID(0),
91 })
92
93 testing.SetOriginCaller(m11)
94
95 dao.MustVoteOnProposal(cross, dao.VoteRequest{
96 Option: dao.NoVote,
97 ProposalID: dao.ProposalID(0),
98 })
99
100 testing.SetOriginCaller(m2)
101
102 dao.MustVoteOnProposal(cross, dao.VoteRequest{
103 Option: dao.NoVote,
104 ProposalID: dao.ProposalID(0),
105 })
106
107 testing.SetOriginCaller(m3)
108
109 dao.MustVoteOnProposal(cross, dao.VoteRequest{
110 Option: dao.NoVote,
111 ProposalID: dao.ProposalID(0),
112 })
113
114 testing.SetOriginCaller(m4)
115
116 urequire.AbortsWithMessage(t, "member on specified tier is not allowed to vote on this proposal", func() {
117 dao.MustVoteOnProposal(cross, dao.VoteRequest{
118 Option: dao.NoVote,
119 ProposalID: dao.ProposalID(0),
120 })
121 })
122
123 testing.SetOriginCaller(m111)
124
125 // Same effect as:
126 // dao.MustVoteOnProposal(dao.VoteRequest{
127 // Option: dao.NoVote,
128 // ProposalID: dao.ProposalID(0),
129 // })
130 dao.MustVoteOnProposalSimple(cross, 0, "NO")
131
132 urequire.Equal(t, true, strings.Contains(dao.Render(""), "Prop #0 - New T2 Member Proposal"))
133 // urequire.Equal(t, true, strings.Contains(dao.Render(""), "Author: "+m1.String()))
134
135 urequire.AbortsWithMessage(t, "proposal didn't reach supermajority yet: 66.66", func() {
136 dao.ExecuteProposal(cross, dao.ProposalID(0))
137 })
138
139 testing.SetOriginCaller(m1111)
140 dao.MustVoteOnProposal(cross, dao.VoteRequest{
141 Option: dao.NoVote,
142 ProposalID: dao.ProposalID(0),
143 })
144
145 accepted := dao.ExecuteProposal(cross, dao.ProposalID(0))
146 urequire.Equal(t, false, accepted)
147
148 urequire.Equal(t, true, contains(dao.Render("0"), "**PROPOSAL HAS BEEN DENIED**"))
149 urequire.Equal(t, true, contains(dao.Render("0"), "NO PERCENT: 68.42105263157895%"))
150}
151
152func TestExecutorCreationRealm(cur realm, t *testing.T) {
153 loadMembers()
154
155 // Test that executor creation realm is captured correctly
156 testing.SetOriginCaller(m1)
157 testing.SetRealm(testing.NewCodeRealm("gno.land/r/template/contract"))
158
159 // Create executor in the template contract realm
160 executor := dao.NewSimpleExecutor(func(realm) error { return nil }, "Test executor from template")
161
162 proposalRequest := dao.NewProposalRequest(
163 "Test Proposal",
164 "This proposal tests executor creation realm tracking",
165 executor,
166 )
167
168 // Create proposal from user realm (user can call DAO directly)
169 testing.SetRealm(testing.NewUserRealm(m1))
170 pid := dao.MustCreateProposal(cross, proposalRequest)
171
172 // Get the proposal
173 prop := dao.MustGetProposal(cross, pid)
174
175 // Verify the author is m1
176 urequire.Equal(t, m1, prop.Author())
177
178 // Verify the executor creation realm is captured correctly
179 urequire.Equal(t, "gno.land/r/template/contract", prop.ExecutorCreationRealm())
180
181 // Check that it's displayed in the individual proposal render output
182 individualRendered := dao.Render(pid.String())
183 urequire.Equal(t, true, contains(individualRendered, "Executor created in: gno.land/r/template/contract"))
184 urequire.Equal(t, true, contains(individualRendered, "Test executor from template"))
185
186 // Also verify the main content is there
187 urequire.Equal(t, true, contains(individualRendered, "Test Proposal"))
188 urequire.Equal(t, true, contains(individualRendered, "This proposal tests executor creation realm tracking"))
189}
190
191func TestProposalPagination(cur realm, t *testing.T) {
192 loadMembers()
193 portfolio := "### This is my portfolio:\n\n- THINGS"
194
195 testing.SetOriginCaller(m1)
196 testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl"))
197
198 nm1 := testutils.TestAddress("nm1")
199
200 var pid dao.ProposalID
201
202 proposalRequest := NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio)
203
204 testing.SetOriginCaller(m1)
205 testing.SetRealm(testing.NewUserRealm(m1))
206 pid = dao.MustCreateProposal(cross, proposalRequest)
207
208 // TODO: tests keep the same vm state: https://github.com/gnolang/gno/issues/1982
209 urequire.Equal(t, 2, int(pid))
210
211 testing.SetRealm(testing.NewUserRealm(m1))
212 pid = dao.MustCreateProposal(cross, proposalRequest)
213 urequire.Equal(t, 3, int(pid))
214
215 testing.SetRealm(testing.NewUserRealm(m1))
216 pid = dao.MustCreateProposal(cross, proposalRequest)
217 urequire.Equal(t, 4, int(pid))
218
219 testing.SetRealm(testing.NewUserRealm(m1))
220 pid = dao.MustCreateProposal(cross, proposalRequest)
221 urequire.Equal(t, 5, int(pid))
222
223 testing.SetRealm(testing.NewUserRealm(m1))
224 pid = dao.MustCreateProposal(cross, proposalRequest)
225 urequire.Equal(t, 6, int(pid))
226
227 testing.SetRealm(testing.NewUserRealm(m1))
228 pid = dao.MustCreateProposal(cross, proposalRequest)
229 urequire.Equal(t, 7, int(pid))
230
231 fmt.Println(dao.Render(""))
232 urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #7 - New T2 Member Proposal](/r/gov/dao:7)"))
233 urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #6 - New T2 Member Proposal](/r/gov/dao:6)"))
234 urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #5 - New T2 Member Proposal](/r/gov/dao:5)"))
235 urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #4 - New T2 Member Proposal](/r/gov/dao:4)"))
236 urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #3 - New T2 Member Proposal](/r/gov/dao:3)"))
237
238 urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #2 - New T2 Member Proposal](/r/gov/dao:2)"))
239 urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #1 - Test Proposal](/r/gov/dao:1)"))
240 urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #0 - New T2 Member Proposal](/r/gov/dao:0)"))
241}
242
243func TestUpgradeDaoImplementation(t *testing.T) {
244 loadMembers()
245
246 testing.SetOriginCaller(noMember)
247 testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl"))
248
249 urequire.PanicsWithMessage(t, "proposer is not a member", func() {
250 NewUpgradeDaoImplRequest(govDAO, "gno.land/r/gov/dao/v4/impl", "Something happened and we have to fix it.")
251 })
252
253 testing.SetOriginCaller(m1)
254 testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl"))
255
256 preq := NewUpgradeDaoImplRequest(govDAO, "gno.land/r/gov/dao/v4/impl", "Something happened and we have to fix it.")
257
258 testing.SetOriginCaller(m1)
259 testing.SetRealm(testing.NewUserRealm(m1))
260 pid := dao.MustCreateProposal(cross, preq)
261 urequire.Equal(t, int(pid), 8)
262
263 // m1 votes yes because that member is interested on it
264 dao.MustVoteOnProposal(cross, dao.VoteRequest{
265 Option: dao.YesVote,
266 ProposalID: dao.ProposalID(pid),
267 })
268
269 testing.SetOriginCaller(m11)
270
271 dao.MustVoteOnProposal(cross, dao.VoteRequest{
272 Option: dao.YesVote,
273 ProposalID: dao.ProposalID(pid),
274 })
275
276 testing.SetOriginCaller(m2)
277
278 dao.MustVoteOnProposal(cross, dao.VoteRequest{
279 Option: dao.YesVote,
280 ProposalID: dao.ProposalID(pid),
281 })
282
283 testing.SetOriginCaller(m3)
284
285 dao.MustVoteOnProposal(cross, dao.VoteRequest{
286 Option: dao.YesVote,
287 ProposalID: dao.ProposalID(pid),
288 })
289
290 testing.SetOriginCaller(m111)
291
292 // Same effect as:
293 // dao.MustVoteOnProposal(dao.VoteRequest{
294 // Option: dao.YesVote,
295 // ProposalID: dao.ProposalID(pid),
296 // })
297 dao.MustVoteOnProposalSimple(cross, int64(pid), "YES")
298
299 urequire.Equal(t, true, contains(dao.Render("8"), "**Proposal is open for votes**"))
300 urequire.Equal(t, true, contains(dao.Render("8"), "68.42105263157895%"))
301 urequire.Equal(t, true, contains(dao.Render("8"), "0%"))
302
303 accepted := dao.ExecuteProposal(cross, dao.ProposalID(pid))
304 urequire.Equal(t, true, accepted)
305 urequire.Equal(t, true, contains(dao.Render("8"), "**PROPOSAL HAS BEEN ACCEPTED**"))
306 urequire.Equal(t, true, contains(dao.Render("8"), "YES PERCENT: 68.42105263157895%"))
307}
308
309func contains(s, substr string) bool {
310 return strings.Index(s, substr) >= 0
311}