-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds unit tests to the scheduler package. Also adds some helper test utils: - app/golden: for golden file testing - beaconmock: beacon client mock category: testing ticket: #145
- Loading branch information
1 parent
a63ca8a
commit 1f93dc2
Showing
10 changed files
with
735 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright © 2021 Obol Technologies Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package golden provides a test utility for asserting blobs against golden files in a testdata folder. | ||
// This is heavily inspired from https://github.com/sebdah/goldie. | ||
package golden | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"os" | ||
"path" | ||
"strings" | ||
"sync" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var ( | ||
update = flag.Bool("update", false, "Create or update golden files, instead of comparing them") | ||
clean = flag.Bool("clean", false, "Deletes the testdata folder before updating (noop of update==false)") | ||
) | ||
|
||
var cleanOnce sync.Once | ||
|
||
// RequireBytes asserts that a golden testdata file exists containing the exact data. | ||
func RequireBytes(t *testing.T, data []byte) { | ||
t.Helper() | ||
|
||
filename := path.Join("testdata", strings.ReplaceAll(t.Name(), "/", "_")) | ||
|
||
if *update { | ||
if *clean { | ||
cleanOnce.Do(func() { | ||
_ = os.RemoveAll("testdata") | ||
}) | ||
} | ||
|
||
require.NoError(t, os.MkdirAll("testdata", 0o755)) | ||
|
||
_ = os.Remove(filename) | ||
require.NoError(t, os.WriteFile(filename, data, 0o644)) //nolint:gosec | ||
|
||
return | ||
} | ||
|
||
expected, err := os.ReadFile(filename) | ||
if os.IsNotExist(err) { | ||
t.Fatalf("golden file does not exist, %s, generate by running with -update", filename) | ||
return | ||
} | ||
|
||
require.Equalf(t, expected, data, "Golden file mismatch, %s", filename) | ||
} | ||
|
||
// RequireJSON asserts that a golden testdata file exists containing the JSON serialised form of the data object. | ||
func RequireJSON(t *testing.T, data interface{}) { | ||
t.Helper() | ||
|
||
b, err := json.MarshalIndent(data, "", " ") | ||
require.NoError(t, err) | ||
|
||
RequireBytes(t, b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright © 2021 Obol Technologies Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package beaconmock provides a mock beacon client primarily for testing. | ||
package beaconmock | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
eth2client "github.com/attestantio/go-eth2-client" | ||
eth2v1 "github.com/attestantio/go-eth2-client/api/v1" | ||
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0" | ||
) | ||
|
||
// Interface assertions. | ||
var ( | ||
_ eth2client.Service = (*Mock)(nil) | ||
_ eth2client.NodeSyncingProvider = (*Mock)(nil) | ||
_ eth2client.GenesisTimeProvider = (*Mock)(nil) | ||
_ eth2client.ValidatorsProvider = (*Mock)(nil) | ||
_ eth2client.SlotsPerEpochProvider = (*Mock)(nil) | ||
_ eth2client.SlotDurationProvider = (*Mock)(nil) | ||
_ eth2client.AttesterDutiesProvider = (*Mock)(nil) | ||
_ eth2client.ProposerDutiesProvider = (*Mock)(nil) | ||
) | ||
|
||
// NewMock returns a new beacon client mock configured with the default and provided options. | ||
func NewMock(opts ...Option) Mock { | ||
mock := defaultMock() | ||
for _, opt := range opts { | ||
opt(&mock) | ||
} | ||
|
||
return mock | ||
} | ||
|
||
// Mock provides a mock beacon client and implements eth2client.Service and many of the erh2client Providers. | ||
type Mock struct { | ||
ProposerDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.ProposerDuty, error) | ||
AttesterDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.AttesterDuty, error) | ||
SlotDurationFunc func(context.Context) (time.Duration, error) | ||
SlotsPerEpochFunc func(context.Context) (uint64, error) | ||
ValidatorsFunc func(context.Context, string, []eth2p0.ValidatorIndex) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error) | ||
ValidatorsByPubKeyFunc func(context.Context, string, []eth2p0.BLSPubKey) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error) | ||
GenesisTimeFunc func(context.Context) (time.Time, error) | ||
NodeSyncingFunc func(context.Context) (*eth2v1.SyncState, error) | ||
} | ||
|
||
func (m Mock) ProposerDuties(ctx context.Context, epoch eth2p0.Epoch, validatorIndices []eth2p0.ValidatorIndex) ([]*eth2v1.ProposerDuty, error) { | ||
return m.ProposerDutiesFunc(ctx, epoch, validatorIndices) | ||
} | ||
|
||
func (m Mock) AttesterDuties(ctx context.Context, epoch eth2p0.Epoch, validatorIndices []eth2p0.ValidatorIndex) ([]*eth2v1.AttesterDuty, error) { | ||
return m.AttesterDutiesFunc(ctx, epoch, validatorIndices) | ||
} | ||
|
||
func (m Mock) SlotDuration(ctx context.Context) (time.Duration, error) { | ||
return m.SlotDurationFunc(ctx) | ||
} | ||
|
||
func (m Mock) SlotsPerEpoch(ctx context.Context) (uint64, error) { | ||
return m.SlotsPerEpochFunc(ctx) | ||
} | ||
|
||
func (m Mock) Validators(ctx context.Context, stateID string, validatorIndices []eth2p0.ValidatorIndex) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error) { | ||
return m.ValidatorsFunc(ctx, stateID, validatorIndices) | ||
} | ||
|
||
func (m Mock) ValidatorsByPubKey(ctx context.Context, stateID string, validatorPubKeys []eth2p0.BLSPubKey) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error) { | ||
return m.ValidatorsByPubKeyFunc(ctx, stateID, validatorPubKeys) | ||
} | ||
|
||
func (m Mock) GenesisTime(ctx context.Context) (time.Time, error) { | ||
return m.GenesisTimeFunc(ctx) | ||
} | ||
|
||
func (m Mock) NodeSyncing(ctx context.Context) (*eth2v1.SyncState, error) { | ||
return m.NodeSyncingFunc(ctx) | ||
} | ||
|
||
func (Mock) Name() string { | ||
return "beacon-mock" | ||
} | ||
|
||
func (Mock) Address() string { | ||
return "mock-address" | ||
} |
Oops, something went wrong.