package impl import ( "fmt" "strings" "testing" "gno.land/p/nt/testutils/v0" "gno.land/p/nt/urequire/v0" "gno.land/r/gov/dao" "gno.land/r/gov/dao/v3/memberstore" ) func init() { loadMembers() dao.UpdateImpl(cross, dao.UpdateRequest{ DAO: govDAO, AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, }) } var ( m1 = testutils.TestAddress("m1") m11 = testutils.TestAddress("m1.1") m111 = testutils.TestAddress("m1.1.1") m1111 = testutils.TestAddress("m1.1.1.1") m2 = testutils.TestAddress("m2") m3 = testutils.TestAddress("m3") m4 = testutils.TestAddress("m4") m5 = testutils.TestAddress("m5") m6 = testutils.TestAddress("m6") noMember = testutils.TestAddress("nm1") ) func loadMembers() { // This is needed because state is saved between unit tests, // and we want to avoid having real members used on tests mstore := memberstore.Get() mstore.DeleteAll() mstore.SetTier(memberstore.T1) mstore.SetTier(memberstore.T2) mstore.SetTier(memberstore.T3) mstore.SetMember(memberstore.T1, m1, memberByTier(memberstore.T1)) mstore.SetMember(memberstore.T1, m11, memberByTier(memberstore.T1)) mstore.SetMember(memberstore.T1, m111, memberByTier(memberstore.T1)) mstore.SetMember(memberstore.T1, m1111, memberByTier(memberstore.T1)) mstore.SetMember(memberstore.T2, m2, memberByTier(memberstore.T2)) mstore.SetMember(memberstore.T2, m3, memberByTier(memberstore.T2)) mstore.SetMember(memberstore.T3, m4, memberByTier(memberstore.T3)) mstore.SetMember(memberstore.T3, m5, memberByTier(memberstore.T3)) mstore.SetMember(memberstore.T3, m6, memberByTier(memberstore.T3)) } func TestCreateProposalAndVote(cur realm, t *testing.T) { loadMembers() portfolio := "# This is my portfolio:\n\n- THINGS" testing.SetOriginCaller(noMember) testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) nm1 := testutils.TestAddress("nm1") 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) { dao.MustCreateProposal(cross, NewAddMemberRequest(cur, nm1, memberstore.T3, portfolio)) }) urequire.AbortsWithMessage(t, "proposer is not a member", func(cur realm) { dao.MustCreateProposal(cross, NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio)) }) testing.SetOriginCaller(m1) testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) proposalRequest := NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio) testing.SetOriginCaller(m1) testing.SetRealm(testing.NewUserRealm(m1)) pid := dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, int(pid), 0) // m1 votes yes because that member is interested on it dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(0), }) testing.SetOriginCaller(m11) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.NoVote, ProposalID: dao.ProposalID(0), }) testing.SetOriginCaller(m2) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.NoVote, ProposalID: dao.ProposalID(0), }) testing.SetOriginCaller(m3) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.NoVote, ProposalID: dao.ProposalID(0), }) testing.SetOriginCaller(m4) urequire.AbortsWithMessage(t, "member on specified tier is not allowed to vote on this proposal", func() { dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.NoVote, ProposalID: dao.ProposalID(0), }) }) testing.SetOriginCaller(m111) // Same effect as: // dao.MustVoteOnProposal(dao.VoteRequest{ // Option: dao.NoVote, // ProposalID: dao.ProposalID(0), // }) dao.MustVoteOnProposalSimple(cross, 0, "NO") urequire.Equal(t, true, strings.Contains(dao.Render(""), "Prop #0 - New T2 Member Proposal")) // urequire.Equal(t, true, strings.Contains(dao.Render(""), "Author: "+m1.String())) urequire.AbortsWithMessage(t, "proposal didn't reach supermajority yet: 66.66", func() { dao.ExecuteProposal(cross, dao.ProposalID(0)) }) testing.SetOriginCaller(m1111) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.NoVote, ProposalID: dao.ProposalID(0), }) accepted := dao.ExecuteProposal(cross, dao.ProposalID(0)) urequire.Equal(t, false, accepted) urequire.Equal(t, true, contains(dao.Render("0"), "**PROPOSAL HAS BEEN DENIED**")) urequire.Equal(t, true, contains(dao.Render("0"), "NO PERCENT: 68.42105263157895%")) } func TestExecutorCreationRealm(cur realm, t *testing.T) { loadMembers() // Test that executor creation realm is captured correctly testing.SetOriginCaller(m1) testing.SetRealm(testing.NewCodeRealm("gno.land/r/template/contract")) // Create executor in the template contract realm executor := dao.NewSimpleExecutor(func(realm) error { return nil }, "Test executor from template") proposalRequest := dao.NewProposalRequest( "Test Proposal", "This proposal tests executor creation realm tracking", executor, ) // Create proposal from user realm (user can call DAO directly) testing.SetRealm(testing.NewUserRealm(m1)) pid := dao.MustCreateProposal(cross, proposalRequest) // Get the proposal prop := dao.MustGetProposal(cross, pid) // Verify the author is m1 urequire.Equal(t, m1, prop.Author()) // Verify the executor creation realm is captured correctly urequire.Equal(t, "gno.land/r/template/contract", prop.ExecutorCreationRealm()) // Check that it's displayed in the individual proposal render output individualRendered := dao.Render(pid.String()) urequire.Equal(t, true, contains(individualRendered, "Executor created in: gno.land/r/template/contract")) urequire.Equal(t, true, contains(individualRendered, "Test executor from template")) // Also verify the main content is there urequire.Equal(t, true, contains(individualRendered, "Test Proposal")) urequire.Equal(t, true, contains(individualRendered, "This proposal tests executor creation realm tracking")) } func TestProposalPagination(cur realm, t *testing.T) { loadMembers() portfolio := "### This is my portfolio:\n\n- THINGS" testing.SetOriginCaller(m1) testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) nm1 := testutils.TestAddress("nm1") var pid dao.ProposalID proposalRequest := NewAddMemberRequest(cur, nm1, memberstore.T2, portfolio) testing.SetOriginCaller(m1) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) // TODO: tests keep the same vm state: https://github.com/gnolang/gno/issues/1982 urequire.Equal(t, 2, int(pid)) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, 3, int(pid)) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, 4, int(pid)) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, 5, int(pid)) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, 6, int(pid)) testing.SetRealm(testing.NewUserRealm(m1)) pid = dao.MustCreateProposal(cross, proposalRequest) urequire.Equal(t, 7, int(pid)) fmt.Println(dao.Render("")) urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #7 - New T2 Member Proposal](/r/gov/dao:7)")) urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #6 - New T2 Member Proposal](/r/gov/dao:6)")) urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #5 - New T2 Member Proposal](/r/gov/dao:5)")) urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #4 - New T2 Member Proposal](/r/gov/dao:4)")) urequire.Equal(t, true, contains(dao.Render(""), "### [Prop #3 - New T2 Member Proposal](/r/gov/dao:3)")) urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #2 - New T2 Member Proposal](/r/gov/dao:2)")) urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #1 - Test Proposal](/r/gov/dao:1)")) urequire.Equal(t, true, contains(dao.Render("?page=2"), "### [Prop #0 - New T2 Member Proposal](/r/gov/dao:0)")) } func TestUpgradeDaoImplementation(t *testing.T) { loadMembers() testing.SetOriginCaller(noMember) testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) urequire.PanicsWithMessage(t, "proposer is not a member", func() { NewUpgradeDaoImplRequest(govDAO, "gno.land/r/gov/dao/v4/impl", "Something happened and we have to fix it.") }) testing.SetOriginCaller(m1) testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) preq := NewUpgradeDaoImplRequest(govDAO, "gno.land/r/gov/dao/v4/impl", "Something happened and we have to fix it.") testing.SetOriginCaller(m1) testing.SetRealm(testing.NewUserRealm(m1)) pid := dao.MustCreateProposal(cross, preq) urequire.Equal(t, int(pid), 8) // m1 votes yes because that member is interested on it dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(pid), }) testing.SetOriginCaller(m11) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(pid), }) testing.SetOriginCaller(m2) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(pid), }) testing.SetOriginCaller(m3) dao.MustVoteOnProposal(cross, dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(pid), }) testing.SetOriginCaller(m111) // Same effect as: // dao.MustVoteOnProposal(dao.VoteRequest{ // Option: dao.YesVote, // ProposalID: dao.ProposalID(pid), // }) dao.MustVoteOnProposalSimple(cross, int64(pid), "YES") urequire.Equal(t, true, contains(dao.Render("8"), "**Proposal is open for votes**")) urequire.Equal(t, true, contains(dao.Render("8"), "68.42105263157895%")) urequire.Equal(t, true, contains(dao.Render("8"), "0%")) accepted := dao.ExecuteProposal(cross, dao.ProposalID(pid)) urequire.Equal(t, true, accepted) urequire.Equal(t, true, contains(dao.Render("8"), "**PROPOSAL HAS BEEN ACCEPTED**")) urequire.Equal(t, true, contains(dao.Render("8"), "YES PERCENT: 68.42105263157895%")) } func contains(s, substr string) bool { return strings.Index(s, substr) >= 0 }