diff --git a/examples/gno.land/p/moul/udao/basicdao/basicdao.gno b/examples/gno.land/p/moul/udao/basicdao/basicdao.gno new file mode 100644 index 00000000000..991631ce7fb --- /dev/null +++ b/examples/gno.land/p/moul/udao/basicdao/basicdao.gno @@ -0,0 +1,142 @@ +package basicdao + +import ( + "time" + + "gno.land/p/demo/avl/list" + "gno.land/p/moul/udao" + "gno.land/p/moul/udao/wrap" +) + +// BasicProposal implements the Proposal interface +type BasicProposal struct { + id uint64 + definition udao.PropDefinition + state udao.PropState + yea float64 + nay float64 + abstain float64 + pending float64 +} + +func (p BasicProposal) ID() uint64 { return p.id } +func (p BasicProposal) Definition() udao.PropDefinition { return p.definition } +func (p BasicProposal) GetState() udao.PropState { return p.state } +func (p BasicProposal) GetYeaPercentage() float64 { return p.yea } +func (p BasicProposal) GetNayPercentage() float64 { return p.nay } +func (p BasicProposal) GetAbstainPercentage() float64 { return p.abstain } +func (p BasicProposal) GetPendingVoterPercentage() float64 { return p.pending } + +// BasicPropDefinition implements udao.PropDefinition +type BasicPropDefinition struct { + title string + body string + created time.Time + finished time.Time +} + +func (d BasicPropDefinition) Title() string { return d.title } +func (d BasicPropDefinition) Body() string { return d.body } +func (d BasicPropDefinition) Created() time.Time { return d.created } +func (d BasicPropDefinition) Finished() time.Time { return d.finished } +func (d BasicPropDefinition) CheckConstraints() (valid bool, reasons []string) { + panic("not implemented") +} + +// BasicDAO provides a minimal implementation of the DAO interface +type BasicDAO struct { + proposals map[uint64]udao.Proposal + nextID uint64 + active *list.List + archived *list.List +} + +// NewBasicDAO creates a new BasicDAO instance +func NewBasicDAO() *BasicDAO { + return &BasicDAO{ + proposals: make(map[uint64]udao.Proposal), + nextID: 1, + active: &list.List{}, + archived: &list.List{}, + } +} + +func (d *BasicDAO) Propose(def udao.PropDefinition) (udao.Proposal, error) { + prop := BasicProposal{ + id: d.nextID, + definition: def, + state: udao.Pending, + yea: 0, + nay: 0, + abstain: 0, + pending: 100, + } + d.proposals[d.nextID] = prop + d.active.Append(prop) + d.nextID++ + return prop, nil +} + +func (d *BasicDAO) GetProposal(proposalID uint64) (udao.Proposal, error) { + if prop, ok := d.proposals[proposalID]; ok { + return prop, nil + } + return nil, nil +} + +func (d *BasicDAO) Execute(proposalID uint64) error { + if prop, ok := d.proposals[proposalID]; ok { + if prop.GetState() == udao.Passed { + newProp := prop.(BasicProposal) + newProp.state = udao.Executed + d.proposals[proposalID] = newProp + + // Move from active to archived + for i := 0; i < d.active.Len(); i++ { + if p := d.active.Get(i).(udao.Proposal); p.ID() == proposalID { + d.active.Delete(i) + d.archived.Append(newProp) + break + } + } + return nil + } + } + return nil +} + +func (d *BasicDAO) ActiveProposals() udao.PropList { + return wrap.WrapAsPropList(d.active) +} + +func (d *BasicDAO) ArchivedProposals() udao.PropList { + return wrap.WrapAsPropList(d.archived) +} + +func (d *BasicDAO) Len() int { + return len(d.proposals) +} + +// Helper methods for managing proposal state +func (d *BasicDAO) UpdateProposalState(proposalID uint64, state udao.PropState) error { + if prop, ok := d.proposals[proposalID]; ok { + newProp := prop.(BasicProposal) + newProp.state = state + d.proposals[proposalID] = newProp + return nil + } + return nil +} + +func (d *BasicDAO) UpdateVotingPercentages(proposalID uint64, yea, nay, abstain, pending float64) error { + if prop, ok := d.proposals[proposalID]; ok { + newProp := prop.(BasicProposal) + newProp.yea = yea + newProp.nay = nay + newProp.abstain = abstain + newProp.pending = pending + d.proposals[proposalID] = newProp + return nil + } + return nil +} diff --git a/examples/gno.land/p/moul/udao/basicdao/basicdao_test.gno b/examples/gno.land/p/moul/udao/basicdao/basicdao_test.gno new file mode 100644 index 00000000000..76d3c98cc8e --- /dev/null +++ b/examples/gno.land/p/moul/udao/basicdao/basicdao_test.gno @@ -0,0 +1,276 @@ +package basicdao + +import ( + "testing" + "time" + + "gno.land/p/moul/udao" +) + +func TestBasicDAO(t *testing.T) { + dao := NewBasicDAO() + if dao.Len() != 0 { + t.Errorf("expected empty DAO, got len=%d", dao.Len()) + } + + // Test proposal creation + def := BasicPropDefinition{ + title: "Test Proposal", + body: "This is a test proposal", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + + prop, err := dao.Propose(def) + if err != nil { + t.Errorf("unexpected error creating proposal: %v", err) + } + if prop.ID() != 1 { + t.Errorf("expected proposal ID=1, got %d", prop.ID()) + } + if dao.Len() != 1 { + t.Errorf("expected DAO len=1, got %d", dao.Len()) + } + + // Test proposal retrieval + retrieved, err := dao.GetProposal(1) + if err != nil { + t.Errorf("unexpected error getting proposal: %v", err) + } + if retrieved == nil { + t.Fatal("expected to retrieve proposal, got nil") + } + if retrieved.ID() != prop.ID() { + t.Errorf("expected retrieved ID=%d, got %d", prop.ID(), retrieved.ID()) + } + + // Test active proposals list + active := dao.ActiveProposals() + if active.Len() != 1 { + t.Errorf("expected 1 active proposal, got %d", active.Len()) + } + if active.Get(0).ID() != prop.ID() { + t.Errorf("expected active proposal ID=%d, got %d", prop.ID(), active.Get(0).ID()) + } + + // Test proposal execution + dao.UpdateProposalState(1, udao.Passed) + err = dao.Execute(1) + if err != nil { + t.Errorf("unexpected error executing proposal: %v", err) + } + + + executed, _ := dao.GetProposal(1) + if executed.GetState() != udao.Executed { + t.Errorf("expected state=Executed, got %s", executed.GetState()) + } + + // Test archived proposals + archived := dao.ArchivedProposals() + if archived.Len() != 1 { + t.Errorf("expected 1 archived proposal, got %d", archived.Len()) + } + if archived.Get(0).ID() != prop.ID() { + t.Errorf("expected archived proposal ID=%d, got %d", prop.ID(), archived.Get(0).ID()) + } +} + +func TestPropListWrapper(t *testing.T) { + dao := NewBasicDAO() + + // Create multiple proposals + for i := 0; i < 3; i++ { + def := BasicPropDefinition{ + title: "Test Proposal", + body: "This is a test proposal", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + dao.Propose(def) + } + + active := dao.ActiveProposals() + + // Test Len() + if active.Len() != 3 { + t.Errorf("expected len=3, got %d", active.Len()) + } + + // Test Get() + prop := active.Get(0) + if prop.ID() != 1 { + t.Errorf("expected first proposal ID=1, got %d", prop.ID()) + } + + // Test Slice() + slice := active.Slice(0, 2) + if len(slice) != 2 { + t.Errorf("expected slice len=2, got %d", len(slice)) + } + if slice[0].ID() != 1 || slice[1].ID() != 2 { + t.Errorf("unexpected slice IDs: %d, %d", slice[0].ID(), slice[1].ID()) + } +} + +func TestProposalVoting(t *testing.T) { + dao := NewBasicDAO() + + // Create a proposal + def := BasicPropDefinition{ + title: "Voting Test", + body: "Testing voting percentages", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + prop, _ := dao.Propose(def) + + // Test initial voting percentages + if prop.GetYeaPercentage() != 0 { + t.Errorf("expected initial yea=0, got %f", prop.GetYeaPercentage()) + } + if prop.GetNayPercentage() != 0 { + t.Errorf("expected initial nay=0, got %f", prop.GetNayPercentage()) + } + if prop.GetPendingVoterPercentage() != 100 { + t.Errorf("expected initial pending=100, got %f", prop.GetPendingVoterPercentage()) + } + + // Update voting percentages + err := dao.UpdateVotingPercentages(prop.ID(), 60, 30, 10, 0) + if err != nil { + t.Errorf("unexpected error updating voting percentages: %v", err) + } + + // Verify updated percentages + updated, _ := dao.GetProposal(prop.ID()) + if updated.GetYeaPercentage() != 60 { + t.Errorf("expected yea=60, got %f", updated.GetYeaPercentage()) + } + if updated.GetNayPercentage() != 30 { + t.Errorf("expected nay=30, got %f", updated.GetNayPercentage()) + } + if updated.GetAbstainPercentage() != 10 { + t.Errorf("expected abstain=10, got %f", updated.GetAbstainPercentage()) + } + if updated.GetPendingVoterPercentage() != 0 { + t.Errorf("expected pending=0, got %f", updated.GetPendingVoterPercentage()) + } +} + +func TestProposalStateTransitions(t *testing.T) { + dao := NewBasicDAO() + + // Create a proposal + def := BasicPropDefinition{ + title: "State Test", + body: "Testing state transitions", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + prop, _ := dao.Propose(def) + + // Test initial state + if prop.GetState() != udao.Pending { + t.Errorf("expected initial state=Pending, got %s", prop.GetState()) + } + + // Test state transitions + states := []udao.PropState{udao.Active, udao.Passed, udao.Failed, udao.Executed, udao.Cancelled} + for _, state := range states { + err := dao.UpdateProposalState(prop.ID(), state) + if err != nil { + t.Errorf("unexpected error updating state to %s: %v", state, err) + } + + updated, _ := dao.GetProposal(prop.ID()) + if updated.GetState() != state { + t.Errorf("expected state=%s, got %s", state, updated.GetState()) + } + } +} + +func TestInvalidProposalOperations(t *testing.T) { + dao := NewBasicDAO() + + // Test getting non-existent proposal + prop, err := dao.GetProposal(999) + if err != nil { + t.Errorf("expected nil error for non-existent proposal, got %v", err) + } + if prop != nil { + t.Error("expected nil proposal for non-existent ID") + } + + // Test executing non-existent proposal + err = dao.Execute(999) + if err != nil { + t.Errorf("expected nil error for executing non-existent proposal, got %v", err) + } + + // Test executing non-passed proposal + def := BasicPropDefinition{ + title: "Invalid Execute", + body: "Testing invalid execution", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + prop, _ = dao.Propose(def) + err = dao.Execute(prop.ID()) + if err != nil { + t.Errorf("expected nil error for executing non-passed proposal, got %v", err) + } + + // Verify state didn't change + updated, _ := dao.GetProposal(prop.ID()) + if updated.GetState() != udao.Pending { + t.Errorf("expected state to remain Pending, got %s", updated.GetState()) + } +} + +func TestPropListSlicing(t *testing.T) { + dao := NewBasicDAO() + + // Create 5 proposals + for i := 0; i < 5; i++ { + def := BasicPropDefinition{ + title: "Test Proposal", + body: "This is a test proposal", + created: time.Now(), + finished: time.Now().Add(24 * time.Hour), + } + dao.Propose(def) + } + + active := dao.ActiveProposals() + + // Test various slice operations + testCases := []struct { + start int + end int + expected int + }{ + {0, 2, 2}, + {1, 4, 3}, + {0, 5, 5}, + {2, 3, 1}, + {4, 5, 1}, + } + + for _, tc := range testCases { + slice := active.Slice(tc.start, tc.end) + if len(slice) != tc.expected { + t.Errorf("expected slice[%d:%d] len=%d, got %d", + tc.start, tc.end, tc.expected, len(slice)) + } + + // Verify IDs are sequential + for i := range slice { + expectedID := uint64(tc.start + i + 1) + if slice[i].ID() != expectedID { + t.Errorf("expected ID=%d at index %d, got %d", + expectedID, i, slice[i].ID()) + } + } + } +} diff --git a/examples/gno.land/p/moul/udao/basicdao/gno.mod b/examples/gno.land/p/moul/udao/basicdao/gno.mod new file mode 100644 index 00000000000..bb5e9251777 --- /dev/null +++ b/examples/gno.land/p/moul/udao/basicdao/gno.mod @@ -0,0 +1 @@ +module gno.land/p/moul/udao/basicdao diff --git a/examples/gno.land/p/moul/udao/types.gno b/examples/gno.land/p/moul/udao/types.gno deleted file mode 100644 index 9543df9789e..00000000000 --- a/examples/gno.land/p/moul/udao/types.gno +++ /dev/null @@ -1,57 +0,0 @@ -package udao - -import ( - "time" -) - -// DAO defines the interface for proposal management -type DAO interface { - // Core proposal operations - Propose(def PropDefinition) (Proposal, error) - GetProposal(proposalID uint64) (Proposal, error) - Execute(proposalID uint64) error - - // List operations - ActiveProposals() PropList - ArchivedProposals() PropList - Len() int -} - -// PropDefinition defines the content of a proposal -type PropDefinition interface { - Title() string - Body() string - Created() time.Time - Finished() time.Time - CheckConstraints() (valid bool, reasons []string) -} - -// Proposal represents a complete proposal including its ID, definition and status -type Proposal interface { - ID() uint64 - Definition() PropDefinition - GetState() PropState - GetYeaPercentage() float64 - GetNayPercentage() float64 - GetAbstainPercentage() float64 - GetPendingVoterPercentage() float64 -} - -// PropState represents the state of a proposal -type PropState string - -const ( - Pending PropState = "pending" - Active PropState = "active" - Passed PropState = "passed" - Failed PropState = "failed" - Executed PropState = "executed" - Cancelled PropState = "cancelled" -) - -// PropList defines the read-only operations available on a proposal list -type PropList interface { - Len() int - Get(index int) Proposal - Slice(startIndex, endIndex int) []Proposal -} diff --git a/examples/gno.land/p/moul/udao/udao.gno b/examples/gno.land/p/moul/udao/udao.gno index c707d9a0d53..bc2ad441a85 100644 --- a/examples/gno.land/p/moul/udao/udao.gno +++ b/examples/gno.land/p/moul/udao/udao.gno @@ -1,140 +1,55 @@ package udao -import ( - "time" - - "gno.land/p/demo/avl/list" +import "time" + +// DAO defines the interface for proposal management +type DAO interface { + // Core proposal operations + Propose(def PropDefinition) (Proposal, error) + GetProposal(proposalID uint64) (Proposal, error) + Execute(proposalID uint64) error + + // List operations + ActiveProposals() PropList + ArchivedProposals() PropList + Len() int +} + +// PropDefinition defines the content of a proposal +type PropDefinition interface { + Title() string + Body() string + Created() time.Time + Finished() time.Time + CheckConstraints() (valid bool, reasons []string) +} + +// Proposal represents a complete proposal including its ID, definition and status +type Proposal interface { + ID() uint64 + Definition() PropDefinition + GetState() PropState + GetYeaPercentage() float64 + GetNayPercentage() float64 + GetAbstainPercentage() float64 + GetPendingVoterPercentage() float64 +} + +// PropState represents the state of a proposal +type PropState string + +const ( + Pending PropState = "pending" + Active PropState = "active" + Passed PropState = "passed" + Failed PropState = "failed" + Executed PropState = "executed" + Cancelled PropState = "cancelled" ) -// BasicProposal implements the Proposal interface -type BasicProposal struct { - id uint64 - definition PropDefinition - state PropState - yea float64 - nay float64 - abstain float64 - pending float64 -} - -func (p BasicProposal) ID() uint64 { return p.id } -func (p BasicProposal) Definition() PropDefinition { return p.definition } -func (p BasicProposal) GetState() PropState { return p.state } -func (p BasicProposal) GetYeaPercentage() float64 { return p.yea } -func (p BasicProposal) GetNayPercentage() float64 { return p.nay } -func (p BasicProposal) GetAbstainPercentage() float64 { return p.abstain } -func (p BasicProposal) GetPendingVoterPercentage() float64 { return p.pending } - -// BasicPropDefinition implements PropDefinition -type BasicPropDefinition struct { - title string - body string - created time.Time - finished time.Time -} - -func (d BasicPropDefinition) Title() string { return d.title } -func (d BasicPropDefinition) Body() string { return d.body } -func (d BasicPropDefinition) Created() time.Time { return d.created } -func (d BasicPropDefinition) Finished() time.Time { return d.finished } -func (d BasicPropDefinition) CheckConstraints() (valid bool, reasons []string) { - panic("not implemented") -} - -// BasicDAO provides a minimal implementation of the DAO interface -type BasicDAO struct { - proposals map[uint64]Proposal - nextID uint64 - active *list.List - archived *list.List -} - -// NewBasicDAO creates a new BasicDAO instance -func NewBasicDAO() *BasicDAO { - return &BasicDAO{ - proposals: make(map[uint64]Proposal), - nextID: 1, - active: &list.List{}, - archived: &list.List{}, - } -} - -func (d *BasicDAO) Propose(def PropDefinition) (Proposal, error) { - prop := BasicProposal{ - id: d.nextID, - definition: def, - state: Pending, - yea: 0, - nay: 0, - abstain: 0, - pending: 100, - } - d.proposals[d.nextID] = prop - d.active.Append(prop) - d.nextID++ - return prop, nil -} - -func (d *BasicDAO) GetProposal(proposalID uint64) (Proposal, error) { - if prop, ok := d.proposals[proposalID]; ok { - return prop, nil - } - return nil, nil -} - -func (d *BasicDAO) Execute(proposalID uint64) error { - if prop, ok := d.proposals[proposalID]; ok { - if prop.GetState() == Passed { - newProp := prop.(BasicProposal) - newProp.state = Executed - d.proposals[proposalID] = newProp - - // Move from active to archived - for i := 0; i < d.active.Len(); i++ { - if p := d.active.Get(i).(Proposal); p.ID() == proposalID { - d.active.Delete(i) - d.archived.Append(newProp) - break - } - } - return nil - } - } - return nil -} - -func (d *BasicDAO) ActiveProposals() PropList { - return WrapAsPropList(d.active) -} - -func (d *BasicDAO) ArchivedProposals() PropList { - return WrapAsPropList(d.archived) -} - -func (d *BasicDAO) Len() int { - return len(d.proposals) -} - -// Helper methods for managing proposal state -func (d *BasicDAO) UpdateProposalState(proposalID uint64, state PropState) error { - if prop, ok := d.proposals[proposalID]; ok { - newProp := prop.(BasicProposal) - newProp.state = state - d.proposals[proposalID] = newProp - return nil - } - return nil -} - -func (d *BasicDAO) UpdateVotingPercentages(proposalID uint64, yea, nay, abstain, pending float64) error { - if prop, ok := d.proposals[proposalID]; ok { - newProp := prop.(BasicProposal) - newProp.yea = yea - newProp.nay = nay - newProp.abstain = abstain - newProp.pending = pending - d.proposals[proposalID] = newProp - return nil - } - return nil +// PropList defines the read-only operations available on a proposal list +type PropList interface { + Len() int + Get(index int) Proposal + Slice(startIndex, endIndex int) []Proposal } diff --git a/examples/gno.land/p/moul/udao/udao_test.gno b/examples/gno.land/p/moul/udao/udao_test.gno index 8029bca033e..2c0644ecff6 100644 --- a/examples/gno.land/p/moul/udao/udao_test.gno +++ b/examples/gno.land/p/moul/udao/udao_test.gno @@ -1,273 +1 @@ package udao - -import ( - "testing" - "time" -) - -func TestBasicDAO(t *testing.T) { - dao := NewBasicDAO() - if dao.Len() != 0 { - t.Errorf("expected empty DAO, got len=%d", dao.Len()) - } - - // Test proposal creation - def := BasicPropDefinition{ - title: "Test Proposal", - body: "This is a test proposal", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - - prop, err := dao.Propose(def) - if err != nil { - t.Errorf("unexpected error creating proposal: %v", err) - } - if prop.ID() != 1 { - t.Errorf("expected proposal ID=1, got %d", prop.ID()) - } - if dao.Len() != 1 { - t.Errorf("expected DAO len=1, got %d", dao.Len()) - } - - // Test proposal retrieval - retrieved, err := dao.GetProposal(1) - if err != nil { - t.Errorf("unexpected error getting proposal: %v", err) - } - if retrieved == nil { - t.Fatal("expected to retrieve proposal, got nil") - } - if retrieved.ID() != prop.ID() { - t.Errorf("expected retrieved ID=%d, got %d", prop.ID(), retrieved.ID()) - } - - // Test active proposals list - active := dao.ActiveProposals() - if active.Len() != 1 { - t.Errorf("expected 1 active proposal, got %d", active.Len()) - } - if active.Get(0).ID() != prop.ID() { - t.Errorf("expected active proposal ID=%d, got %d", prop.ID(), active.Get(0).ID()) - } - - // Test proposal execution - dao.UpdateProposalState(1, Passed) - err = dao.Execute(1) - if err != nil { - t.Errorf("unexpected error executing proposal: %v", err) - } - - executed, _ := dao.GetProposal(1) - if executed.GetState() != Executed { - t.Errorf("expected state=Executed, got %s", executed.GetState()) - } - - // Test archived proposals - archived := dao.ArchivedProposals() - if archived.Len() != 1 { - t.Errorf("expected 1 archived proposal, got %d", archived.Len()) - } - if archived.Get(0).ID() != prop.ID() { - t.Errorf("expected archived proposal ID=%d, got %d", prop.ID(), archived.Get(0).ID()) - } -} - -func TestPropListWrapper(t *testing.T) { - dao := NewBasicDAO() - - // Create multiple proposals - for i := 0; i < 3; i++ { - def := BasicPropDefinition{ - title: "Test Proposal", - body: "This is a test proposal", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - dao.Propose(def) - } - - active := dao.ActiveProposals() - - // Test Len() - if active.Len() != 3 { - t.Errorf("expected len=3, got %d", active.Len()) - } - - // Test Get() - prop := active.Get(0) - if prop.ID() != 1 { - t.Errorf("expected first proposal ID=1, got %d", prop.ID()) - } - - // Test Slice() - slice := active.Slice(0, 2) - if len(slice) != 2 { - t.Errorf("expected slice len=2, got %d", len(slice)) - } - if slice[0].ID() != 1 || slice[1].ID() != 2 { - t.Errorf("unexpected slice IDs: %d, %d", slice[0].ID(), slice[1].ID()) - } -} - -func TestProposalVoting(t *testing.T) { - dao := NewBasicDAO() - - // Create a proposal - def := BasicPropDefinition{ - title: "Voting Test", - body: "Testing voting percentages", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - prop, _ := dao.Propose(def) - - // Test initial voting percentages - if prop.GetYeaPercentage() != 0 { - t.Errorf("expected initial yea=0, got %f", prop.GetYeaPercentage()) - } - if prop.GetNayPercentage() != 0 { - t.Errorf("expected initial nay=0, got %f", prop.GetNayPercentage()) - } - if prop.GetPendingVoterPercentage() != 100 { - t.Errorf("expected initial pending=100, got %f", prop.GetPendingVoterPercentage()) - } - - // Update voting percentages - err := dao.UpdateVotingPercentages(prop.ID(), 60, 30, 10, 0) - if err != nil { - t.Errorf("unexpected error updating voting percentages: %v", err) - } - - // Verify updated percentages - updated, _ := dao.GetProposal(prop.ID()) - if updated.GetYeaPercentage() != 60 { - t.Errorf("expected yea=60, got %f", updated.GetYeaPercentage()) - } - if updated.GetNayPercentage() != 30 { - t.Errorf("expected nay=30, got %f", updated.GetNayPercentage()) - } - if updated.GetAbstainPercentage() != 10 { - t.Errorf("expected abstain=10, got %f", updated.GetAbstainPercentage()) - } - if updated.GetPendingVoterPercentage() != 0 { - t.Errorf("expected pending=0, got %f", updated.GetPendingVoterPercentage()) - } -} - -func TestProposalStateTransitions(t *testing.T) { - dao := NewBasicDAO() - - // Create a proposal - def := BasicPropDefinition{ - title: "State Test", - body: "Testing state transitions", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - prop, _ := dao.Propose(def) - - // Test initial state - if prop.GetState() != Pending { - t.Errorf("expected initial state=Pending, got %s", prop.GetState()) - } - - // Test state transitions - states := []PropState{Active, Passed, Failed, Executed, Cancelled} - for _, state := range states { - err := dao.UpdateProposalState(prop.ID(), state) - if err != nil { - t.Errorf("unexpected error updating state to %s: %v", state, err) - } - - updated, _ := dao.GetProposal(prop.ID()) - if updated.GetState() != state { - t.Errorf("expected state=%s, got %s", state, updated.GetState()) - } - } -} - -func TestInvalidProposalOperations(t *testing.T) { - dao := NewBasicDAO() - - // Test getting non-existent proposal - prop, err := dao.GetProposal(999) - if err != nil { - t.Errorf("expected nil error for non-existent proposal, got %v", err) - } - if prop != nil { - t.Error("expected nil proposal for non-existent ID") - } - - // Test executing non-existent proposal - err = dao.Execute(999) - if err != nil { - t.Errorf("expected nil error for executing non-existent proposal, got %v", err) - } - - // Test executing non-passed proposal - def := BasicPropDefinition{ - title: "Invalid Execute", - body: "Testing invalid execution", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - prop, _ = dao.Propose(def) - err = dao.Execute(prop.ID()) - if err != nil { - t.Errorf("expected nil error for executing non-passed proposal, got %v", err) - } - - // Verify state didn't change - updated, _ := dao.GetProposal(prop.ID()) - if updated.GetState() != Pending { - t.Errorf("expected state to remain Pending, got %s", updated.GetState()) - } -} - -func TestPropListSlicing(t *testing.T) { - dao := NewBasicDAO() - - // Create 5 proposals - for i := 0; i < 5; i++ { - def := BasicPropDefinition{ - title: "Test Proposal", - body: "This is a test proposal", - created: time.Now(), - finished: time.Now().Add(24 * time.Hour), - } - dao.Propose(def) - } - - active := dao.ActiveProposals() - - // Test various slice operations - testCases := []struct { - start int - end int - expected int - }{ - {0, 2, 2}, - {1, 4, 3}, - {0, 5, 5}, - {2, 3, 1}, - {4, 5, 1}, - } - - for _, tc := range testCases { - slice := active.Slice(tc.start, tc.end) - if len(slice) != tc.expected { - t.Errorf("expected slice[%d:%d] len=%d, got %d", - tc.start, tc.end, tc.expected, len(slice)) - } - - // Verify IDs are sequential - for i := range slice { - expectedID := uint64(tc.start + i + 1) - if slice[i].ID() != expectedID { - t.Errorf("expected ID=%d at index %d, got %d", - expectedID, i, slice[i].ID()) - } - } - } -} diff --git a/examples/gno.land/p/moul/udao/wrappers.gno b/examples/gno.land/p/moul/udao/wrap/avl_list.gno similarity index 54% rename from examples/gno.land/p/moul/udao/wrappers.gno rename to examples/gno.land/p/moul/udao/wrap/avl_list.gno index ea06e439bd0..6ede8c13b34 100644 --- a/examples/gno.land/p/moul/udao/wrappers.gno +++ b/examples/gno.land/p/moul/udao/wrap/avl_list.gno @@ -1,7 +1,8 @@ -package udao +package wrap import ( "gno.land/p/demo/avl/list" + "gno.land/p/moul/udao" ) // PropListWrapper wraps an IReadOnlyList to implement PropList @@ -10,7 +11,7 @@ type PropListWrapper struct { } // WrapAsPropList converts an IReadOnlyList to a PropList -func WrapAsPropList(list list.IList) PropList { +func WrapAsPropList(list list.IList) udao.PropList { return &PropListWrapper{list: list} } @@ -18,15 +19,15 @@ func (pl *PropListWrapper) Len() int { return pl.list.Len() } -func (pl *PropListWrapper) Get(index int) Proposal { - return pl.list.Get(index).(Proposal) +func (pl *PropListWrapper) Get(index int) udao.Proposal { + return pl.list.Get(index).(udao.Proposal) } -func (pl *PropListWrapper) Slice(startIndex, endIndex int) []Proposal { +func (pl *PropListWrapper) Slice(startIndex, endIndex int) []udao.Proposal { slice := pl.list.Slice(startIndex, endIndex) - result := make([]Proposal, len(slice)) + result := make([]udao.Proposal, len(slice)) for i, v := range slice { - result[i] = v.(Proposal) + result[i] = v.(udao.Proposal) } return result } diff --git a/examples/gno.land/p/moul/udao/wrap/avl_list_test.gno b/examples/gno.land/p/moul/udao/wrap/avl_list_test.gno new file mode 100644 index 00000000000..a1080fa3223 --- /dev/null +++ b/examples/gno.land/p/moul/udao/wrap/avl_list_test.gno @@ -0,0 +1 @@ +package wrap diff --git a/examples/gno.land/p/moul/udao/wrap/gno.mod b/examples/gno.land/p/moul/udao/wrap/gno.mod new file mode 100644 index 00000000000..d2c0c5c6700 --- /dev/null +++ b/examples/gno.land/p/moul/udao/wrap/gno.mod @@ -0,0 +1 @@ +module gno.land/p/moul/udao/wrap