Skip to content

Commit

Permalink
add tests for checkErrorHanlder
Browse files Browse the repository at this point in the history
  • Loading branch information
shileiwill committed Feb 22, 2024
1 parent bad4376 commit bb9295a
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func NewStreamsLookup(
}
}

// Lookup looks through check upkeep results looking for any that need off chain lookup
// Lookup looks through check upkeep results to find any that needs off chain lookup
func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult {
lookups := map[int]*mercury.StreamsLookup{}
for i, checkResult := range checkResults {
Expand Down Expand Up @@ -196,7 +196,7 @@ func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *merc
return
}

// Mercury request returned values, call checkCallback
// Mercury request returned values or user's checkErrorhandler didn't return error, call checkCallback
err = s.CheckCallback(ctx, values, lookup, checkResults, i)
if err != nil {
s.lggr.Errorf("at block %d upkeep %s requested time %s CheckCallback err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package streams
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
Expand All @@ -11,27 +12,26 @@ import (
"testing"
"time"

"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/pkg/errors"
"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury"
v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02"
v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation"

evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks"
iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury"
v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02"
v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03"
)

type MockMercuryConfigProvider struct {
Expand Down Expand Up @@ -118,6 +118,151 @@ func setupStreams(t *testing.T) *streams {
return streams
}

func TestStreams_CheckErrorHandler(t *testing.T) {
upkeepId := big.NewInt(123456789)
blockNumber := uint64(999)
tests := []struct {
name string
lookup *mercury.StreamsLookup
checkResults []ocr2keepers.CheckResult
errCode encoding.ErrCode

callbackResp []byte
callbackErr error

upkeepNeeded bool
performData []byte
wantErr assert.ErrorAssertionFunc

state encoding.PipelineExecutionState
retryable bool
}{
{
name: "success - empty extra data",
lookup: &mercury.StreamsLookup{
StreamsLookupError: &mercury.StreamsLookupError{
FeedParamKey: mercury.FeedIdHex,
Feeds: []string{"ETD-USD", "BTC-ETH"},
TimeParamKey: mercury.BlockNumber,
Time: big.NewInt(100),
ExtraData: []byte{48, 120, 48, 48},
},
UpkeepId: upkeepId,
Block: blockNumber,
},
checkResults: []ocr2keepers.CheckResult{
{},
},
errCode: encoding.ErrCodeNil,
callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
upkeepNeeded: true,
performData: []byte{48, 120, 48, 48},
wantErr: assert.NoError,
},
{
name: "success - with extra data",
lookup: &mercury.StreamsLookup{
StreamsLookupError: &mercury.StreamsLookupError{
FeedParamKey: mercury.FeedIdHex,
Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"},
TimeParamKey: mercury.BlockNumber,
Time: big.NewInt(18952430),
// this is the address of precompile contract ArbSys(0x0000000000000000000000000000000000000064)
ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100},
},
UpkeepId: upkeepId,
Block: blockNumber,
},
checkResults: []ocr2keepers.CheckResult{
{},
},
errCode: encoding.ErrCodeNil,
callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
upkeepNeeded: true,
performData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100},
wantErr: assert.NoError,
},
{
name: "failure - checkCallback eth_call failure",
lookup: &mercury.StreamsLookup{
StreamsLookupError: &mercury.StreamsLookupError{
FeedParamKey: mercury.FeedIdHex,
Feeds: []string{"ETD-USD", "BTC-ETH"},
TimeParamKey: mercury.BlockNumber,
Time: big.NewInt(100),
ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
UpkeepId: upkeepId,
Block: blockNumber,
},
checkResults: []ocr2keepers.CheckResult{
{},
},
errCode: encoding.ErrCodeNil,
callbackResp: []byte{},
callbackErr: errors.New("bad response"),
wantErr: assert.Error,
state: encoding.RpcFlakyFailure,
retryable: true,
upkeepNeeded: false,
},
{
name: "failure - unpack error because of invalid response bytes from checkCallback eth_call",
lookup: &mercury.StreamsLookup{
StreamsLookupError: &mercury.StreamsLookupError{
FeedParamKey: mercury.FeedIdHex,
Feeds: []string{"ETD-USD", "BTC-ETH"},
TimeParamKey: mercury.BlockNumber,
Time: big.NewInt(100),
ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
UpkeepId: upkeepId,
Block: blockNumber,
},
checkResults: []ocr2keepers.CheckResult{
{},
},
errCode: encoding.ErrCodeNil,
callbackResp: []byte{0x01, 0xAB, 0x45, 0xFF, 0x32}, // invalid response bytes
wantErr: assert.Error,
state: encoding.PackUnpackDecodeFailed,
retryable: false,
upkeepNeeded: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := new(evmClientMocks.Client)
s := setupStreams(t)
defer s.Close()

userPayload, err := s.packer.PackUserCheckErrorHandler(tt.errCode, tt.lookup.ExtraData)
require.Nil(t, err)
payload, err := s.abi.Pack("executeCallback", tt.lookup.UpkeepId, userPayload)
require.Nil(t, err)

args := map[string]interface{}{
"to": s.registry.Address().Hex(),
"data": hexutil.Bytes(payload),
}
client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, hexutil.EncodeUint64(tt.lookup.Block)).Return(tt.callbackErr).
Run(func(args mock.Arguments) {
by := args.Get(1).(*hexutil.Bytes)
*by = tt.callbackResp
}).Once()
s.client = client

err = s.CheckErrorHandler(testutils.Context(t), tt.errCode, tt.lookup, tt.checkResults, 0)
tt.wantErr(t, err, fmt.Sprintf("Error assertion failed: %v", tt.name))
assert.Equal(t, uint8(tt.state), tt.checkResults[0].PipelineExecutionState)
assert.Equal(t, tt.retryable, tt.checkResults[0].Retryable)
assert.Equal(t, tt.upkeepNeeded, tt.checkResults[0].Eligible)
assert.Equal(t, tt.performData, tt.checkResults[0].PerformData)
})
}
}

func TestStreams_CheckCallback(t *testing.T) {
upkeepId := big.NewInt(123456789)
bn := uint64(999)
Expand Down Expand Up @@ -246,12 +391,9 @@ func TestStreams_CheckCallback(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := setupStreams(t)
defer r.Close()
r.registry = tt.registry

client := new(evmClientMocks.Client)
s := setupStreams(t)
defer s.Close()
payload, err := s.abi.Pack("checkCallback", tt.lookup.UpkeepId, values, tt.lookup.ExtraData)
require.Nil(t, err)
args := map[string]interface{}{
Expand All @@ -269,6 +411,7 @@ func TestStreams_CheckCallback(t *testing.T) {
tt.wantErr(t, err, fmt.Sprintf("Error assertion failed: %v", tt.name))
assert.Equal(t, uint8(tt.state), tt.input[0].PipelineExecutionState)
assert.Equal(t, tt.retryable, tt.input[0].Retryable)
assert.Equal(t, tt.upkeepNeeded, tt.input[0].Eligible)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sonar.python.version=3.8
# Full exclusions from the static analysis
sonar.exclusions=**/node_modules/**/*,**/mocks/**/*, **/testdata/**/*, **/contracts/typechain/**/*, **/contracts/artifacts/**/*, **/contracts/cache/**/*, **/contracts/scripts/**/*, **/generated/**/*, **/fixtures/**/*, **/docs/**/*, **/tools/**/*, **/*.pb.go, **/*report.xml, **/*.config.ts, **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, core/services/relay/evm/types/*_gen.go, core/services/relay/evm/types/gen/main.go, core/services/relay/evm/testfiles/*, **/core/web/assets**, core/scripts/chaincli/handler/debug.go
# Coverage exclusions
sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go, **/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go
sonar.coverage.exclusions=**/*.test.ts, **/*_test.go, **/contracts/test/**/*, **/contracts/**/tests/**/*, **/core/**/testutils/**/*, **/core/**/mocks/**/*, **/core/**/cltest/**/*, **/integration-tests/**/*, **/generated/**/*, **/core/scripts**/* , **/*.pb.go, ./plugins/**/*, **/main.go, **/0195_add_not_null_to_evm_chain_id_in_job_specs.go
# Duplication exclusions
sonar.cpd.exclusions=**/contracts/**/*.sol, **/config.go, /core/services/ocr2/plugins/ocr2keeper/evm*/*

Expand Down

0 comments on commit bb9295a

Please sign in to comment.