2019-10-30 19:43:05 -07:00
|
|
|
package chainfee
|
2018-02-14 08:55:03 +01:00
|
|
|
|
|
|
|
import (
|
2019-04-08 19:31:36 -07:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2018-02-14 08:55:03 +01:00
|
|
|
"testing"
|
2024-04-23 09:49:04 +02:00
|
|
|
"time"
|
2018-02-14 08:55:03 +01:00
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2021-12-07 19:51:16 +08:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-02-14 08:55:03 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// TestFeeRateTypes checks that converting fee rates between the
|
|
|
|
// different types that represent fee rates and calculating fees
|
|
|
|
// work as expected.
|
|
|
|
func TestFeeRateTypes(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
// We'll be calculating the transaction fees for the given measurements
|
|
|
|
// using different fee rates and expecting them to match.
|
|
|
|
const vsize = 300
|
|
|
|
const weight = vsize * 4
|
|
|
|
|
|
|
|
// Test the conversion from sat/kw to sat/kb.
|
2019-10-30 19:43:05 -07:00
|
|
|
for feePerKw := SatPerKWeight(250); feePerKw < 10000; feePerKw += 50 {
|
2018-07-27 18:37:05 -07:00
|
|
|
feePerKB := feePerKw.FeePerKVByte()
|
2019-10-30 19:43:05 -07:00
|
|
|
if feePerKB != SatPerKVByte(feePerKw*4) {
|
2018-07-27 18:37:05 -07:00
|
|
|
t.Fatalf("expected %d sat/kb, got %d sat/kb when "+
|
|
|
|
"converting from %d sat/kw", feePerKw*4,
|
|
|
|
feePerKB, feePerKw)
|
2018-02-14 08:55:03 +01:00
|
|
|
}
|
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
// The resulting transaction fee should be the same when using
|
|
|
|
// both rates.
|
|
|
|
expectedFee := btcutil.Amount(feePerKw * weight / 1000)
|
|
|
|
fee1 := feePerKw.FeeForWeight(weight)
|
|
|
|
if fee1 != expectedFee {
|
|
|
|
t.Fatalf("expected fee of %d sats, got %d sats",
|
|
|
|
expectedFee, fee1)
|
2018-02-14 08:55:03 +01:00
|
|
|
}
|
2018-07-27 18:37:05 -07:00
|
|
|
fee2 := feePerKB.FeeForVSize(vsize)
|
|
|
|
if fee2 != expectedFee {
|
|
|
|
t.Fatalf("expected fee of %d sats, got %d sats",
|
|
|
|
expectedFee, fee2)
|
2018-02-14 08:55:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
// Test the conversion from sat/kb to sat/kw.
|
2019-10-30 19:43:05 -07:00
|
|
|
for feePerKB := SatPerKVByte(1000); feePerKB < 40000; feePerKB += 1000 {
|
2018-07-27 18:37:05 -07:00
|
|
|
feePerKw := feePerKB.FeePerKWeight()
|
2019-10-30 19:43:05 -07:00
|
|
|
if feePerKw != SatPerKWeight(feePerKB/4) {
|
2018-07-27 18:37:05 -07:00
|
|
|
t.Fatalf("expected %d sat/kw, got %d sat/kw when "+
|
|
|
|
"converting from %d sat/kb", feePerKB/4,
|
|
|
|
feePerKw, feePerKB)
|
|
|
|
}
|
2018-02-14 08:55:03 +01:00
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
// The resulting transaction fee should be the same when using
|
|
|
|
// both rates.
|
|
|
|
expectedFee := btcutil.Amount(feePerKB * vsize / 1000)
|
|
|
|
fee1 := feePerKB.FeeForVSize(vsize)
|
|
|
|
if fee1 != expectedFee {
|
|
|
|
t.Fatalf("expected fee of %d sats, got %d sats",
|
|
|
|
expectedFee, fee1)
|
|
|
|
}
|
|
|
|
fee2 := feePerKw.FeeForWeight(weight)
|
|
|
|
if fee2 != expectedFee {
|
|
|
|
t.Fatalf("expected fee of %d sats, got %d sats",
|
|
|
|
expectedFee, fee2)
|
2018-02-14 08:55:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 19:31:36 -07:00
|
|
|
// TestStaticFeeEstimator checks that the StaticFeeEstimator returns the
|
|
|
|
// expected fee rate.
|
2018-02-14 08:55:03 +01:00
|
|
|
func TestStaticFeeEstimator(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2019-10-30 19:43:05 -07:00
|
|
|
const feePerKw = FeePerKwFloor
|
2018-02-14 08:55:03 +01:00
|
|
|
|
2019-10-30 19:43:05 -07:00
|
|
|
feeEstimator := NewStaticEstimator(feePerKw, 0)
|
2018-02-14 08:55:03 +01:00
|
|
|
if err := feeEstimator.Start(); err != nil {
|
|
|
|
t.Fatalf("unable to start fee estimator: %v", err)
|
|
|
|
}
|
|
|
|
defer feeEstimator.Stop()
|
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
feeRate, err := feeEstimator.EstimateFeePerKW(6)
|
2022-05-05 20:11:50 +00:00
|
|
|
require.NoError(t, err, "unable to get fee rate")
|
2018-02-14 08:55:03 +01:00
|
|
|
|
2018-07-27 18:37:05 -07:00
|
|
|
if feeRate != feePerKw {
|
|
|
|
t.Fatalf("expected fee rate %v, got %v", feePerKw, feeRate)
|
2018-02-14 08:55:03 +01:00
|
|
|
}
|
|
|
|
}
|
2019-04-08 19:31:36 -07:00
|
|
|
|
|
|
|
// TestSparseConfFeeSource checks that SparseConfFeeSource generates URLs and
|
|
|
|
// parses API responses as expected.
|
|
|
|
func TestSparseConfFeeSource(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
// Test that GenQueryURL returns the URL as is.
|
|
|
|
url := "test"
|
2019-10-30 19:43:05 -07:00
|
|
|
feeSource := SparseConfFeeSource{URL: url}
|
2019-04-08 19:31:36 -07:00
|
|
|
|
|
|
|
// Test parsing a properly formatted JSON API response.
|
|
|
|
// First, create the response as a bytes.Reader.
|
|
|
|
testFees := map[uint32]uint32{
|
|
|
|
1: 12345,
|
|
|
|
2: 42,
|
|
|
|
3: 54321,
|
|
|
|
}
|
2024-07-04 20:32:41 +08:00
|
|
|
testMinRelayFee := SatPerKVByte(1000)
|
|
|
|
testResp := WebAPIResponse{
|
|
|
|
MinRelayFeerate: testMinRelayFee,
|
|
|
|
FeeByBlockTarget: testFees,
|
2023-10-19 22:52:35 +08:00
|
|
|
}
|
2024-07-04 20:32:41 +08:00
|
|
|
|
|
|
|
jsonResp, err := json.Marshal(testResp)
|
2022-05-05 20:11:50 +00:00
|
|
|
require.NoError(t, err, "unable to marshal JSON API response")
|
2019-04-08 19:31:36 -07:00
|
|
|
reader := bytes.NewReader(jsonResp)
|
|
|
|
|
|
|
|
// Finally, ensure the expected map is returned without error.
|
2024-07-04 20:32:41 +08:00
|
|
|
resp, err := feeSource.parseResponse(reader)
|
2022-05-05 20:11:50 +00:00
|
|
|
require.NoError(t, err, "unable to parse API response")
|
2024-07-04 20:32:41 +08:00
|
|
|
require.Equal(t, testResp, resp, "unexpected resp returned")
|
2019-04-08 19:31:36 -07:00
|
|
|
|
|
|
|
// Test parsing an improperly formatted JSON API response.
|
|
|
|
badFees := map[string]uint32{"hi": 12345, "hello": 42, "satoshi": 54321}
|
|
|
|
badJSON := map[string]map[string]uint32{"fee_by_block_target": badFees}
|
|
|
|
jsonResp, err = json.Marshal(badJSON)
|
2022-05-05 20:11:50 +00:00
|
|
|
require.NoError(t, err, "unable to marshal JSON API response")
|
2019-04-08 19:31:36 -07:00
|
|
|
reader = bytes.NewReader(jsonResp)
|
|
|
|
|
|
|
|
// Finally, ensure the improperly formatted fees error.
|
2023-10-19 22:52:35 +08:00
|
|
|
_, err = feeSource.parseResponse(reader)
|
|
|
|
require.Error(t, err, "expected error when parsing bad JSON")
|
2019-04-08 19:31:36 -07:00
|
|
|
}
|
2019-04-10 10:12:12 -07:00
|
|
|
|
2024-07-04 20:32:41 +08:00
|
|
|
// TestFeeSourceCompatibility checks that when a fee source doesn't return a
|
|
|
|
// `min_relay_feerate` field in its response, the floor feerate is used.
|
|
|
|
//
|
|
|
|
// NOTE: Field `min_relay_feerate` was added in v0.18.3.
|
|
|
|
func TestFeeSourceCompatibility(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
// Test that GenQueryURL returns the URL as is.
|
|
|
|
url := "test"
|
|
|
|
feeSource := SparseConfFeeSource{URL: url}
|
|
|
|
|
|
|
|
// Test parsing a properly formatted JSON API response.
|
|
|
|
//
|
|
|
|
// Create the resp without the `min_relay_feerate` field.
|
|
|
|
testFees := map[uint32]uint32{
|
|
|
|
1: 12345,
|
|
|
|
}
|
|
|
|
testResp := struct {
|
|
|
|
// FeeByBlockTarget is a map of confirmation targets to sat/kvb
|
|
|
|
// fees.
|
|
|
|
FeeByBlockTarget map[uint32]uint32 `json:"fee_by_block_target"`
|
|
|
|
}{
|
|
|
|
FeeByBlockTarget: testFees,
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonResp, err := json.Marshal(testResp)
|
|
|
|
require.NoError(t, err, "unable to marshal JSON API response")
|
|
|
|
reader := bytes.NewReader(jsonResp)
|
|
|
|
|
|
|
|
// Ensure the expected map is returned without error.
|
|
|
|
resp, err := feeSource.parseResponse(reader)
|
|
|
|
require.NoError(t, err, "unable to parse API response")
|
|
|
|
require.Equal(t, testResp.FeeByBlockTarget, resp.FeeByBlockTarget,
|
|
|
|
"unexpected resp returned")
|
|
|
|
|
|
|
|
// Expect the floor feerate to be used.
|
|
|
|
require.Equal(t, FeePerKwFloor.FeePerKVByte(), resp.MinRelayFeerate)
|
|
|
|
}
|
|
|
|
|
2019-04-10 10:12:12 -07:00
|
|
|
// TestWebAPIFeeEstimator checks that the WebAPIFeeEstimator returns fee rates
|
|
|
|
// as expected.
|
|
|
|
func TestWebAPIFeeEstimator(t *testing.T) {
|
|
|
|
t.Parallel()
|
2023-08-23 06:43:16 +08:00
|
|
|
|
|
|
|
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
|
2024-04-23 09:49:04 +02:00
|
|
|
|
|
|
|
minFeeUpdateTimeout = 5 * time.Minute
|
|
|
|
maxFeeUpdateTimeout = 20 * time.Minute
|
2023-08-23 06:43:16 +08:00
|
|
|
)
|
2021-12-07 20:14:06 +08:00
|
|
|
|
2019-04-10 10:12:12 -07:00
|
|
|
testCases := []struct {
|
2023-08-23 06:43:16 +08:00
|
|
|
name string
|
|
|
|
target uint32
|
|
|
|
expectedFeeRate uint32
|
|
|
|
expectedErr string
|
2019-04-10 10:12:12 -07:00
|
|
|
}{
|
2021-12-07 20:14:06 +08:00
|
|
|
{
|
2023-08-23 06:43:16 +08:00
|
|
|
// When requested target is below minBlockTarget, an
|
|
|
|
// error is returned.
|
|
|
|
name: "target_below_min",
|
|
|
|
target: 0,
|
|
|
|
expectedFeeRate: 0,
|
|
|
|
expectedErr: "too low, minimum",
|
2021-12-07 20:14:06 +08:00
|
|
|
},
|
|
|
|
{
|
2023-08-23 06:43:16 +08:00
|
|
|
// When requested target is larger than the max cached
|
|
|
|
// target, the fee rate of the max cached target is
|
|
|
|
// returned.
|
|
|
|
name: "target_w_too-low_fee",
|
|
|
|
target: maxTarget + 100,
|
|
|
|
expectedFeeRate: minFeeRate,
|
|
|
|
expectedErr: "",
|
2021-12-07 20:14:06 +08:00
|
|
|
},
|
|
|
|
{
|
2023-09-20 11:37:32 -04:00
|
|
|
// When requested target is smaller than the min cached
|
2023-08-23 06:43:16 +08:00
|
|
|
// target, the fee rate of the min cached target is
|
|
|
|
// returned.
|
|
|
|
name: "API-omitted_target",
|
|
|
|
target: minTarget - 1,
|
|
|
|
expectedFeeRate: maxFeeRate,
|
|
|
|
expectedErr: "",
|
2021-12-07 20:14:06 +08:00
|
|
|
},
|
|
|
|
{
|
2023-08-23 06:43:16 +08:00
|
|
|
// When the target is found, return it.
|
|
|
|
name: "valid_target",
|
|
|
|
target: maxTarget,
|
|
|
|
expectedFeeRate: minFeeRate,
|
|
|
|
expectedErr: "",
|
2021-12-07 20:14:06 +08:00
|
|
|
},
|
2019-04-10 10:12:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Construct mock fee source for the Estimator to pull fees from.
|
2023-03-22 16:13:17 +07:00
|
|
|
//
|
|
|
|
// This will create a `feeByBlockTarget` map with the following values,
|
2023-08-23 06:43:16 +08:00
|
|
|
// - 2: 4000 sat/kb
|
|
|
|
// - 6: 2000 sat/kb.
|
2024-07-04 20:32:41 +08:00
|
|
|
feeRates := map[uint32]uint32{
|
2023-08-23 06:43:16 +08:00
|
|
|
minTarget: maxFeeRate,
|
|
|
|
maxTarget: minFeeRate,
|
2019-04-10 10:12:12 -07:00
|
|
|
}
|
2024-07-04 20:32:41 +08:00
|
|
|
resp := WebAPIResponse{
|
|
|
|
FeeByBlockTarget: feeRates,
|
|
|
|
}
|
2019-04-10 10:12:12 -07:00
|
|
|
|
2023-10-19 22:59:57 +08:00
|
|
|
// Create a mock fee source and mock its returned map.
|
|
|
|
feeSource := &mockFeeSource{}
|
2024-07-04 20:32:41 +08:00
|
|
|
feeSource.On("GetFeeInfo").Return(resp, nil)
|
2019-04-10 10:12:12 -07:00
|
|
|
|
2024-04-23 09:49:04 +02:00
|
|
|
estimator, _ := NewWebAPIEstimator(
|
|
|
|
feeSource, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
|
|
|
|
)
|
2019-04-10 10:12:12 -07:00
|
|
|
|
2024-07-04 20:19:21 +08:00
|
|
|
// Test that when the estimator is not started, an error is returned.
|
2021-12-07 20:14:06 +08:00
|
|
|
feeRate, err := estimator.EstimateFeePerKW(5)
|
2024-07-04 20:19:21 +08:00
|
|
|
require.Error(t, err, "expected an error")
|
|
|
|
require.Zero(t, feeRate, "expected zero fee rate")
|
2019-04-10 10:12:12 -07:00
|
|
|
|
2024-07-04 20:19:21 +08:00
|
|
|
// Start the estimator.
|
2023-08-23 06:43:16 +08:00
|
|
|
require.NoError(t, estimator.Start(), "unable to start fee estimator")
|
2019-04-10 10:12:12 -07:00
|
|
|
|
|
|
|
for _, tc := range testCases {
|
2019-10-30 19:43:05 -07:00
|
|
|
tc := tc
|
2019-04-10 10:12:12 -07:00
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
est, err := estimator.EstimateFeePerKW(tc.target)
|
2023-03-22 16:13:17 +07:00
|
|
|
|
|
|
|
// Test an error case.
|
2023-08-23 06:43:16 +08:00
|
|
|
if tc.expectedErr != "" {
|
2023-03-22 16:13:17 +07:00
|
|
|
require.Error(t, err, "expected error")
|
2023-08-23 06:43:16 +08:00
|
|
|
require.ErrorContains(t, err, tc.expectedErr)
|
2023-03-22 16:13:17 +07:00
|
|
|
|
|
|
|
return
|
2019-04-10 10:12:12 -07:00
|
|
|
}
|
2023-03-22 16:13:17 +07:00
|
|
|
|
|
|
|
// Test an non-error case.
|
|
|
|
require.NoErrorf(t, err, "error from target %v",
|
|
|
|
tc.target)
|
|
|
|
|
2023-08-23 06:43:16 +08:00
|
|
|
exp := SatPerKVByte(tc.expectedFeeRate).FeePerKWeight()
|
2023-03-22 16:13:17 +07:00
|
|
|
require.Equalf(t, exp, est, "target %v failed, fee "+
|
2024-07-04 20:32:41 +08:00
|
|
|
"map is %v", tc.target, feeRate)
|
2019-04-10 10:12:12 -07:00
|
|
|
})
|
|
|
|
}
|
2023-08-23 06:43:16 +08:00
|
|
|
|
|
|
|
// Stop the estimator when test ends.
|
|
|
|
require.NoError(t, estimator.Stop(), "unable to stop fee estimator")
|
2023-10-19 22:59:57 +08:00
|
|
|
|
|
|
|
// Assert the mocked fee source is called as expected.
|
|
|
|
feeSource.AssertExpectations(t)
|
2019-04-10 10:12:12 -07:00
|
|
|
}
|
2021-12-07 19:51:16 +08:00
|
|
|
|
|
|
|
// TestGetCachedFee checks that the fee caching logic works as expected.
|
|
|
|
func TestGetCachedFee(t *testing.T) {
|
2023-08-23 06:43:16 +08:00
|
|
|
var (
|
|
|
|
minTarget uint32 = 2
|
|
|
|
maxTarget uint32 = 6
|
|
|
|
|
|
|
|
minFeeRate uint32 = 100
|
|
|
|
maxFeeRate uint32 = 1000
|
2024-04-23 09:49:04 +02:00
|
|
|
|
|
|
|
minFeeUpdateTimeout = 5 * time.Minute
|
|
|
|
maxFeeUpdateTimeout = 20 * time.Minute
|
2023-08-23 06:43:16 +08:00
|
|
|
)
|
2021-12-07 19:51:16 +08:00
|
|
|
|
|
|
|
// Create a dummy estimator without WebAPIFeeSource.
|
2024-04-23 09:49:04 +02:00
|
|
|
estimator, _ := NewWebAPIEstimator(
|
|
|
|
nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
|
|
|
|
)
|
2021-12-07 19:51:16 +08:00
|
|
|
|
|
|
|
// When the cache is empty, an error should be returned.
|
2023-08-23 06:43:16 +08:00
|
|
|
cachedFee, err := estimator.getCachedFee(minTarget)
|
2021-12-07 19:51:16 +08:00
|
|
|
require.Zero(t, cachedFee)
|
|
|
|
require.ErrorIs(t, err, errEmptyCache)
|
|
|
|
|
2023-08-23 06:43:16 +08:00
|
|
|
// Store a fee rate inside the cache. The cache map now looks like,
|
|
|
|
// {2: 1000, 6: 100}
|
|
|
|
estimator.feeByBlockTarget = map[uint32]uint32{
|
|
|
|
minTarget: maxFeeRate,
|
|
|
|
maxTarget: minFeeRate,
|
|
|
|
}
|
2021-12-07 19:51:16 +08:00
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
confTarget uint32
|
|
|
|
expectedFee uint32
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
// When the target is cached, return it.
|
|
|
|
name: "return cached fee",
|
2023-08-23 06:43:16 +08:00
|
|
|
confTarget: minTarget,
|
|
|
|
expectedFee: maxFeeRate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// When the target is not cached, return the next
|
|
|
|
// 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,
|
2021-12-07 19:51:16 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// When the target is not cached, return the next
|
2023-08-23 06:43:16 +08:00
|
|
|
// lowest target that's cached. In this case,
|
|
|
|
// requesting fee rate for target 5 will give the
|
|
|
|
// result for target 2.
|
2021-12-07 19:51:16 +08:00
|
|
|
name: "return next cached fee",
|
2023-08-23 06:43:16 +08:00
|
|
|
confTarget: maxTarget - 1,
|
|
|
|
expectedFee: maxFeeRate,
|
2021-12-07 19:51:16 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// When the target is not cached, and the next lowest
|
|
|
|
// target is not cached, return the nearest fee rate.
|
2023-08-23 06:43:16 +08:00
|
|
|
// In this case, requesting fee rate for target 1 will
|
|
|
|
// give the result for target 2.
|
2021-12-07 19:51:16 +08:00
|
|
|
name: "return highest cached fee",
|
2023-08-23 06:43:16 +08:00
|
|
|
confTarget: minTarget - 1,
|
|
|
|
expectedFee: maxFeeRate,
|
2021-12-07 19:51:16 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
tc := tc
|
|
|
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
cachedFee, err := estimator.getCachedFee(tc.confTarget)
|
|
|
|
|
2023-08-23 06:43:16 +08:00
|
|
|
require.NoError(t, err)
|
2021-12-07 19:51:16 +08:00
|
|
|
require.Equal(t, tc.expectedFee, cachedFee)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2024-04-23 09:49:04 +02:00
|
|
|
|
|
|
|
func TestRandomFeeUpdateTimeout(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
var (
|
|
|
|
minFeeUpdateTimeout = 1 * time.Minute
|
|
|
|
maxFeeUpdateTimeout = 2 * time.Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
estimator, _ := NewWebAPIEstimator(
|
|
|
|
nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
timeout := estimator.randomFeeUpdateTimeout()
|
|
|
|
|
|
|
|
require.GreaterOrEqual(t, timeout, minFeeUpdateTimeout)
|
|
|
|
require.LessOrEqual(t, timeout, maxFeeUpdateTimeout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvalidFeeUpdateTimeout(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
var (
|
|
|
|
minFeeUpdateTimeout = 2 * time.Minute
|
|
|
|
maxFeeUpdateTimeout = 1 * time.Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
_, err := NewWebAPIEstimator(
|
|
|
|
nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
|
|
|
|
)
|
|
|
|
require.Error(t, err, "NewWebAPIEstimator should return an error "+
|
|
|
|
"when minFeeUpdateTimeout > maxFeeUpdateTimeout")
|
|
|
|
}
|