Skip to content

Commit

Permalink
chore_: update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
friofry committed Oct 6, 2024
1 parent a88901a commit 3b2948a
Show file tree
Hide file tree
Showing 16 changed files with 566 additions and 371 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package statusaggregator
package aggregator

import (
"github.com/status-im/status-go/healthmanager/rpcstatus"
"sync"
"time"

"github.com/status-im/status-go/healthmanager/rpcstatus"
)

// Aggregator manages and aggregates the statuses of multiple providers.
Expand Down Expand Up @@ -36,26 +37,26 @@ func (a *Aggregator) RegisterProvider(providerName string) {

// Update modifies the status of a specific provider.
// If the provider is not already registered, it adds the provider.
func (a *Aggregator) Update(status rpcstatus.ProviderStatus) {
func (a *Aggregator) Update(providerStatus rpcstatus.ProviderStatus) {
a.mu.Lock()
defer a.mu.Unlock()

// Update existing provider status or add a new provider.
if ps, exists := a.providerStatuses[status.Name]; exists {
ps.Status = status.Status
if status.Status == rpcstatus.StatusUp {
ps.LastSuccessAt = status.LastSuccessAt
} else if status.Status == rpcstatus.StatusDown {
ps.LastErrorAt = status.LastErrorAt
ps.LastError = status.LastError
if ps, exists := a.providerStatuses[providerStatus.Name]; exists {
ps.Status = providerStatus.Status
if providerStatus.Status == rpcstatus.StatusUp {
ps.LastSuccessAt = providerStatus.LastSuccessAt
} else if providerStatus.Status == rpcstatus.StatusDown {
ps.LastErrorAt = providerStatus.LastErrorAt
ps.LastError = providerStatus.LastError
}
} else {
a.providerStatuses[status.Name] = &rpcstatus.ProviderStatus{
Name: status.Name,
LastSuccessAt: status.LastSuccessAt,
LastErrorAt: status.LastErrorAt,
LastError: status.LastError,
Status: status.Status,
a.providerStatuses[providerStatus.Name] = &rpcstatus.ProviderStatus{
Name: providerStatus.Name,
LastSuccessAt: providerStatus.LastSuccessAt,
LastErrorAt: providerStatus.LastErrorAt,
LastError: providerStatus.LastError,
Status: providerStatus.Status,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package statusaggregator
package aggregator

import (
"github.com/status-im/status-go/healthmanager/rpcstatus"
"sync"
"testing"
"time"

"github.com/status-im/status-go/healthmanager/rpcstatus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
Expand Down
11 changes: 8 additions & 3 deletions healthmanager/blockchain_health_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package healthmanager

import (
"context"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/healthmanager/aggregator"
"github.com/status-im/status-go/healthmanager/rpcstatus"
"github.com/status-im/status-go/healthmanager/statusaggregator"
"sync"
)

Expand All @@ -16,7 +17,7 @@ type BlockchainFullStatus struct {
// BlockchainHealthManager manages the state of all providers and aggregates their statuses.
type BlockchainHealthManager struct {
mu sync.RWMutex
aggregator *statusaggregator.Aggregator
aggregator *aggregator.Aggregator
subscribers []chan struct{}

providers map[uint64]*ProvidersHealthManager
Expand All @@ -27,7 +28,7 @@ type BlockchainHealthManager struct {

// NewBlockchainHealthManager creates a new instance of BlockchainHealthManager.
func NewBlockchainHealthManager() *BlockchainHealthManager {
aggregator := statusaggregator.NewAggregator("blockchain")
aggregator := aggregator.NewAggregator("blockchain")
return &BlockchainHealthManager{
aggregator: aggregator,
providers: make(map[uint64]*ProvidersHealthManager),
Expand All @@ -39,6 +40,10 @@ func NewBlockchainHealthManager() *BlockchainHealthManager {
func (b *BlockchainHealthManager) RegisterProvidersHealthManager(phm *ProvidersHealthManager) {
b.mu.Lock()
defer b.mu.Unlock()
if b.started {
log.Warn("Cannot register a provider when the manager is already started")
return
}

// Store the providers
b.providers[phm.ChainID()] = phm
Expand Down
10 changes: 5 additions & 5 deletions healthmanager/blockchain_health_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *BlockchainHealthManagerSuite) TestStatusUpdateNotification() {
ch := s.manager.Subscribe()

// Update the provider status
phm.Update(s.ctx, []rpcstatus.RpcCallStatus{
phm.Update(s.ctx, []rpcstatus.RpcProviderCallStatus{
{Name: "providerName", Timestamp: time.Now(), Err: nil},
})

Expand All @@ -81,10 +81,10 @@ func (s *BlockchainHealthManagerSuite) TestGetFullStatus() {
ch := s.manager.Subscribe()

// Update the provider status
phm1.Update(s.ctx, []rpcstatus.RpcCallStatus{
phm1.Update(s.ctx, []rpcstatus.RpcProviderCallStatus{
{Name: "providerName1", Timestamp: time.Now(), Err: nil},
})
phm2.Update(s.ctx, []rpcstatus.RpcCallStatus{
phm2.Update(s.ctx, []rpcstatus.RpcProviderCallStatus{
{Name: "providerName2", Timestamp: time.Now(), Err: errors.New("connection error")},
})

Expand Down Expand Up @@ -138,7 +138,7 @@ func (s *BlockchainHealthManagerSuite) TestConcurrency() {
err = nil
}
name := fmt.Sprintf("provider-%d", j)
go phm.Update(ctx, []rpcstatus.RpcCallStatus{
go phm.Update(ctx, []rpcstatus.RpcProviderCallStatus{
{Name: name, Timestamp: time.Now(), Err: err},
})
}
Expand Down Expand Up @@ -180,7 +180,7 @@ func (s *BlockchainHealthManagerSuite) TestUnsubscribeOneOfMultipleSubscribers()
s.manager.Unsubscribe(subscriber1)

err := errors.New("connection error")
phm.Update(ctx, []rpcstatus.RpcCallStatus{
phm.Update(ctx, []rpcstatus.RpcProviderCallStatus{
{Name: "provider-1", Timestamp: time.Now(), Err: err},
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
package networkerrors
package provider_errors

import (
"context"
"crypto/tls"
"errors"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"net"
"net/http"
"strings"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/rpc/chain/rpclimiter"
)

// ProviderErrorType defines the type of non-RPC error for JSON serialization.
type ProviderErrorType string

const (
// Non-RPC Errors
ProviderErrorTypeNone ProviderErrorType = "none"
ProviderErrorTypeContextCanceled ProviderErrorType = "context_canceled"
ProviderErrorTypeContextDeadlineExceeded ProviderErrorType = "context_deadline"
ProviderErrorTypeConnection ProviderErrorType = "connection"
ProviderErrorTypeNotAuthorized ProviderErrorType = "not_authorized"
ProviderErrorTypeForbidden ProviderErrorType = "forbidden"
ProviderErrorTypeBadRequest ProviderErrorType = "bad_request"
ProviderErrorTypeContentTooLarge ProviderErrorType = "content_too_large"
ProviderErrorTypeInternalError ProviderErrorType = "internal"
ProviderErrorTypeServiceUnavailable ProviderErrorType = "service_unavailable"
ProviderErrorTypeRateLimit ProviderErrorType = "rate_limit"
ProviderErrorTypeOther ProviderErrorType = "other"
)

// IsConnectionError checks if the error is related to network issues.
func IsConnectionError(err error) bool {
if err == nil {
Expand Down Expand Up @@ -66,7 +84,7 @@ func IsConnectionError(err error) bool {
return true
}

// Common connection refused or timeout errors
// Common connection refused or timeout error messages
errMsg := strings.ToLower(err.Error())
if strings.Contains(errMsg, "i/o timeout") ||
strings.Contains(errMsg, "connection refused") ||
Expand All @@ -79,7 +97,7 @@ func IsConnectionError(err error) bool {
return false
}

func IsRPSLimitError(err error) bool {
func IsRateLimitError(err error) bool {
if err == nil {
return false
}
Expand All @@ -91,6 +109,7 @@ func IsRPSLimitError(err error) bool {
if errors.Is(err, rpclimiter.ErrRequestsOverLimit) {
return true
}

errMsg := strings.ToLower(err.Error())
if strings.Contains(errMsg, "backoff_seconds") ||
strings.Contains(errMsg, "has exceeded its throughput limit") ||
Expand Down Expand Up @@ -120,13 +139,6 @@ var propagateErrors = []error{
bind.ErrNoCode,
}

// Not found should not be cancelling the requests, as that's returned
// when we are hitting a non archival node for example, it should continue the
// chain as the next provider might have archival support.
func IsNotFoundError(err error) bool {
return strings.Contains(err.Error(), ethereum.NotFound.Error())
}

func IsHTTPError(err error) (bool, int) {
var httpErrPtr *rpc.HTTPError
if errors.As(err, &httpErrPtr) {
Expand Down Expand Up @@ -183,32 +195,53 @@ func IsServiceUnavailableError(err error) bool {
return false
}

func IsRPCError(err error) (rpc.Error, bool) {
var rpcErr rpc.Error
if errors.As(err, &rpcErr) {
return rpcErr, true
// determineProviderErrorType determines the ProviderErrorType based on the error.
func determineProviderErrorType(err error) ProviderErrorType {
if err == nil {
return ProviderErrorTypeNone
}
return nil, false
}

func IsMethodNotFoundError(err error) bool {
if rpcErr, ok := IsRPCError(err); ok {
return rpcErr.ErrorCode() == -32601
if errors.Is(err, context.Canceled) {
return ProviderErrorTypeContextCanceled
}
return false
if errors.Is(err, context.DeadlineExceeded) {
return ProviderErrorTypeContextDeadlineExceeded
}
if IsConnectionError(err) {
return ProviderErrorTypeConnection
}
if IsNotAuthorizedError(err) {
return ProviderErrorTypeNotAuthorized
}
if IsForbiddenError(err) {
return ProviderErrorTypeForbidden
}
if IsBadRequestError(err) {
return ProviderErrorTypeBadRequest
}
if IsContentTooLargeError(err) {
return ProviderErrorTypeContentTooLarge
}
if IsInternalServerError(err) {
return ProviderErrorTypeInternalError
}
if IsServiceUnavailableError(err) {
return ProviderErrorTypeServiceUnavailable
}
if IsRateLimitError(err) {
return ProviderErrorTypeRateLimit
}
// Add additional non-RPC checks as necessary
return ProviderErrorTypeOther
}

func IsVMError(err error) bool {
if rpcErr, ok := IsRPCError(err); ok {
return rpcErr.ErrorCode() == -32015 // Код ошибки VM execution error
}
if strings.Contains(err.Error(), core.ErrInsufficientFunds.Error()) {
// IsNonCriticalProviderError determines if the non-RPC error is not critical.
func IsNonCriticalProviderError(err error) bool {
errorType := determineProviderErrorType(err)

switch errorType {
case ProviderErrorTypeNone, ProviderErrorTypeContextCanceled, ProviderErrorTypeContentTooLarge, ProviderErrorTypeRateLimit:
return true
default:
return false
}
for _, vmError := range propagateErrors {
if strings.Contains(err.Error(), vmError.Error()) {
return true
}
}
return false
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package networkerrors
package provider_errors

import (
"context"
Expand All @@ -9,7 +9,7 @@ import (
"testing"
)

// TestIsConnectionError tests the isConnectionError function.
// TestIsConnectionError tests the IsConnectionError function.
func TestIsConnectionError(t *testing.T) {
tests := []struct {
name string
Expand All @@ -22,12 +22,12 @@ func TestIsConnectionError(t *testing.T) {
wantResult: false,
},
{
name: "net.Error timeout",
name: "net.DNSError with timeout",
err: &net.DNSError{IsTimeout: true},
wantResult: true,
},
{
name: "DNS error",
name: "DNS error without timeout",
err: &net.DNSError{},
wantResult: true,
},
Expand Down Expand Up @@ -104,53 +104,11 @@ func TestIsConnectionError(t *testing.T) {
}

for _, tt := range tests {
tt := tt // capture the variable
t.Run(tt.name, func(t *testing.T) {
got := IsConnectionError(tt.err)
if got != tt.wantResult {
t.Errorf("isConnectionError(%v) = %v; want %v", tt.err, got, tt.wantResult)
}
})
}
}

func TestIsRPSLimitError(t *testing.T) {
tests := []struct {
name string
err error
wantResult bool
}{
{
name: "Error contains 'backoff_seconds'",
err: errors.New("Error: backoff_seconds: 30"),
wantResult: true,
},
{
name: "Error contains 'has exceeded its throughput limit'",
err: errors.New("Your application has exceeded its throughput limit."),
wantResult: true,
},
{
name: "Error contains 'request rate exceeded'",
err: errors.New("Request rate exceeded. Please try again later."),
wantResult: true,
},
{
name: "Error does not contain any matching phrases",
err: errors.New("Some other error occurred."),
wantResult: false,
},
{
name: "Error is nil",
err: nil,
wantResult: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := IsRPSLimitError(tt.err)
if got != tt.wantResult {
t.Errorf("IsRPSLimitError(%v) = %v; want %v", tt.err, got, tt.wantResult)
t.Errorf("IsConnectionError(%v) = %v; want %v", tt.err, got, tt.wantResult)
}
})
}
Expand Down
Loading

0 comments on commit 3b2948a

Please sign in to comment.