mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-04-19 03:01:06 -04:00
**What type of PR is this?** Other **What does this PR do? Why is it needed?** Follow up to https://github.com/OffchainLabs/prysm/pull/16215 this pr improves logging, fixes stuttering in package naming, adds additional unit tests, and deduplicates fallback node code. **Which issues(s) does this PR fix?** fixes a potential race if reconnecting to the same host very quickly which has a stale connection still. **Other notes for review** **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable).
297 lines
5.8 KiB
Go
297 lines
5.8 KiB
Go
package beacon_api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/validator/client/beacon-api/mock"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
const forkEndpoint = "/eth/v1/beacon/states/head/fork"
|
|
|
|
func TestGetFork_Nominal(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
stateForkResponseJson := structs.GetStateForkResponse{}
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
expected := structs.GetStateForkResponse{
|
|
Data: &structs.Fork{
|
|
PreviousVersion: "0x1",
|
|
CurrentVersion: "0x2",
|
|
Epoch: "3",
|
|
},
|
|
}
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
forkEndpoint,
|
|
&stateForkResponseJson,
|
|
).Return(
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
expected,
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
fork, err := validatorClient.fork(ctx)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, &expected, fork)
|
|
}
|
|
|
|
func TestGetFork_Invalid(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
forkEndpoint,
|
|
gomock.Any(),
|
|
).Return(
|
|
errors.New("custom error"),
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
_, err := validatorClient.fork(ctx)
|
|
require.ErrorContains(t, "custom error", err)
|
|
}
|
|
|
|
const headersEndpoint = "/eth/v1/beacon/headers"
|
|
|
|
func TestGetHeaders_Nominal(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
blockHeadersResponseJson := structs.GetBlockHeadersResponse{}
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
expected := structs.GetBlockHeadersResponse{
|
|
Data: []*structs.SignedBeaconBlockHeaderContainer{
|
|
{
|
|
Header: &structs.SignedBeaconBlockHeader{
|
|
Message: &structs.BeaconBlockHeader{
|
|
Slot: "42",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
headersEndpoint,
|
|
&blockHeadersResponseJson,
|
|
).Return(
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
expected,
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
headers, err := validatorClient.headers(ctx)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, &expected, headers)
|
|
}
|
|
|
|
func TestGetHeaders_Invalid(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
headersEndpoint,
|
|
gomock.Any(),
|
|
).Return(
|
|
errors.New("custom error"),
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
_, err := validatorClient.headers(ctx)
|
|
require.ErrorContains(t, "custom error", err)
|
|
}
|
|
|
|
const livenessEndpoint = "/eth/v1/validator/liveness/42"
|
|
|
|
func TestGetLiveness_Nominal(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
livenessResponseJson := structs.GetLivenessResponse{}
|
|
|
|
indexes := []string{"1", "2"}
|
|
marshalledIndexes, err := json.Marshal(indexes)
|
|
require.NoError(t, err)
|
|
|
|
expected := structs.GetLivenessResponse{
|
|
Data: []*structs.Liveness{
|
|
{
|
|
Index: "1",
|
|
IsLive: true,
|
|
},
|
|
{
|
|
Index: "2",
|
|
IsLive: false,
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := t.Context()
|
|
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
handler.EXPECT().Post(
|
|
gomock.Any(),
|
|
livenessEndpoint,
|
|
nil,
|
|
bytes.NewBuffer(marshalledIndexes),
|
|
&livenessResponseJson,
|
|
).SetArg(
|
|
4,
|
|
expected,
|
|
).Return(
|
|
nil,
|
|
).Times(1)
|
|
|
|
validatorClient := &beaconApiValidatorClient{handler: handler}
|
|
liveness, err := validatorClient.liveness(ctx, 42, indexes)
|
|
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, &expected, liveness)
|
|
}
|
|
|
|
func TestGetLiveness_Invalid(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := t.Context()
|
|
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
handler.EXPECT().Post(
|
|
gomock.Any(),
|
|
livenessEndpoint,
|
|
nil,
|
|
gomock.Any(),
|
|
gomock.Any(),
|
|
).Return(
|
|
errors.New("custom error"),
|
|
).Times(1)
|
|
|
|
validatorClient := &beaconApiValidatorClient{handler: handler}
|
|
_, err := validatorClient.liveness(ctx, 42, nil)
|
|
|
|
require.ErrorContains(t, "custom error", err)
|
|
}
|
|
|
|
const syncingEndpoint = "/eth/v1/node/syncing"
|
|
|
|
func TestGetIsSyncing_Nominal(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
isSyncing bool
|
|
}{
|
|
{
|
|
name: "Syncing",
|
|
isSyncing: true,
|
|
},
|
|
{
|
|
name: "Not syncing",
|
|
isSyncing: false,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
syncingResponseJson := structs.SyncStatusResponse{}
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
expected := structs.SyncStatusResponse{
|
|
Data: &structs.SyncStatusResponseData{
|
|
IsSyncing: testCase.isSyncing,
|
|
},
|
|
}
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
syncingEndpoint,
|
|
&syncingResponseJson,
|
|
).Return(
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
expected,
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
isSyncing, err := validatorClient.isSyncing(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testCase.isSyncing, isSyncing)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetIsSyncing_Invalid(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
syncingResponseJson := structs.SyncStatusResponse{}
|
|
handler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
ctx := t.Context()
|
|
|
|
handler.EXPECT().Get(
|
|
gomock.Any(),
|
|
syncingEndpoint,
|
|
&syncingResponseJson,
|
|
).Return(
|
|
errors.New("custom error"),
|
|
).Times(1)
|
|
|
|
validatorClient := beaconApiValidatorClient{
|
|
handler: handler,
|
|
}
|
|
|
|
isSyncing, err := validatorClient.isSyncing(ctx)
|
|
assert.Equal(t, true, isSyncing)
|
|
assert.ErrorContains(t, "failed to get syncing status", err)
|
|
}
|