prysm-pulse/client/simulator/service_test.go
Raul Jordan b6cd890a67 Log Verbosity Flags + Logrus Package Level Logs With Fields (#304)
Former-commit-id: f7344b7d3fb2fa07f0fd421c5ed3721f6c7a9258 [formerly e678ab2f1681d9492c0e1b142cd06ee08a462fdb]
Former-commit-id: d942da3d5cfcde921bebb149c26edd5c4ed178dd
2018-07-21 12:51:18 -05:00

276 lines
7.3 KiB
Go

package simulator
import (
"context"
"errors"
"fmt"
"io/ioutil"
"math/big"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/prysmaticlabs/prysm/client/mainchain"
"github.com/prysmaticlabs/prysm/client/p2p"
"github.com/prysmaticlabs/prysm/client/params"
"github.com/prysmaticlabs/prysm/client/types"
"github.com/sirupsen/logrus"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func init() {
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(ioutil.Discard)
}
var _ = types.Service(&Simulator{})
type faultyReader struct{}
type goodReader struct{}
type faultySMCCaller struct{}
type goodSMCCaller struct{}
func (f *faultySMCCaller) CollationRecords(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
}, error) {
res := new(struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
})
return *res, errors.New("error fetching collation record")
}
func (g *goodSMCCaller) CollationRecords(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
}, error) {
res := new(struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
})
body := []byte{1, 2, 3, 4, 5}
res.ChunkRoot = [32]byte(gethTypes.DeriveSha(types.Chunks(body)))
res.Proposer = common.BytesToAddress([]byte{})
res.IsElected = false
return *res, nil
}
func (f *faultyReader) BlockByNumber(ctx context.Context, number *big.Int) (*gethTypes.Block, error) {
return nil, fmt.Errorf("cannot fetch block by number")
}
func (f *faultyReader) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) {
return nil, nil
}
func (g *goodReader) BlockByNumber(ctx context.Context, number *big.Int) (*gethTypes.Block, error) {
return gethTypes.NewBlock(&gethTypes.Header{Number: big.NewInt(0)}, nil, nil, nil), nil
}
func (g *goodReader) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) {
return nil, nil
}
func TestStartStop(t *testing.T) {
hook := logTest.NewGlobal()
shardID := 0
server, err := p2p.NewServer()
if err != nil {
t.Fatalf("Unable to setup p2p server: %v", err)
}
simulator, err := NewSimulator(params.DefaultConfig, &mainchain.SMCClient{}, server, shardID, 1*time.Second)
if err != nil {
t.Fatalf("Unable to setup simulator service: %v", err)
}
simulator.Start()
msg := hook.LastEntry().Message
if msg != "Starting simulator service" {
t.Errorf("incorrect log, expected %s, got %s", "Starting simulator service", msg)
}
if err := simulator.Stop(); err != nil {
t.Fatalf("Unable to stop simulator service: %v", err)
}
msg = hook.LastEntry().Message
if msg != "Stopping simulator service" {
t.Errorf("incorrect log, expected %s, got %s", "Stopping simulator service", msg)
}
// The context should have been canceled.
if simulator.ctx.Err() == nil {
t.Error("Context was not canceled")
}
hook.Reset()
}
// This test uses a faulty chain reader in order to trigger an error
// in the simulateNotaryRequests goroutine when reading the block number from
// the mainchain via RPC.
func TestSimulateNotaryRequests_FaultyReader(t *testing.T) {
hook := logTest.NewGlobal()
shardID := 0
server, err := p2p.NewServer()
if err != nil {
t.Fatalf("Unable to setup p2p server: %v", err)
}
simulator, err := NewSimulator(params.DefaultConfig, &mainchain.SMCClient{}, server, shardID, 0)
if err != nil {
t.Fatalf("Unable to setup simulator service: %v", err)
}
delayChan := make(chan time.Time)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
simulator.simulateNotaryRequests(&goodSMCCaller{}, &faultyReader{}, delayChan, doneChan)
<-exitRoutine
}()
delayChan <- time.Time{}
doneChan <- struct{}{}
msg := hook.LastEntry().Message
want := "Could not fetch current block number: cannot fetch block by number"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
exitRoutine <- true
hook.Reset()
}
// This test uses a faulty SMCCaller in order to trigger an error
// in the simulateNotaryRequests goroutine when reading the collation records
// from the SMC.
func TestSimulateNotaryRequests_FaultyCaller(t *testing.T) {
hook := logTest.NewGlobal()
shardID := 0
server, err := p2p.NewServer()
if err != nil {
t.Fatalf("Unable to setup p2p server: %v", err)
}
simulator, err := NewSimulator(params.DefaultConfig, &mainchain.SMCClient{}, server, shardID, 0)
if err != nil {
t.Fatalf("Unable to setup simulator service: %v", err)
}
delayChan := make(chan time.Time)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
simulator.simulateNotaryRequests(&faultySMCCaller{}, &goodReader{}, delayChan, doneChan)
<-exitRoutine
}()
delayChan <- time.Time{}
doneChan <- struct{}{}
msg := hook.AllEntries()[0].Message
want := "Error constructing collation body request: could not fetch collation record from SMC: error fetching collation record"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
exitRoutine <- true
hook.Reset()
}
// This test checks the proper functioning of the simulateNotaryRequests goroutine
// by listening to the requestSent channel which occurs after successful
// construction and sending of a request via p2p.
func TestSimulateNotaryRequests(t *testing.T) {
hook := logTest.NewGlobal()
shardID := 0
server, err := p2p.NewServer()
if err != nil {
t.Fatalf("Unable to setup p2p server: %v", err)
}
simulator, err := NewSimulator(params.DefaultConfig, &mainchain.SMCClient{}, server, shardID, 0)
if err != nil {
t.Fatalf("Unable to setup simulator service: %v", err)
}
delayChan := make(chan time.Time)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
simulator.simulateNotaryRequests(&goodSMCCaller{}, &goodReader{}, delayChan, doneChan)
<-exitRoutine
}()
delayChan <- time.Time{}
doneChan <- struct{}{}
msg := hook.Entries[1].Message
want := "Sent request for collation body via a shardp2p broadcast"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
exitRoutine <- true
hook.Reset()
}
// This test verifies actor simulator can successfully broadcast
// transactions to rest of the peers.
func TestBroadcastTransactions(t *testing.T) {
hook := logTest.NewGlobal()
shardID := 0
server, err := p2p.NewServer()
if err != nil {
t.Fatalf("Unable to setup p2p server: %v", err)
}
simulator, err := NewSimulator(params.DefaultConfig, &mainchain.SMCClient{}, server, shardID, 1*time.Second)
if err != nil {
t.Fatalf("Unable to setup simulator service: %v", err)
}
delayChan := make(chan time.Time)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
simulator.broadcastTransactions(delayChan, doneChan)
<-exitRoutine
}()
delayChan <- time.Time{}
doneChan <- struct{}{}
msg := hook.Entries[1].Message
want := "Transaction broadcasted"
if !strings.Contains(msg, want) {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
exitRoutine <- true
hook.Reset()
}