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