github.com/noirx94/tendermintmp@v0.0.1/statesync/reactor_test.go (about)

     1  package statesync
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/mock"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	abci "github.com/tendermint/tendermint/abci/types"
    12  	"github.com/tendermint/tendermint/config"
    13  	"github.com/tendermint/tendermint/p2p"
    14  	p2pmocks "github.com/tendermint/tendermint/p2p/mocks"
    15  	ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync"
    16  	proxymocks "github.com/tendermint/tendermint/proxy/mocks"
    17  )
    18  
    19  func TestReactor_Receive_ChunkRequest(t *testing.T) {
    20  	testcases := map[string]struct {
    21  		request        *ssproto.ChunkRequest
    22  		chunk          []byte
    23  		expectResponse *ssproto.ChunkResponse
    24  	}{
    25  		"chunk is returned": {
    26  			&ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1},
    27  			[]byte{1, 2, 3},
    28  			&ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Chunk: []byte{1, 2, 3}}},
    29  		"empty chunk is returned, as nil": {
    30  			&ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1},
    31  			[]byte{},
    32  			&ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Chunk: nil}},
    33  		"nil (missing) chunk is returned as missing": {
    34  			&ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1},
    35  			nil,
    36  			&ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Missing: true},
    37  		},
    38  	}
    39  
    40  	for name, tc := range testcases {
    41  		tc := tc
    42  		t.Run(name, func(t *testing.T) {
    43  			// Mock ABCI connection to return local snapshots
    44  			conn := &proxymocks.AppConnSnapshot{}
    45  			conn.On("LoadSnapshotChunkSync", abci.RequestLoadSnapshotChunk{
    46  				Height: tc.request.Height,
    47  				Format: tc.request.Format,
    48  				Chunk:  tc.request.Index,
    49  			}).Return(&abci.ResponseLoadSnapshotChunk{Chunk: tc.chunk}, nil)
    50  
    51  			// Mock peer to store response, if found
    52  			peer := &p2pmocks.Peer{}
    53  			peer.On("ID").Return(p2p.ID("id"))
    54  			var response *ssproto.ChunkResponse
    55  			if tc.expectResponse != nil {
    56  				peer.On("Send", ChunkChannel, mock.Anything).Run(func(args mock.Arguments) {
    57  					msg, err := decodeMsg(args[1].([]byte))
    58  					require.NoError(t, err)
    59  					response = msg.(*ssproto.ChunkResponse)
    60  				}).Return(true)
    61  			}
    62  
    63  			// Start a reactor and send a ssproto.ChunkRequest, then wait for and check response
    64  			cfg := config.DefaultStateSyncConfig()
    65  			r := NewReactor(*cfg, conn, nil, "")
    66  			err := r.Start()
    67  			require.NoError(t, err)
    68  			t.Cleanup(func() {
    69  				if err := r.Stop(); err != nil {
    70  					t.Error(err)
    71  				}
    72  			})
    73  
    74  			r.Receive(ChunkChannel, peer, mustEncodeMsg(tc.request))
    75  			time.Sleep(100 * time.Millisecond)
    76  			assert.Equal(t, tc.expectResponse, response)
    77  
    78  			conn.AssertExpectations(t)
    79  			peer.AssertExpectations(t)
    80  		})
    81  	}
    82  }
    83  
    84  func TestReactor_Receive_SnapshotsRequest(t *testing.T) {
    85  	testcases := map[string]struct {
    86  		snapshots       []*abci.Snapshot
    87  		expectResponses []*ssproto.SnapshotsResponse
    88  	}{
    89  		"no snapshots": {nil, []*ssproto.SnapshotsResponse{}},
    90  		">10 unordered snapshots": {
    91  			[]*abci.Snapshot{
    92  				{Height: 1, Format: 2, Chunks: 7, Hash: []byte{1, 2}, Metadata: []byte{1}},
    93  				{Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}},
    94  				{Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}},
    95  				{Height: 1, Format: 1, Chunks: 7, Hash: []byte{1, 1}, Metadata: []byte{4}},
    96  				{Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}},
    97  				{Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}},
    98  				{Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}},
    99  				{Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}},
   100  				{Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}},
   101  				{Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}},
   102  				{Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}},
   103  				{Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}},
   104  			},
   105  			[]*ssproto.SnapshotsResponse{
   106  				{Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}},
   107  				{Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}},
   108  				{Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}},
   109  				{Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}},
   110  				{Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}},
   111  				{Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}},
   112  				{Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}},
   113  				{Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}},
   114  				{Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}},
   115  				{Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}},
   116  			},
   117  		},
   118  	}
   119  
   120  	for name, tc := range testcases {
   121  		tc := tc
   122  		t.Run(name, func(t *testing.T) {
   123  			// Mock ABCI connection to return local snapshots
   124  			conn := &proxymocks.AppConnSnapshot{}
   125  			conn.On("ListSnapshotsSync", abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{
   126  				Snapshots: tc.snapshots,
   127  			}, nil)
   128  
   129  			// Mock peer to catch responses and store them in a slice
   130  			responses := []*ssproto.SnapshotsResponse{}
   131  			peer := &p2pmocks.Peer{}
   132  			if len(tc.expectResponses) > 0 {
   133  				peer.On("ID").Return(p2p.ID("id"))
   134  				peer.On("Send", SnapshotChannel, mock.Anything).Run(func(args mock.Arguments) {
   135  					msg, err := decodeMsg(args[1].([]byte))
   136  					require.NoError(t, err)
   137  					responses = append(responses, msg.(*ssproto.SnapshotsResponse))
   138  				}).Return(true)
   139  			}
   140  
   141  			// Start a reactor and send a SnapshotsRequestMessage, then wait for and check responses
   142  			cfg := config.DefaultStateSyncConfig()
   143  			r := NewReactor(*cfg, conn, nil, "")
   144  			err := r.Start()
   145  			require.NoError(t, err)
   146  			t.Cleanup(func() {
   147  				if err := r.Stop(); err != nil {
   148  					t.Error(err)
   149  				}
   150  			})
   151  
   152  			r.Receive(SnapshotChannel, peer, mustEncodeMsg(&ssproto.SnapshotsRequest{}))
   153  			time.Sleep(100 * time.Millisecond)
   154  			assert.Equal(t, tc.expectResponses, responses)
   155  
   156  			conn.AssertExpectations(t)
   157  			peer.AssertExpectations(t)
   158  		})
   159  	}
   160  }