github.com/MetalBlockchain/metalgo@v1.11.9/snow/networking/handler/health_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package handler
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/network/p2p"
    16  	"github.com/MetalBlockchain/metalgo/snow"
    17  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowball"
    18  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    19  	"github.com/MetalBlockchain/metalgo/snow/networking/tracker"
    20  	"github.com/MetalBlockchain/metalgo/snow/snowtest"
    21  	"github.com/MetalBlockchain/metalgo/snow/validators"
    22  	"github.com/MetalBlockchain/metalgo/subnets"
    23  	"github.com/MetalBlockchain/metalgo/utils/logging"
    24  	"github.com/MetalBlockchain/metalgo/utils/math/meter"
    25  	"github.com/MetalBlockchain/metalgo/utils/resource"
    26  	"github.com/MetalBlockchain/metalgo/utils/set"
    27  	"github.com/MetalBlockchain/metalgo/version"
    28  
    29  	p2ppb "github.com/MetalBlockchain/metalgo/proto/pb/p2p"
    30  	commontracker "github.com/MetalBlockchain/metalgo/snow/engine/common/tracker"
    31  )
    32  
    33  func TestHealthCheckSubnet(t *testing.T) {
    34  	tests := map[string]struct {
    35  		consensusParams snowball.Parameters
    36  	}{
    37  		"default consensus params": {
    38  			consensusParams: snowball.DefaultParameters,
    39  		},
    40  		"custom consensus params": {
    41  			func() snowball.Parameters {
    42  				params := snowball.DefaultParameters
    43  				params.K = params.AlphaConfidence
    44  				return params
    45  			}(),
    46  		},
    47  	}
    48  
    49  	for name, test := range tests {
    50  		t.Run(name, func(t *testing.T) {
    51  			require := require.New(t)
    52  
    53  			snowCtx := snowtest.Context(t, snowtest.CChainID)
    54  			ctx := snowtest.ConsensusContext(snowCtx)
    55  
    56  			vdrs := validators.NewManager()
    57  
    58  			resourceTracker, err := tracker.NewResourceTracker(
    59  				prometheus.NewRegistry(),
    60  				resource.NoUsage,
    61  				meter.ContinuousFactory{},
    62  				time.Second,
    63  			)
    64  			require.NoError(err)
    65  
    66  			peerTracker := commontracker.NewPeers()
    67  			vdrs.RegisterSetCallbackListener(ctx.SubnetID, peerTracker)
    68  
    69  			sb := subnets.New(
    70  				ctx.NodeID,
    71  				subnets.Config{
    72  					ConsensusParameters: test.consensusParams,
    73  				},
    74  			)
    75  
    76  			p2pTracker, err := p2p.NewPeerTracker(
    77  				logging.NoLog{},
    78  				"",
    79  				prometheus.NewRegistry(),
    80  				nil,
    81  				version.CurrentApp,
    82  			)
    83  			require.NoError(err)
    84  
    85  			handlerIntf, err := New(
    86  				ctx,
    87  				vdrs,
    88  				nil,
    89  				time.Second,
    90  				testThreadPoolSize,
    91  				resourceTracker,
    92  				validators.UnhandledSubnetConnector,
    93  				sb,
    94  				peerTracker,
    95  				p2pTracker,
    96  				prometheus.NewRegistry(),
    97  			)
    98  			require.NoError(err)
    99  
   100  			bootstrapper := &common.BootstrapperTest{
   101  				EngineTest: common.EngineTest{
   102  					T: t,
   103  				},
   104  			}
   105  			bootstrapper.Default(false)
   106  
   107  			engine := &common.EngineTest{T: t}
   108  			engine.Default(false)
   109  			engine.ContextF = func() *snow.ConsensusContext {
   110  				return ctx
   111  			}
   112  
   113  			handlerIntf.SetEngineManager(&EngineManager{
   114  				Snowman: &Engine{
   115  					Bootstrapper: bootstrapper,
   116  					Consensus:    engine,
   117  				},
   118  			})
   119  
   120  			ctx.State.Set(snow.EngineState{
   121  				Type:  p2ppb.EngineType_ENGINE_TYPE_SNOWMAN,
   122  				State: snow.NormalOp, // assumed bootstrap is done
   123  			})
   124  
   125  			bootstrapper.StartF = func(context.Context, uint32) error {
   126  				return nil
   127  			}
   128  
   129  			handlerIntf.Start(context.Background(), false)
   130  
   131  			testVdrCount := 4
   132  			vdrIDs := set.NewSet[ids.NodeID](testVdrCount)
   133  			for i := 0; i < testVdrCount; i++ {
   134  				vdrID := ids.GenerateTestNodeID()
   135  				vdrIDs.Add(vdrID)
   136  
   137  				require.NoError(vdrs.AddStaker(ctx.SubnetID, vdrID, nil, ids.Empty, 100))
   138  			}
   139  
   140  			for index, nodeID := range vdrIDs.List() {
   141  				require.NoError(peerTracker.Connected(context.Background(), nodeID, nil))
   142  
   143  				details, err := handlerIntf.HealthCheck(context.Background())
   144  				expectedPercentConnected := float64(index+1) / float64(testVdrCount)
   145  				conf := sb.Config()
   146  				minPercentConnected := conf.ConsensusParameters.MinPercentConnectedHealthy()
   147  				if expectedPercentConnected >= minPercentConnected {
   148  					require.NoError(err)
   149  					continue
   150  				}
   151  				require.ErrorIs(err, ErrNotConnectedEnoughStake)
   152  
   153  				detailsMap, ok := details.(map[string]interface{})
   154  				require.True(ok)
   155  				networkingMap, ok := detailsMap["networking"]
   156  				require.True(ok)
   157  				networkingDetails, ok := networkingMap.(map[string]float64)
   158  				require.True(ok)
   159  				percentConnected, ok := networkingDetails["percentConnected"]
   160  				require.True(ok)
   161  				require.Equal(expectedPercentConnected, percentConnected)
   162  			}
   163  		})
   164  	}
   165  }