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