chainfee: fix unit test for WebAPIEstimator

This commit is contained in:
yyforyongyu 2023-08-23 06:43:16 +08:00
parent 13568fd5b0
commit d8924d26ce
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
2 changed files with 88 additions and 68 deletions

View file

@ -610,7 +610,7 @@ func (w *WebAPIEstimator) EstimateFeePerKW(numBlocks uint32) (
// returned. We will log the error and return the fall back fee rate // returned. We will log the error and return the fall back fee rate
// instead. // instead.
if err != nil { if err != nil {
log.Errorf("unable to query estimator: %v", err) log.Errorf("Unable to query estimator: %v", err)
} }
// If the result is too low, then we'll clamp it to our current fee // If the result is too low, then we'll clamp it to our current fee

View file

@ -155,82 +155,81 @@ func TestSparseConfFeeSource(t *testing.T) {
// as expected. // as expected.
func TestWebAPIFeeEstimator(t *testing.T) { func TestWebAPIFeeEstimator(t *testing.T) {
t.Parallel() t.Parallel()
feeFloor := uint32(FeePerKwFloor.FeePerKVByte())
testFeeRate := feeFloor * 100 var (
minTarget uint32 = 2
maxTarget uint32 = 6
// Fee rates are in sat/kb.
minFeeRate uint32 = 2000 // 500 sat/kw
maxFeeRate uint32 = 4000 // 1000 sat/kw
)
testCases := []struct { testCases := []struct {
name string name string
target uint32 target uint32
apiEst uint32 expectedFeeRate uint32
est uint32 expectedErr string
err string
}{ }{
{ {
name: "target_below_min", // When requested target is below minBlockTarget, an
target: 0, // error is returned.
apiEst: 0, name: "target_below_min",
est: 0, target: 0,
err: "too low, minimum", expectedFeeRate: 0,
expectedErr: "too low, minimum",
}, },
{ {
name: "target_w_too-low_fee", // When requested target is larger than the max cached
target: 100, // target, the fee rate of the max cached target is
apiEst: 42, // returned.
est: feeFloor, name: "target_w_too-low_fee",
err: "", target: maxTarget + 100,
expectedFeeRate: minFeeRate,
expectedErr: "",
}, },
{ {
name: "API-omitted_target", // When requested target is smaller than the min cahced
target: 2, // target, the fee rate of the min cached target is
apiEst: 0, // returned.
est: testFeeRate, name: "API-omitted_target",
err: "", target: minTarget - 1,
expectedFeeRate: maxFeeRate,
expectedErr: "",
}, },
{ {
name: "valid_target", // When the target is found, return it.
target: 20, name: "valid_target",
apiEst: testFeeRate, target: maxTarget,
est: testFeeRate, expectedFeeRate: minFeeRate,
err: "", expectedErr: "",
},
{
name: "valid_target_extrapolated_fee",
target: 25,
apiEst: 0,
est: testFeeRate,
err: "",
}, },
} }
// Construct mock fee source for the Estimator to pull fees from. // Construct mock fee source for the Estimator to pull fees from.
// //
// This will create a `feeByBlockTarget` map with the following values, // This will create a `feeByBlockTarget` map with the following values,
// - 20: testFeeRate // - 2: 4000 sat/kb
// - 100: 42, which will be floored to feeFloor. // - 6: 2000 sat/kb.
testFees := make(map[uint32]uint32) feeRateResp := map[uint32]uint32{
for _, tc := range testCases { minTarget: maxFeeRate,
if tc.apiEst != 0 { maxTarget: minFeeRate,
testFees[tc.target] = tc.apiEst
}
} }
feeSource := mockSparseConfFeeSource{ feeSource := mockSparseConfFeeSource{
url: "https://www.github.com", url: "https://www.github.com",
fees: testFees, fees: feeRateResp,
} }
estimator := NewWebAPIEstimator(feeSource, false) estimator := NewWebAPIEstimator(feeSource, false)
// Test that requesting a fee when no fees have been cached fails. // Test that requesting a fee when no fees have been cached won't fail.
feeRate, err := estimator.EstimateFeePerKW(5) feeRate, err := estimator.EstimateFeePerKW(5)
require.NoErrorf(t, err, "expected no error") require.NoErrorf(t, err, "expected no error")
require.Equalf(t, FeePerKwFloor, feeRate, "expected fee rate floor "+ require.Equalf(t, FeePerKwFloor, feeRate, "expected fee rate floor "+
"returned when no cached fee rate found") "returned when no cached fee rate found")
if err := estimator.Start(); err != nil { require.NoError(t, estimator.Start(), "unable to start fee estimator")
t.Fatalf("unable to start fee estimator, got: %v", err)
}
defer estimator.Stop()
for _, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
@ -238,9 +237,9 @@ func TestWebAPIFeeEstimator(t *testing.T) {
est, err := estimator.EstimateFeePerKW(tc.target) est, err := estimator.EstimateFeePerKW(tc.target)
// Test an error case. // Test an error case.
if tc.err != "" { if tc.expectedErr != "" {
require.Error(t, err, "expected error") require.Error(t, err, "expected error")
require.ErrorContains(t, err, tc.err) require.ErrorContains(t, err, tc.expectedErr)
return return
} }
@ -249,57 +248,78 @@ func TestWebAPIFeeEstimator(t *testing.T) {
require.NoErrorf(t, err, "error from target %v", require.NoErrorf(t, err, "error from target %v",
tc.target) tc.target)
exp := SatPerKVByte(tc.est).FeePerKWeight() exp := SatPerKVByte(tc.expectedFeeRate).FeePerKWeight()
require.Equalf(t, exp, est, "target %v failed, fee "+ require.Equalf(t, exp, est, "target %v failed, fee "+
"map is %v", tc.target, feeSource.fees) "map is %v", tc.target, feeSource.fees)
}) })
} }
// Stop the estimator when test ends.
require.NoError(t, estimator.Stop(), "unable to stop fee estimator")
} }
// TestGetCachedFee checks that the fee caching logic works as expected. // TestGetCachedFee checks that the fee caching logic works as expected.
func TestGetCachedFee(t *testing.T) { func TestGetCachedFee(t *testing.T) {
target := uint32(2) var (
fee := uint32(100) minTarget uint32 = 2
maxTarget uint32 = 6
minFeeRate uint32 = 100
maxFeeRate uint32 = 1000
)
// Create a dummy estimator without WebAPIFeeSource. // Create a dummy estimator without WebAPIFeeSource.
estimator := NewWebAPIEstimator(nil, false) estimator := NewWebAPIEstimator(nil, false)
// When the cache is empty, an error should be returned. // When the cache is empty, an error should be returned.
cachedFee, err := estimator.getCachedFee(target) cachedFee, err := estimator.getCachedFee(minTarget)
require.Zero(t, cachedFee) require.Zero(t, cachedFee)
require.ErrorIs(t, err, errEmptyCache) require.ErrorIs(t, err, errEmptyCache)
// Store a fee rate inside the cache. // Store a fee rate inside the cache. The cache map now looks like,
estimator.feeByBlockTarget[target] = fee // {2: 1000, 6: 100}
estimator.feeByBlockTarget = map[uint32]uint32{
minTarget: maxFeeRate,
maxTarget: minFeeRate,
}
testCases := []struct { testCases := []struct {
name string name string
confTarget uint32 confTarget uint32
expectedFee uint32 expectedFee uint32
expectErr error
}{ }{
{ {
// When the target is cached, return it. // When the target is cached, return it.
name: "return cached fee", name: "return cached fee",
confTarget: target, confTarget: minTarget,
expectedFee: fee, expectedFee: maxFeeRate,
expectErr: nil,
}, },
{ {
// When the target is not cached, return the next // When the target is not cached, return the next
// lowest target that's cached. // lowest target that's cached. In this case,
// requesting fee rate for target 7 will give the
// result for target 6.
name: "return lowest cached fee",
confTarget: maxTarget + 1,
expectedFee: minFeeRate,
},
{
// When the target is not cached, return the next
// lowest target that's cached. In this case,
// requesting fee rate for target 5 will give the
// result for target 2.
name: "return next cached fee", name: "return next cached fee",
confTarget: target + 1, confTarget: maxTarget - 1,
expectedFee: fee, expectedFee: maxFeeRate,
expectErr: nil,
}, },
{ {
// When the target is not cached, and the next lowest // When the target is not cached, and the next lowest
// target is not cached, return the nearest fee rate. // target is not cached, return the nearest fee rate.
// In this case, requesting fee rate for target 1 will
// give the result for target 2.
name: "return highest cached fee", name: "return highest cached fee",
confTarget: target - 1, confTarget: minTarget - 1,
expectedFee: fee, expectedFee: maxFeeRate,
expectErr: nil,
}, },
} }
@ -309,8 +329,8 @@ func TestGetCachedFee(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cachedFee, err := estimator.getCachedFee(tc.confTarget) cachedFee, err := estimator.getCachedFee(tc.confTarget)
require.NoError(t, err)
require.Equal(t, tc.expectedFee, cachedFee) require.Equal(t, tc.expectedFee, cachedFee)
require.ErrorIs(t, err, tc.expectErr)
}) })
} }
} }