From 497f421009cde367986ee964d2365697cebb2210 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 12 Jul 2023 16:23:29 +0800 Subject: [PATCH] sweep: patch unit test for `feeRateForPreference` --- sweep/sweeper.go | 9 +++- sweep/sweeper_test.go | 118 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/sweep/sweeper.go b/sweep/sweeper.go index bf6930945..487ad6cee 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -47,6 +47,10 @@ var ( // request from a client whom did not specify a fee preference. ErrNoFeePreference = errors.New("no fee preference specified") + // ErrFeePreferenceTooLow is returned when the fee preference gives a + // fee rate that's below the relay fee rate. + ErrFeePreferenceTooLow = errors.New("fee preference too low") + // ErrExclusiveGroupSpend is returned in case a different input of the // same exclusive group was spent. ErrExclusiveGroupSpend = errors.New("other member of exclusive group " + @@ -485,9 +489,10 @@ func (s *UtxoSweeper) feeRateForPreference( if err != nil { return 0, err } + if feeRate < s.relayFeeRate { - return 0, fmt.Errorf("fee preference resulted in invalid fee "+ - "rate %v, minimum is %v", feeRate, s.relayFeeRate) + return 0, fmt.Errorf("%w: got %v, minimum is %v", + ErrFeePreferenceTooLow, feeRate, s.relayFeeRate) } // If the estimated fee rate is above the maximum allowed fee rate, diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index 602a5cb8c..dcb7a5792 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -1,6 +1,7 @@ package sweep import ( + "errors" "os" "reflect" "runtime/debug" @@ -2239,3 +2240,120 @@ func TestSweeperShutdownHandling(t *testing.T) { ) require.Error(t, err) } + +// TestFeeRateForPreference checks `feeRateForPreference` works as expected. +func TestFeeRateForPreference(t *testing.T) { + t.Parallel() + + dummyErr := errors.New("dummy") + + // Create a test sweeper. + s := New(&UtxoSweeperConfig{}) + + // errFeeFunc is a mock over DetermineFeePerKw that always return the + // above dummy error. + errFeeFunc := func(_ chainfee.Estimator, _ FeePreference) ( + chainfee.SatPerKWeight, error) { + + return 0, dummyErr + } + + // Set the relay fee rate to be 1. + s.relayFeeRate = 1 + + // smallFeeFunc is a mock over DetermineFeePerKw that always return a + // fee rate that's below the relayFeeRate. + smallFeeFunc := func(_ chainfee.Estimator, _ FeePreference) ( + chainfee.SatPerKWeight, error) { + + return s.relayFeeRate - 1, nil + } + + // Set the max fee rate to be 1000 sat/vb. + s.cfg.MaxFeeRate = 1000 + + // largeFeeFunc is a mock over DetermineFeePerKw that always return a + // fee rate that's larger than the MaxFeeRate. + largeFeeFunc := func(_ chainfee.Estimator, _ FeePreference) ( + chainfee.SatPerKWeight, error) { + + return s.cfg.MaxFeeRate.FeePerKWeight() + 1, nil + } + + // validFeeRate is used to test the success case. + validFeeRate := (s.cfg.MaxFeeRate.FeePerKWeight() + s.relayFeeRate) / 2 + + // normalFeeFunc is a mock over DetermineFeePerKw that always return a + // fee rate that's within the range. + normalFeeFunc := func(_ chainfee.Estimator, _ FeePreference) ( + chainfee.SatPerKWeight, error) { + + return validFeeRate, nil + } + + testCases := []struct { + name string + feePref FeePreference + determineFeePerKw feeDeterminer + expectedFeeRate chainfee.SatPerKWeight + expectedErr error + }{ + { + // When the fee preference is empty, we should see an + // error. + name: "empty fee preference", + feePref: FeePreference{}, + expectedErr: ErrNoFeePreference, + }, + { + // When an error is returned from the fee determinor, + // we should return it. + name: "error from DetermineFeePerKw", + feePref: FeePreference{FeeRate: 1}, + determineFeePerKw: errFeeFunc, + expectedErr: dummyErr, + }, + { + // When DetermineFeePerKw gives a too small value, we + // should return an error. + name: "fee rate below relay fee rate", + feePref: FeePreference{FeeRate: 1}, + determineFeePerKw: smallFeeFunc, + expectedErr: ErrFeePreferenceTooLow, + }, + { + // When DetermineFeePerKw gives a too large value, we + // should cap it at the max fee rate. + name: "fee rate above max fee rate", + feePref: FeePreference{FeeRate: 1}, + determineFeePerKw: largeFeeFunc, + expectedFeeRate: s.cfg.MaxFeeRate.FeePerKWeight(), + }, + { + // When DetermineFeePerKw gives a sane fee rate, we + // should return it without any error. + name: "success", + feePref: FeePreference{FeeRate: 1}, + determineFeePerKw: normalFeeFunc, + expectedFeeRate: validFeeRate, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + // Attach the mocked method. + s.cfg.DetermineFeePerKw = tc.determineFeePerKw + + // Call the function under test. + feerate, err := s.feeRateForPreference(tc.feePref) + + // Assert the expected feerate. + require.Equal(t, tc.expectedFeeRate, feerate) + + // Assert the expected error. + require.ErrorIs(t, err, tc.expectedErr) + }) + } +}