Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple monitor tests from Core #12995

Merged
merged 11 commits into from
May 21, 2024
5 changes: 5 additions & 0 deletions .changeset/slow-readers-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

Decouple monitoring tests from core #internal
96 changes: 58 additions & 38 deletions core/chains/evm/monitor/balance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/onsi/gomega"
pkgerrors "github.com/pkg/errors"
"github.com/stretchr/testify/assert"
Expand All @@ -15,12 +16,13 @@ import (

"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/services/servicetest"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
ksmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils"
)

var nilBigInt *big.Int
Expand All @@ -35,11 +37,12 @@ func TestBalanceMonitor_Start(t *testing.T) {
t.Parallel()

t.Run("updates balance from nil for multiple keys", func(t *testing.T) {
db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
ethKeyStore := ksmocks.NewEth(t)
k0Addr := testutils.NewAddress()
k1Addr := testutils.NewAddress()
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{k0Addr, k1Addr}, nil)
ethClient := newEthClientMock(t)
_, k1Addr := cltest.MustInsertRandomKey(t, ethKeyStore)
_, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore)

bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t))

Expand All @@ -62,12 +65,12 @@ func TestBalanceMonitor_Start(t *testing.T) {
})

t.Run("handles nil head", func(t *testing.T) {
db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
ethKeyStore := ksmocks.NewEth(t)
k0Addr := testutils.NewAddress()
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{k0Addr}, nil)
ethClient := newEthClientMock(t)

_, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore)

bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t))
k0bal := big.NewInt(42)

Expand All @@ -81,25 +84,25 @@ func TestBalanceMonitor_Start(t *testing.T) {
})

t.Run("cancelled context", func(t *testing.T) {
db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
ethKeyStore := ksmocks.NewEth(t)
k0Addr := testutils.NewAddress()
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{k0Addr}, nil)
ethClient := newEthClientMock(t)

_, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore)

bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t))
ctxCancelledAwaiter := cltest.NewAwaiter()
ctxCancelledAwaiter := testutils.NewAwaiter()

ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Run(func(args mock.Arguments) {
ctx := args.Get(0).(context.Context)
select {
case <-time.After(testutils.WaitTimeout(t)):
case <-time.After(tests.WaitTimeout(t)):
case <-ctx.Done():
ctxCancelledAwaiter.ItHappened()
}
}).Return(nil, nil)

ctx, cancel := context.WithCancel(testutils.Context(t))
ctx, cancel := context.WithCancel(tests.Context(t))
go func() {
<-time.After(time.Second)
cancel()
Expand All @@ -110,12 +113,12 @@ func TestBalanceMonitor_Start(t *testing.T) {
})

t.Run("recovers on error", func(t *testing.T) {
db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
ethKeyStore := ksmocks.NewEth(t)
k0Addr := testutils.NewAddress()
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{k0Addr}, nil)
ethClient := newEthClientMock(t)

_, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore)

bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t))

ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).
Expand All @@ -134,20 +137,20 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) {
t.Parallel()

t.Run("updates balance for multiple keys", func(t *testing.T) {
db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()
ethKeyStore := ksmocks.NewEth(t)
k0Addr := testutils.NewAddress()
k1Addr := testutils.NewAddress()
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{k0Addr, k1Addr}, nil)
ethClient := newEthClientMock(t)

_, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore)
_, k1Addr := cltest.MustInsertRandomKey(t, ethKeyStore)

bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t))
k0bal := big.NewInt(42)
// Deliberately larger than a 64 bit unsigned integer to test overflow
k1bal := big.NewInt(0)
k1bal.SetString("19223372036854776000", 10)

head := cltest.Head(0)
head := testutils.Head(0)

ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal, nil)
ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal, nil)
Expand All @@ -158,7 +161,7 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) {
ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal, nil)

// Do the thing
bm.OnNewLongestChain(testutils.Context(t), head)
bm.OnNewLongestChain(tests.Context(t), head)

<-bm.WorkDone()
assert.Equal(t, k0bal, bm.GetEthBalance(k0Addr).ToInt())
Expand All @@ -168,12 +171,12 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) {
k0bal2 := big.NewInt(142)
k1bal2 := big.NewInt(142)

head = cltest.Head(1)
head = testutils.Head(1)

ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt).Once().Return(k0bal2, nil)
ethClient.On("BalanceAt", mock.Anything, k1Addr, nilBigInt).Once().Return(k1bal2, nil)

bm.OnNewLongestChain(testutils.Context(t), head)
bm.OnNewLongestChain(tests.Context(t), head)

<-bm.WorkDone()
assert.Equal(t, k0bal2, bm.GetEthBalance(k0Addr).ToInt())
Expand All @@ -184,10 +187,9 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) {
func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) {
t.Parallel()

db := pgtest.NewSqlxDB(t)
ethKeyStore := cltest.NewKeyStore(t, db).Eth()

cltest.MustInsertRandomKey(t, ethKeyStore)
ethKeyStore := ksmocks.NewEth(t)
ethKeyStore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).
Return([]common.Address{testutils.NewAddress()}, nil)

ethClient := newEthClientMock(t)

Expand All @@ -197,7 +199,7 @@ func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) {
Return(big.NewInt(1), nil)
servicetest.RunHealthy(t, bm)

head := cltest.Head(0)
head := testutils.Head(0)

// Only expect this twice, even though 10 heads will come in
mockUnblocker := make(chan time.Time)
Expand All @@ -216,11 +218,11 @@ func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) {

// Do the thing multiple times
for i := 0; i < 10; i++ {
bm.OnNewLongestChain(testutils.Context(t), head)
bm.OnNewLongestChain(tests.Context(t), head)
}

// Unblock the first mock
cltest.CallbackOrTimeout(t, "FewerRPCCallsWhenBehind unblock BalanceAt", func() {
callbackOrTimeout(t, "FewerRPCCallsWhenBehind unblock BalanceAt", func() {
mockUnblocker <- time.Time{}
})

Expand Down Expand Up @@ -254,3 +256,21 @@ func Test_ApproximateFloat64(t *testing.T) {
})
}
}

func callbackOrTimeout(t testing.TB, msg string, callback func()) {
t.Helper()

duration := 100 * time.Millisecond

done := make(chan struct{})
go func() {
defer close(done)
callback()
}()

select {
case <-done:
case <-time.After(duration):
t.Fatalf("CallbackOrTimeout: %s timed out", msg)
}
}
21 changes: 0 additions & 21 deletions core/internal/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -1192,27 +1192,6 @@ func (a Awaiter) AwaitOrFail(t testing.TB, durationParams ...time.Duration) {
}
}

func CallbackOrTimeout(t testing.TB, msg string, callback func(), durationParams ...time.Duration) {
t.Helper()

duration := 100 * time.Millisecond
if len(durationParams) > 0 {
duration = durationParams[0]
}

done := make(chan struct{})
go func() {
callback()
close(done)
}()

select {
case <-done:
case <-time.After(duration):
t.Fatalf("CallbackOrTimeout: %s timed out", msg)
}
}

func MustParseURL(t testing.TB, input string) *url.URL {
return testutils.MustParseURL(t, input)
}
Expand Down
Loading