github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/unicast/manager_test.go (about)

     1  package unicast_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	libp2pnet "github.com/libp2p/go-libp2p/core/network"
     9  	"github.com/libp2p/go-libp2p/core/protocol"
    10  	"github.com/libp2p/go-libp2p/p2p/net/swarm"
    11  	"github.com/stretchr/testify/mock"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/onflow/flow-go/config"
    15  	"github.com/onflow/flow-go/module/metrics"
    16  	mockp2p "github.com/onflow/flow-go/network/p2p/mock"
    17  	p2ptest "github.com/onflow/flow-go/network/p2p/test"
    18  	"github.com/onflow/flow-go/network/p2p/unicast"
    19  	unicastcache "github.com/onflow/flow-go/network/p2p/unicast/cache"
    20  	"github.com/onflow/flow-go/network/p2p/unicast/stream"
    21  	"github.com/onflow/flow-go/utils/unittest"
    22  )
    23  
    24  func unicastManagerFixture(t *testing.T) (*unicast.Manager, *mockp2p.StreamFactory, unicast.ConfigCache) {
    25  	streamFactory := mockp2p.NewStreamFactory(t)
    26  	streamFactory.On("SetStreamHandler", mock.AnythingOfType("protocol.ID"), mock.AnythingOfType("network.StreamHandler")).Return().Once()
    27  
    28  	cfg, err := config.DefaultConfig()
    29  	require.NoError(t, err)
    30  
    31  	unicastConfigCache := unicastcache.NewUnicastConfigCache(cfg.NetworkConfig.Unicast.UnicastManager.ConfigCacheSize,
    32  		unittest.Logger(),
    33  		metrics.NewNoopCollector(),
    34  		func() unicast.Config {
    35  			return unicast.Config{
    36  				StreamCreationRetryAttemptBudget: cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes,
    37  			}
    38  		})
    39  
    40  	mgr, err := unicast.NewUnicastManager(&unicast.ManagerConfig{
    41  		Logger:        unittest.Logger(),
    42  		StreamFactory: streamFactory,
    43  		SporkId:       unittest.IdentifierFixture(),
    44  		Metrics:       metrics.NewNoopCollector(),
    45  		Parameters:    &cfg.NetworkConfig.Unicast.UnicastManager,
    46  		UnicastConfigCacheFactory: func(func() unicast.Config) unicast.ConfigCache {
    47  			return unicastConfigCache
    48  		},
    49  	})
    50  	require.NoError(t, err)
    51  	mgr.SetDefaultHandler(func(libp2pnet.Stream) {}) // no-op handler, we don't care about the handler for this test
    52  
    53  	return mgr, streamFactory, unicastConfigCache
    54  }
    55  
    56  // TestManagerConfigValidation tests the validation of the unicast manager config.
    57  // It tests that the config is valid when all the required fields are provided.
    58  func TestManagerConfigValidation(t *testing.T) {
    59  	cfg, err := config.DefaultConfig()
    60  	require.NoError(t, err)
    61  
    62  	validConfig := unicast.ManagerConfig{
    63  		Logger:        unittest.Logger(),
    64  		StreamFactory: mockp2p.NewStreamFactory(t),
    65  		SporkId:       unittest.IdentifierFixture(),
    66  		Parameters:    &cfg.NetworkConfig.Unicast.UnicastManager,
    67  		Metrics:       metrics.NewNoopCollector(),
    68  		UnicastConfigCacheFactory: func(func() unicast.Config) unicast.ConfigCache {
    69  			return unicastcache.NewUnicastConfigCache(cfg.NetworkConfig.Unicast.UnicastManager.ConfigCacheSize,
    70  				unittest.Logger(),
    71  				metrics.NewNoopCollector(),
    72  				func() unicast.Config {
    73  					return unicast.Config{
    74  						StreamCreationRetryAttemptBudget: cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes,
    75  					}
    76  				})
    77  		},
    78  	}
    79  
    80  	t.Run("Valid Config", func(t *testing.T) {
    81  		mgr, err := unicast.NewUnicastManager(&validConfig)
    82  		require.NoError(t, err)
    83  		require.NotNil(t, mgr)
    84  	})
    85  
    86  	t.Run("Missing Fields", func(t *testing.T) {
    87  		cfg := &unicast.ManagerConfig{}
    88  		mgr, err := unicast.NewUnicastManager(cfg)
    89  		require.Error(t, err)
    90  		require.Nil(t, mgr)
    91  	})
    92  
    93  	t.Run("Nil Parameters", func(t *testing.T) {
    94  		cfg := validConfig
    95  		cfg.Parameters = nil
    96  		mgr, err := unicast.NewUnicastManager(&cfg)
    97  		require.Error(t, err)
    98  		require.Nil(t, mgr)
    99  	})
   100  
   101  	t.Run("Invalid UnicastConfigCacheFactory", func(t *testing.T) {
   102  		cfg := validConfig
   103  		cfg.UnicastConfigCacheFactory = nil
   104  		mgr, err := unicast.NewUnicastManager(&cfg)
   105  		require.Error(t, err)
   106  		require.Nil(t, mgr)
   107  	})
   108  
   109  	t.Run("Missing StreamFactory", func(t *testing.T) {
   110  		cfg := validConfig
   111  		cfg.StreamFactory = nil
   112  		mgr, err := unicast.NewUnicastManager(&cfg)
   113  		require.Error(t, err)
   114  		require.Nil(t, mgr)
   115  	})
   116  
   117  	t.Run("Missing Metrics", func(t *testing.T) {
   118  		cfg := validConfig
   119  		cfg.Metrics = nil
   120  		mgr, err := unicast.NewUnicastManager(&cfg)
   121  		require.Error(t, err)
   122  		require.Nil(t, mgr)
   123  	})
   124  }
   125  
   126  // TestUnicastManager_SuccessfulStream tests that when CreateStream is successful on the first attempt for stream creation,
   127  // it updates the consecutive successful stream counter.
   128  func TestUnicastManager_SuccessfulStream(t *testing.T) {
   129  	peerID := unittest.PeerIdFixture(t)
   130  	mgr, streamFactory, configCache := unicastManagerFixture(t)
   131  
   132  	cfg, err := config.DefaultConfig()
   133  	require.NoError(t, err)
   134  
   135  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Once()
   136  
   137  	ctx, cancel := context.WithCancel(context.Background())
   138  	defer cancel()
   139  
   140  	s, err := mgr.CreateStream(ctx, peerID)
   141  	require.NoError(t, err)
   142  	require.NotNil(t, s)
   143  
   144  	// The unicast config must be updated with the backoff budget decremented.
   145  	unicastCfg, err := configCache.GetWithInit(peerID)
   146  	require.NoError(t, err)
   147  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget) // stream backoff budget must remain intact.
   148  	require.Equal(t, uint64(1), unicastCfg.ConsecutiveSuccessfulStream)                                                                        // consecutive successful stream must incremented.
   149  }
   150  
   151  // TestUnicastManager_StreamBackoff tests the backoff mechanism of the unicast manager for stream creation.
   152  // It tests the situation that CreateStream is called but the stream creation fails.
   153  // It tests that it tries to create a stream some number of times (unicastmodel.MaxStreamCreationAttemptTimes), before giving up.
   154  // It also checks the consecutive successful stream counter is reset when the stream creation fails.
   155  func TestUnicastManager_StreamBackoff(t *testing.T) {
   156  	peerID := unittest.PeerIdFixture(t)
   157  	mgr, streamFactory, configCache := unicastManagerFixture(t)
   158  
   159  	cfg, err := config.DefaultConfig()
   160  	require.NoError(t, err)
   161  
   162  	// mocks that it attempts to create a stream some number of times, before giving up.
   163  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   164  		Return(nil, fmt.Errorf("some error")).
   165  		Times(int(cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes + 1))
   166  
   167  	ctx, cancel := context.WithCancel(context.Background())
   168  	defer cancel()
   169  
   170  	s, err := mgr.CreateStream(ctx, peerID)
   171  	require.Error(t, err)
   172  	require.Nil(t, s)
   173  
   174  	// The unicast config must be updated with the backoff budget decremented.
   175  	unicastCfg, err := configCache.GetWithInit(peerID)
   176  	require.NoError(t, err)
   177  	// stream backoff budget must be decremented by 1 since all budget is used up.
   178  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget)
   179  	// consecutive successful stream must be reset to zero, since the stream creation failed.
   180  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   181  }
   182  
   183  // TestUnicastManager_StreamFactory_StreamBackoff tests the backoff mechanism of the unicast manager for stream creation.
   184  // It tests when there is a connection, but no stream, it tries to create a stream some number of times (unicastmodel.MaxStreamCreationAttemptTimes), before
   185  // giving up.
   186  func TestUnicastManager_StreamFactory_StreamBackoff(t *testing.T) {
   187  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   188  	peerID := unittest.PeerIdFixture(t)
   189  
   190  	cfg, err := config.DefaultConfig()
   191  	require.NoError(t, err)
   192  
   193  	// mocks that it attempts to create a stream some number of times, before giving up.
   194  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   195  		Return(nil, fmt.Errorf("some error")).
   196  		Times(int(cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes + 1))
   197  
   198  	ctx, cancel := context.WithCancel(context.Background())
   199  	defer cancel()
   200  	s, err := mgr.CreateStream(ctx, peerID)
   201  	require.Error(t, err)
   202  	require.Nil(t, s)
   203  
   204  	// The unicast config must be updated with the stream backoff budget decremented.
   205  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   206  	require.NoError(t, err)
   207  	// stream backoff budget must be decremented by 1.
   208  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget)
   209  	// consecutive successful stream must be zero as we have not created a successful stream yet.
   210  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   211  }
   212  
   213  // TestUnicastManager_Stream_ConsecutiveStreamCreation_Increment tests that when stream creation is successful,
   214  // it increments the consecutive successful stream counter in the unicast config.
   215  func TestUnicastManager_Stream_ConsecutiveStreamCreation_Increment(t *testing.T) {
   216  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   217  	peerID := unittest.PeerIdFixture(t)
   218  
   219  	cfg, err := config.DefaultConfig()
   220  	require.NoError(t, err)
   221  
   222  	// total times we successfully create a stream to the peer.
   223  	totalSuccessAttempts := 10
   224  
   225  	// mocks that it attempts to create a stream 10 times, and each time it succeeds.
   226  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Times(totalSuccessAttempts)
   227  
   228  	ctx, cancel := context.WithCancel(context.Background())
   229  	defer cancel()
   230  
   231  	for i := 0; i < totalSuccessAttempts; i++ {
   232  		s, err := mgr.CreateStream(ctx, peerID)
   233  		require.NoError(t, err)
   234  		require.NotNil(t, s)
   235  
   236  		// The unicast config must be updated with the stream backoff budget decremented.
   237  		unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   238  		require.NoError(t, err)
   239  		// stream backoff budget must be intact (all stream creation attempts are successful).
   240  		require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget)
   241  		// consecutive successful stream must be incremented.
   242  		require.Equal(t, uint64(i+1), unicastCfg.ConsecutiveSuccessfulStream)
   243  	}
   244  }
   245  
   246  // TestUnicastManager_Stream_ConsecutiveStreamCreation_Reset tests that when the stream creation fails, it resets
   247  // the consecutive successful stream counter in the unicast config.
   248  func TestUnicastManager_Stream_ConsecutiveStreamCreation_Reset(t *testing.T) {
   249  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   250  	peerID := unittest.PeerIdFixture(t)
   251  
   252  	// mocks that it attempts to create a stream once and fails.
   253  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   254  		Return(nil, fmt.Errorf("some error")).
   255  		Once()
   256  
   257  	adjustedUnicastConfig, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) {
   258  		// sets the consecutive successful stream to 5 meaning that the last 5 stream creation attempts were successful.
   259  		unicastConfig.ConsecutiveSuccessfulStream = 5
   260  		// sets the stream back budget to 0 meaning that the stream backoff budget is exhausted.
   261  		unicastConfig.StreamCreationRetryAttemptBudget = 0
   262  
   263  		return unicastConfig, nil
   264  	})
   265  	require.NoError(t, err)
   266  	require.Equal(t, uint64(5), adjustedUnicastConfig.ConsecutiveSuccessfulStream)
   267  
   268  	ctx, cancel := context.WithCancel(context.Background())
   269  	defer cancel()
   270  
   271  	s, err := mgr.CreateStream(ctx, peerID)
   272  	require.Error(t, err)
   273  	require.Nil(t, s)
   274  
   275  	// The unicast config must be updated with the stream backoff budget decremented.
   276  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   277  	require.NoError(t, err)
   278  
   279  	// stream backoff budget must be intact (we can't decrement it below 0).
   280  	require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget)
   281  	// consecutive successful stream must be reset to 0.
   282  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   283  }
   284  
   285  // TestUnicastManager_StreamFactory_ErrProtocolNotSupported tests that when there is a protocol not supported error, it does not retry creating a stream.
   286  func TestUnicastManager_StreamFactory_ErrProtocolNotSupported(t *testing.T) {
   287  	mgr, streamFactory, _ := unicastManagerFixture(t)
   288  	peerID := unittest.PeerIdFixture(t)
   289  
   290  	// mocks that upon creating a stream, it returns a protocol not supported error, the mock is set to once, meaning that it won't retry stream creation again.
   291  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   292  		Return(nil, stream.NewProtocolNotSupportedErr(peerID, protocol.ID("protocol-1"), fmt.Errorf("some error"))).
   293  		Once()
   294  
   295  	ctx, cancel := context.WithCancel(context.Background())
   296  	defer cancel()
   297  	s, err := mgr.CreateStream(ctx, peerID)
   298  	require.Error(t, err)
   299  	require.Nil(t, s)
   300  }
   301  
   302  // TestUnicastManager_StreamFactory_ErrNoAddresses tests that when stream creation returns a no addresses error,
   303  // it does not retry stream creation again and returns an error immediately.
   304  func TestUnicastManager_StreamFactory_ErrNoAddresses(t *testing.T) {
   305  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   306  
   307  	cfg, err := config.DefaultConfig()
   308  	require.NoError(t, err)
   309  
   310  	peerID := unittest.PeerIdFixture(t)
   311  
   312  	// mocks that stream creation returns a no addresses error, and the mock is set to once, meaning that it won't retry stream creation again.
   313  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   314  		Return(nil, fmt.Errorf("some error to ensure wrapping works fine: %w", swarm.ErrNoAddresses)).
   315  		Once()
   316  
   317  	ctx, cancel := context.WithCancel(context.Background())
   318  	defer cancel()
   319  	s, err := mgr.CreateStream(ctx, peerID)
   320  	require.Error(t, err)
   321  	require.Nil(t, s)
   322  
   323  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   324  	require.NoError(t, err)
   325  
   326  	// stream backoff budget must be reduced by 1 due to failed stream creation.
   327  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget)
   328  	// consecutive successful stream must be set to zero.
   329  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   330  }
   331  
   332  // TestUnicastManager_Stream_ErrSecurityProtocolNegotiationFailed tests that when there is a security protocol negotiation error, it does not retry stream creation.
   333  func TestUnicastManager_Stream_ErrSecurityProtocolNegotiationFailed(t *testing.T) {
   334  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   335  
   336  	cfg, err := config.DefaultConfig()
   337  	require.NoError(t, err)
   338  
   339  	peerID := unittest.PeerIdFixture(t)
   340  
   341  	// mocks that stream creation returns a security protocol negotiation error, and the mock is set to once, meaning that it won't retry stream creation.
   342  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   343  		Return(nil, stream.NewSecurityProtocolNegotiationErr(peerID, fmt.Errorf("some error"))).
   344  		Once()
   345  
   346  	ctx, cancel := context.WithCancel(context.Background())
   347  	defer cancel()
   348  	s, err := mgr.CreateStream(ctx, peerID)
   349  	require.Error(t, err)
   350  	require.Nil(t, s)
   351  
   352  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   353  	require.NoError(t, err)
   354  	// stream retry budget must be decremented by 1 (since we didn't have a successful stream creation, the budget is decremented).
   355  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget)
   356  	// consecutive successful stream must be set to zero.
   357  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   358  }
   359  
   360  // TestUnicastManager_StreamFactory_ErrGaterDisallowedConnection tests that when there is a connection-gater disallow listing error, it does not retry stream creation.
   361  func TestUnicastManager_StreamFactory_ErrGaterDisallowedConnection(t *testing.T) {
   362  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   363  	peerID := unittest.PeerIdFixture(t)
   364  
   365  	cfg, err := config.DefaultConfig()
   366  	require.NoError(t, err)
   367  
   368  	// mocks that stream creation to the peer returns a connection gater disallow-listing, and the mock is set to once, meaning that it won't retry stream creation.
   369  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   370  		Return(nil, stream.NewGaterDisallowedConnectionErr(fmt.Errorf("some error"))).
   371  		Once()
   372  
   373  	ctx, cancel := context.WithCancel(context.Background())
   374  	defer cancel()
   375  	s, err := mgr.CreateStream(ctx, peerID)
   376  	require.Error(t, err)
   377  	require.Nil(t, s)
   378  
   379  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   380  	require.NoError(t, err)
   381  	// stream backoff budget must be reduced by 1 due to failed stream creation.
   382  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget)
   383  	// consecutive successful stream must be set to zero.
   384  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)
   385  }
   386  
   387  // TestUnicastManager_Connection_BackoffBudgetDecremented tests that everytime the unicast manger gives up on creating a stream (after retrials),
   388  // it decrements the backoff budget for the remote peer.
   389  func TestUnicastManager_Stream_BackoffBudgetDecremented(t *testing.T) {
   390  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   391  	peerID := unittest.PeerIdFixture(t)
   392  
   393  	cfg, err := config.DefaultConfig()
   394  	require.NoError(t, err)
   395  
   396  	// totalAttempts is the total number of times that unicast manager calls NewStream on the stream factory to create stream to the peer.
   397  	// Note that it already assumes that the connection is established, so it does not try to connect to the peer.
   398  	// Let's consider x = unicastmodel.MaxStreamCreationRetryAttemptTimes + 1. Then the test tries x times CreateStream. With dynamic backoffs,
   399  	// the first CreateStream call will try to NewStream x times, the second CreateStream call will try to NewStream x-1 times,
   400  	// and so on. So the total number of Connect calls is x + (x-1) + (x-2) + ... + 1 = x(x+1)/2.
   401  	maxStreamRetryBudget := cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes
   402  	maxStreamAttempt := maxStreamRetryBudget + 1 // 1 attempt + retry times
   403  	totalAttempts := maxStreamAttempt * (maxStreamAttempt + 1) / 2
   404  
   405  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).
   406  		Return(nil, fmt.Errorf("some error")).
   407  		Times(int(totalAttempts))
   408  
   409  	ctx, cancel := context.WithCancel(context.Background())
   410  	defer cancel()
   411  	for i := 0; i < int(maxStreamRetryBudget); i++ {
   412  		s, err := mgr.CreateStream(ctx, peerID)
   413  		require.Error(t, err)
   414  		require.Nil(t, s)
   415  
   416  		unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   417  		require.NoError(t, err)
   418  
   419  		if i == int(maxStreamRetryBudget)-1 {
   420  			require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget)
   421  		} else {
   422  			require.Equal(t, maxStreamRetryBudget-uint64(i)-1, unicastCfg.StreamCreationRetryAttemptBudget)
   423  		}
   424  	}
   425  	// At this time the backoff budget for connection must be 0.
   426  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   427  	require.NoError(t, err)
   428  	require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget)
   429  
   430  	// After all the backoff budget is used up, it should stay at 0.
   431  	s, err := mgr.CreateStream(ctx, peerID)
   432  	require.Error(t, err)
   433  	require.Nil(t, s)
   434  
   435  	unicastCfg, err = unicastConfigCache.GetWithInit(peerID)
   436  	require.NoError(t, err)
   437  	require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget)
   438  }
   439  
   440  // TestUnicastManager_Stream_BackoffBudgetResetToDefault tests that when the stream retry attempt budget is zero, and the consecutive successful stream counter is above the reset threshold,
   441  // it resets the stream retry attempt budget to the default value and increments the consecutive successful stream counter.
   442  func TestUnicastManager_Stream_BackoffBudgetResetToDefault(t *testing.T) {
   443  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   444  	peerID := unittest.PeerIdFixture(t)
   445  
   446  	cfg, err := config.DefaultConfig()
   447  	require.NoError(t, err)
   448  
   449  	// mocks that it attempts to create a stream once and succeeds.
   450  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Once()
   451  
   452  	// update the unicast config of the peer to have a zero stream backoff budget but a consecutive successful stream counter above the reset threshold.
   453  	adjustedCfg, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) {
   454  		unicastConfig.StreamCreationRetryAttemptBudget = 0
   455  		unicastConfig.ConsecutiveSuccessfulStream = cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold + 1
   456  		return unicastConfig, nil
   457  	})
   458  	require.NoError(t, err)
   459  	require.Equal(t, uint64(0), adjustedCfg.StreamCreationRetryAttemptBudget)
   460  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold+1, adjustedCfg.ConsecutiveSuccessfulStream)
   461  
   462  	ctx, cancel := context.WithCancel(context.Background())
   463  	defer cancel()
   464  
   465  	s, err := mgr.CreateStream(ctx, peerID)
   466  	require.NoError(t, err)
   467  	require.NotNil(t, s)
   468  
   469  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   470  	require.NoError(t, err)
   471  	// stream backoff budget must reset to default.
   472  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget)
   473  	// consecutive successful stream must increment by 1 (it was threshold + 1 before).
   474  	require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold+1+1, unicastCfg.ConsecutiveSuccessfulStream)
   475  }
   476  
   477  // TestUnicastManager_Stream_NoBackoff_When_Budget_Is_Zero tests that when the stream backoff budget is zero and the consecutive successful stream counter is not above the
   478  // zero rest threshold, the unicast manager does not backoff if the stream creation attempt fails.
   479  func TestUnicastManager_Stream_NoBackoff_When_Budget_Is_Zero(t *testing.T) {
   480  	mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t)
   481  	peerID := unittest.PeerIdFixture(t)
   482  
   483  	// mocks that it attempts to create a stream once and fails, and does not retry.
   484  	streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(nil, fmt.Errorf("some error")).Once()
   485  
   486  	adjustedCfg, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) {
   487  		unicastConfig.ConsecutiveSuccessfulStream = 2      // set the consecutive successful stream to 2, which is below the reset threshold.
   488  		unicastConfig.StreamCreationRetryAttemptBudget = 0 // set the stream backoff budget to 0, meaning that the stream backoff budget is exhausted.
   489  		return unicastConfig, nil
   490  	})
   491  	require.NoError(t, err)
   492  	require.Equal(t, uint64(0), adjustedCfg.StreamCreationRetryAttemptBudget)
   493  	require.Equal(t, uint64(2), adjustedCfg.ConsecutiveSuccessfulStream)
   494  
   495  	ctx, cancel := context.WithCancel(context.Background())
   496  	defer cancel()
   497  
   498  	s, err := mgr.CreateStream(ctx, peerID)
   499  	require.Error(t, err)
   500  	require.Nil(t, s)
   501  
   502  	unicastCfg, err := unicastConfigCache.GetWithInit(peerID)
   503  	require.NoError(t, err)
   504  	require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) // stream backoff budget must remain zero.
   505  	require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream)      // consecutive successful stream must be set to zero.
   506  }