diff --git a/pkg/data/manager.go b/pkg/data/manager.go index 85af0c6..3c525e0 100644 --- a/pkg/data/manager.go +++ b/pkg/data/manager.go @@ -2,7 +2,7 @@ package data import ( "fmt" - "main/pkg/fetchers" + fetchersPkg "main/pkg/fetchers" "main/pkg/types" "sync" @@ -10,14 +10,22 @@ import ( ) type Manager struct { - Logger zerolog.Logger - Chains types.Chains + Logger zerolog.Logger + Chains types.Chains + Fetchers []fetchersPkg.Fetcher } func NewManager(logger *zerolog.Logger, chains types.Chains) *Manager { + fetchers := make([]fetchersPkg.Fetcher, len(chains)) + + for index, chain := range chains { + fetchers[index] = fetchersPkg.GetFetcher(chain, logger) + } + return &Manager{ - Logger: logger.With().Str("component", "data_manager").Logger(), - Chains: chains, + Logger: logger.With().Str("component", "data_manager").Logger(), + Chains: chains, + Fetchers: fetchers, } } @@ -28,11 +36,11 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { errors := make([]error, 0) tallies := make(map[string]types.ChainTallyInfos) - for _, chain := range m.Chains { - fetcher := fetchers.GetFetcher(chain, m.Logger) + for index, chain := range m.Chains { + fetcher := m.Fetchers[index] wg.Add(1) - go func(c *types.Chain, fetcher fetchers.Fetcher) { + go func(c *types.Chain, fetcher fetchersPkg.Fetcher) { defer wg.Done() talliesForChain, err := fetcher.GetTallies() @@ -40,9 +48,9 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { mutex.Lock() if err != nil { - m.Logger.Error().Err(err).Str("chain", c.Name).Msg("Error fetching staking pool") + m.Logger.Error().Err(err).Str("chain", c.Name).Msg("Error fetching tallies") errors = append(errors, err) - } else { + } else if len(talliesForChain.TallyInfos) > 0 { tallies[c.Name] = talliesForChain } mutex.Unlock() @@ -66,14 +74,14 @@ func (m *Manager) GetParams() (map[string]types.ChainWithVotingParams, error) { params := make(map[string]types.ChainWithVotingParams) errors := make([]error, 0) - for _, chain := range m.Chains { + for index := range m.Chains { + fetcher := m.Fetchers[index] + wg.Add(1) - go func(chain *types.Chain) { + go func(fetcher fetchersPkg.Fetcher) { defer wg.Done() - fetcher := fetchers.GetFetcher(chain, m.Logger) - chainParams, errs := fetcher.GetChainParams() mutex.Lock() defer mutex.Unlock() @@ -84,7 +92,7 @@ func (m *Manager) GetParams() (map[string]types.ChainWithVotingParams, error) { } params[chainParams.Chain.Name] = *chainParams - }(chain) + }(fetcher) } wg.Wait() diff --git a/pkg/data/manager_test.go b/pkg/data/manager_test.go new file mode 100644 index 0000000..fee29cc --- /dev/null +++ b/pkg/data/manager_test.go @@ -0,0 +1,98 @@ +package data + +import ( + fetchersPkg "main/pkg/fetchers" + "main/pkg/logger" + "main/pkg/types" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/stretchr/testify/assert" +) + +func TestDataManagerNew(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := NewManager(log, types.Chains{ + {Name: "chain"}, + }) + + assert.NotNil(t, dataManager) +} + +func TestDataManagerGetTallyWithError(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := &Manager{ + Logger: *log, + Chains: types.Chains{{Name: "chain"}}, + Fetchers: []fetchersPkg.Fetcher{&fetchersPkg.TestFetcher{WithTallyError: true}}, + } + + tallies, err := dataManager.GetTallies() + require.Error(t, err) + assert.Empty(t, tallies) +} + +func TestDataManagerGetTallyEmpty(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := &Manager{ + Logger: *log, + Chains: types.Chains{{Name: "chain"}}, + Fetchers: []fetchersPkg.Fetcher{&fetchersPkg.TestFetcher{}}, + } + + tallies, err := dataManager.GetTallies() + require.NoError(t, err) + assert.Empty(t, tallies) +} + +func TestDataManagerGetTallyOk(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := &Manager{ + Logger: *log, + Chains: types.Chains{{Name: "chain"}}, + Fetchers: []fetchersPkg.Fetcher{&fetchersPkg.TestFetcher{WithTallyNotEmpty: true}}, + } + + tallies, err := dataManager.GetTallies() + require.NoError(t, err) + assert.NotEmpty(t, tallies) +} + +func TestDataManagerGetParamsWithError(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := &Manager{ + Logger: *log, + Chains: types.Chains{{Name: "chain"}}, + Fetchers: []fetchersPkg.Fetcher{&fetchersPkg.TestFetcher{WithParamsError: true}}, + } + + params, err := dataManager.GetParams() + require.Error(t, err) + assert.Empty(t, params) +} + +func TestDataManagerGetParamsOk(t *testing.T) { + t.Parallel() + + log := logger.GetNopLogger() + dataManager := &Manager{ + Logger: *log, + Chains: types.Chains{{Name: "chain"}}, + Fetchers: []fetchersPkg.Fetcher{&fetchersPkg.TestFetcher{}}, + } + + params, err := dataManager.GetParams() + require.NoError(t, err) + assert.NotEmpty(t, params) +} diff --git a/pkg/fetchers/cosmos/fetcher.go b/pkg/fetchers/cosmos/fetcher.go index 5e67e7c..56fb850 100644 --- a/pkg/fetchers/cosmos/fetcher.go +++ b/pkg/fetchers/cosmos/fetcher.go @@ -17,7 +17,7 @@ type RPC struct { Logger zerolog.Logger } -func NewRPC(chainConfig *types.Chain, logger zerolog.Logger) *RPC { +func NewRPC(chainConfig *types.Chain, logger *zerolog.Logger) *RPC { return &RPC{ ChainConfig: chainConfig, ProposalsType: chainConfig.ProposalsType, diff --git a/pkg/fetchers/cosmos/tally.go b/pkg/fetchers/cosmos/tally.go index b31ee12..c6812ca 100644 --- a/pkg/fetchers/cosmos/tally.go +++ b/pkg/fetchers/cosmos/tally.go @@ -5,6 +5,7 @@ import ( "fmt" "main/pkg/fetchers/cosmos/responses" "main/pkg/types" + "main/pkg/utils" "sync" "cosmossdk.io/math" @@ -61,7 +62,10 @@ func (rpc *RPC) GetTallies() (types.ChainTallyInfos, error) { go func() { defer wg.Done() - chainProposals, _, err := rpc.GetAllProposals(0) + chainProposalsAll, _, err := rpc.GetAllProposals(0) + chainProposals := utils.Filter(chainProposalsAll, func(p types.Proposal) bool { + return p.IsInVoting() + }) mutex.Lock() diff --git a/pkg/fetchers/fetcher.go b/pkg/fetchers/fetcher.go index 4a7dddf..4e1b7c1 100644 --- a/pkg/fetchers/fetcher.go +++ b/pkg/fetchers/fetcher.go @@ -16,7 +16,7 @@ type Fetcher interface { GetChainParams() (*types.ChainWithVotingParams, []error) } -func GetFetcher(chainConfig *types.Chain, logger zerolog.Logger) Fetcher { +func GetFetcher(chainConfig *types.Chain, logger *zerolog.Logger) Fetcher { if chainConfig.Type == "neutron" { return neutron.NewFetcher(chainConfig, logger) } diff --git a/pkg/fetchers/neutron/fetcher.go b/pkg/fetchers/neutron/fetcher.go index b1f8253..c630622 100644 --- a/pkg/fetchers/neutron/fetcher.go +++ b/pkg/fetchers/neutron/fetcher.go @@ -17,7 +17,7 @@ type Fetcher struct { Client *http.Client } -func NewFetcher(chainConfig *types.Chain, logger zerolog.Logger) *Fetcher { +func NewFetcher(chainConfig *types.Chain, logger *zerolog.Logger) *Fetcher { return &Fetcher{ ChainConfig: chainConfig, Logger: logger.With().Str("component", "neutron_fetcher").Logger(), diff --git a/pkg/fetchers/neutron/responses/proposals.go b/pkg/fetchers/neutron/responses/proposals.go index 7c91b92..73c8ee2 100644 --- a/pkg/fetchers/neutron/responses/proposals.go +++ b/pkg/fetchers/neutron/responses/proposals.go @@ -2,7 +2,6 @@ package responses import ( "main/pkg/types" - "main/pkg/utils" "strconv" "time" @@ -66,18 +65,18 @@ func (p ProposalWithID) ToProposal() (types.Proposal, error) { } func (p ProposalsResponse) ToTally() ([]types.TallyInfo, error) { - allProposals := utils.Filter(p.Data.Proposals, func(p ProposalWithID) bool { - return p.Proposal.Status == "open" - }) + tallyInfos := make([]types.TallyInfo, 0) - tallyInfos := make([]types.TallyInfo, len(allProposals)) - - for index, proposal := range allProposals { + for _, proposal := range p.Data.Proposals { proposalParsed, err := proposal.ToProposal() if err != nil { return []types.TallyInfo{}, err } + if !proposalParsed.IsInVoting() { + continue + } + yesVotes, err := math.LegacyNewDecFromStr(proposal.Proposal.Votes.Yes) if err != nil { return []types.TallyInfo{}, err @@ -98,7 +97,7 @@ func (p ProposalsResponse) ToTally() ([]types.TallyInfo, error) { return []types.TallyInfo{}, err } - tallyInfos[index] = types.TallyInfo{ + tallyInfos = append(tallyInfos, types.TallyInfo{ Proposal: proposalParsed, Tally: types.Tally{ {Option: "Yes", Voted: yesVotes}, @@ -106,7 +105,7 @@ func (p ProposalsResponse) ToTally() ([]types.TallyInfo, error) { {Option: "Abstain", Voted: abstainVotes}, }, TotalVotingPower: totalVotes, - } + }) } return tallyInfos, nil diff --git a/pkg/fetchers/neutron/tally.go b/pkg/fetchers/neutron/tally.go index 729d101..0cc55b3 100644 --- a/pkg/fetchers/neutron/tally.go +++ b/pkg/fetchers/neutron/tally.go @@ -6,7 +6,7 @@ import ( ) func (fetcher *Fetcher) GetTallies() (types.ChainTallyInfos, error) { - query := "{\"list_proposals\": {}}" + query := "{\"reverse_proposals\": {\"limit\": 1000}}" var proposals responses.ProposalsResponse if _, err := fetcher.GetSmartContractState(query, &proposals, 0); err != nil { diff --git a/pkg/fetchers/test_fetcher.go b/pkg/fetchers/test_fetcher.go index b2e0d89..59b4982 100644 --- a/pkg/fetchers/test_fetcher.go +++ b/pkg/fetchers/test_fetcher.go @@ -6,11 +6,13 @@ import ( ) type TestFetcher struct { - WithProposals bool WithPassedProposals bool WithProposalsError bool WithVote bool WithVoteError bool + WithTallyError bool + WithTallyNotEmpty bool + WithParamsError bool } func (f *TestFetcher) GetAllProposals( @@ -31,16 +33,12 @@ func (f *TestFetcher) GetAllProposals( }, 123, nil } - if f.WithProposals { - return []types.Proposal{ - { - ID: "1", - Status: types.ProposalStatusVoting, - }, - }, 123, nil - } - - return []types.Proposal{}, 123, nil + return []types.Proposal{ + { + ID: "1", + Status: types.ProposalStatusVoting, + }, + }, 123, nil } func (f *TestFetcher) GetVote( @@ -65,9 +63,35 @@ func (f *TestFetcher) GetVote( } func (f *TestFetcher) GetTallies() (types.ChainTallyInfos, error) { + if f.WithTallyError { + return types.ChainTallyInfos{}, &types.QueryError{ + QueryError: errors.New("error"), + } + } + + if f.WithTallyNotEmpty { + return types.ChainTallyInfos{ + Chain: &types.Chain{Name: "test"}, + TallyInfos: []types.TallyInfo{ + { + Proposal: types.Proposal{ID: "id"}, + Tally: types.Tally{}, + }, + }, + }, nil + } + return types.ChainTallyInfos{}, nil } func (f *TestFetcher) GetChainParams() (*types.ChainWithVotingParams, []error) { - return nil, []error{} + if f.WithParamsError { + return &types.ChainWithVotingParams{}, []error{ + errors.New("test"), + } + } + + return &types.ChainWithVotingParams{ + Chain: &types.Chain{Name: "test"}, + }, []error{} } diff --git a/pkg/http/client.go b/pkg/http/client.go index b1a426b..78cbdab 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -14,7 +14,7 @@ type Client struct { Logger zerolog.Logger } -func NewClient(chainName string, hosts []string, logger zerolog.Logger) *Client { +func NewClient(chainName string, hosts []string, logger *zerolog.Logger) *Client { return &Client{ Hosts: hosts, Logger: logger.With(). diff --git a/pkg/state/generator.go b/pkg/state/generator.go index f4b8183..0abcbc4 100644 --- a/pkg/state/generator.go +++ b/pkg/state/generator.go @@ -19,7 +19,7 @@ func NewStateGenerator(logger *zerolog.Logger, chains types.Chains) *Generator { fetchers := make(map[string]fetchersPkg.Fetcher, len(chains)) for _, chain := range chains { - fetchers[chain.Name] = fetchersPkg.GetFetcher(chain, *logger) + fetchers[chain.Name] = fetchersPkg.GetFetcher(chain, logger) } return &Generator{ diff --git a/pkg/state/generator_test.go b/pkg/state/generator_test.go index cbc76cb..da90964 100644 --- a/pkg/state/generator_test.go +++ b/pkg/state/generator_test.go @@ -111,7 +111,7 @@ func TestReportGeneratorProcessProposalWithoutError(t *testing.T) { Wallets: []*types.Wallet{{Address: "me"}}, } chains := types.Chains{chain} - fetcher := &fetchers.TestFetcher{WithProposals: true, WithVote: true} + fetcher := &fetchers.TestFetcher{WithVote: true} generator := Generator{ Logger: *log, @@ -157,7 +157,7 @@ func TestReportGeneratorProcessVoteWithError(t *testing.T) { Wallets: []*types.Wallet{wallet}, } chains := types.Chains{chain} - fetcher := &fetchers.TestFetcher{WithProposals: true, WithVoteError: true} + fetcher := &fetchers.TestFetcher{WithVoteError: true} proposal := types.Proposal{ID: "1", Status: types.ProposalStatusVoting} generator := Generator{ @@ -214,7 +214,7 @@ func TestReportGeneratorProcessVoteWithDisappearedVote(t *testing.T) { Wallets: []*types.Wallet{wallet}, } chains := types.Chains{chain} - fetcher := &fetchers.TestFetcher{WithProposals: true} + fetcher := &fetchers.TestFetcher{} proposal := types.Proposal{ID: "1", Status: types.ProposalStatusVoting} generator := Generator{ @@ -271,7 +271,7 @@ func TestReportGeneratorProcessVoteWithOkVote(t *testing.T) { Wallets: []*types.Wallet{wallet}, } chains := types.Chains{chain} - fetcher := &fetchers.TestFetcher{WithProposals: true, WithVote: true} + fetcher := &fetchers.TestFetcher{WithVote: true} proposal := types.Proposal{ID: "1", Status: types.ProposalStatusVoting} generator := Generator{