github.com/Oyster-zx/tendermint@v0.34.24-fork/statesync/reactor_test.go (about) 1 package statesync 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/gogo/protobuf/proto" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/mock" 10 "github.com/stretchr/testify/require" 11 12 abci "github.com/tendermint/tendermint/abci/types" 13 "github.com/tendermint/tendermint/config" 14 "github.com/tendermint/tendermint/p2p" 15 p2pmocks "github.com/tendermint/tendermint/p2p/mocks" 16 ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync" 17 proxymocks "github.com/tendermint/tendermint/proxy/mocks" 18 ) 19 20 func TestReactor_Receive_ChunkRequest(t *testing.T) { 21 testcases := map[string]struct { 22 request *ssproto.ChunkRequest 23 chunk []byte 24 expectResponse *ssproto.ChunkResponse 25 }{ 26 "chunk is returned": { 27 &ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1}, 28 []byte{1, 2, 3}, 29 &ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Chunk: []byte{1, 2, 3}}}, 30 "empty chunk is returned, as nil": { 31 &ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1}, 32 []byte{}, 33 &ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Chunk: nil}}, 34 "nil (missing) chunk is returned as missing": { 35 &ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1}, 36 nil, 37 &ssproto.ChunkResponse{Height: 1, Format: 1, Index: 1, Missing: true}, 38 }, 39 } 40 41 for name, tc := range testcases { 42 tc := tc 43 t.Run(name, func(t *testing.T) { 44 // Mock ABCI connection to return local snapshots 45 conn := &proxymocks.AppConnSnapshot{} 46 conn.On("LoadSnapshotChunkSync", abci.RequestLoadSnapshotChunk{ 47 Height: tc.request.Height, 48 Format: tc.request.Format, 49 Chunk: tc.request.Index, 50 }).Return(&abci.ResponseLoadSnapshotChunk{Chunk: tc.chunk}, nil) 51 52 // Mock peer to store response, if found 53 peer := &p2pmocks.Peer{} 54 peer.On("ID").Return(p2p.ID("id")) 55 var response *ssproto.ChunkResponse 56 if tc.expectResponse != nil { 57 peer.On("SendEnvelope", mock.MatchedBy(func(i interface{}) bool { 58 e, ok := i.(p2p.Envelope) 59 return ok && e.ChannelID == ChunkChannel 60 })).Run(func(args mock.Arguments) { 61 e := args[0].(p2p.Envelope) 62 63 // Marshal to simulate a wire roundtrip. 64 bz, err := proto.Marshal(e.Message) 65 require.NoError(t, err) 66 err = proto.Unmarshal(bz, e.Message) 67 require.NoError(t, err) 68 response = e.Message.(*ssproto.ChunkResponse) 69 }).Return(true) 70 } 71 72 // Start a reactor and send a ssproto.ChunkRequest, then wait for and check response 73 cfg := config.DefaultStateSyncConfig() 74 r := NewReactor(*cfg, conn, nil, "") 75 err := r.Start() 76 require.NoError(t, err) 77 t.Cleanup(func() { 78 if err := r.Stop(); err != nil { 79 t.Error(err) 80 } 81 }) 82 83 r.ReceiveEnvelope(p2p.Envelope{ 84 ChannelID: ChunkChannel, 85 Src: peer, 86 Message: tc.request, 87 }) 88 time.Sleep(100 * time.Millisecond) 89 assert.Equal(t, tc.expectResponse, response) 90 91 conn.AssertExpectations(t) 92 peer.AssertExpectations(t) 93 }) 94 } 95 } 96 97 func TestReactor_Receive_SnapshotsRequest(t *testing.T) { 98 testcases := map[string]struct { 99 snapshots []*abci.Snapshot 100 expectResponses []*ssproto.SnapshotsResponse 101 }{ 102 "no snapshots": {nil, []*ssproto.SnapshotsResponse{}}, 103 ">10 unordered snapshots": { 104 []*abci.Snapshot{ 105 {Height: 1, Format: 2, Chunks: 7, Hash: []byte{1, 2}, Metadata: []byte{1}}, 106 {Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}}, 107 {Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}}, 108 {Height: 1, Format: 1, Chunks: 7, Hash: []byte{1, 1}, Metadata: []byte{4}}, 109 {Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}}, 110 {Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}}, 111 {Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}}, 112 {Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}}, 113 {Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}}, 114 {Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}}, 115 {Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}}, 116 {Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}}, 117 }, 118 []*ssproto.SnapshotsResponse{ 119 {Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}}, 120 {Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}}, 121 {Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}}, 122 {Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}}, 123 {Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}}, 124 {Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}}, 125 {Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}}, 126 {Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}}, 127 {Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}}, 128 {Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}}, 129 }, 130 }, 131 } 132 133 for name, tc := range testcases { 134 tc := tc 135 t.Run(name, func(t *testing.T) { 136 // Mock ABCI connection to return local snapshots 137 conn := &proxymocks.AppConnSnapshot{} 138 conn.On("ListSnapshotsSync", abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{ 139 Snapshots: tc.snapshots, 140 }, nil) 141 142 // Mock peer to catch responses and store them in a slice 143 responses := []*ssproto.SnapshotsResponse{} 144 peer := &p2pmocks.Peer{} 145 if len(tc.expectResponses) > 0 { 146 peer.On("ID").Return(p2p.ID("id")) 147 peer.On("SendEnvelope", mock.MatchedBy(func(i interface{}) bool { 148 e, ok := i.(p2p.Envelope) 149 return ok && e.ChannelID == SnapshotChannel 150 })).Run(func(args mock.Arguments) { 151 e := args[0].(p2p.Envelope) 152 153 // Marshal to simulate a wire roundtrip. 154 bz, err := proto.Marshal(e.Message) 155 require.NoError(t, err) 156 err = proto.Unmarshal(bz, e.Message) 157 require.NoError(t, err) 158 responses = append(responses, e.Message.(*ssproto.SnapshotsResponse)) 159 }).Return(true) 160 } 161 162 // Start a reactor and send a SnapshotsRequestMessage, then wait for and check responses 163 cfg := config.DefaultStateSyncConfig() 164 r := NewReactor(*cfg, conn, nil, "") 165 err := r.Start() 166 require.NoError(t, err) 167 t.Cleanup(func() { 168 if err := r.Stop(); err != nil { 169 t.Error(err) 170 } 171 }) 172 173 r.ReceiveEnvelope(p2p.Envelope{ 174 ChannelID: SnapshotChannel, 175 Src: peer, 176 Message: &ssproto.SnapshotsRequest{}, 177 }) 178 time.Sleep(100 * time.Millisecond) 179 assert.Equal(t, tc.expectResponses, responses) 180 181 conn.AssertExpectations(t) 182 peer.AssertExpectations(t) 183 }) 184 } 185 } 186 187 func TestLegacyReactorReceiveBasic(t *testing.T) { 188 cfg := config.DefaultStateSyncConfig() 189 conn := &proxymocks.AppConnSnapshot{} 190 reactor := NewReactor(*cfg, conn, nil, "") 191 peer := p2p.CreateRandomPeer(false) 192 193 reactor.InitPeer(peer) 194 reactor.AddPeer(peer) 195 m := &ssproto.ChunkRequest{Height: 1, Format: 1, Index: 1} 196 wm := m.Wrap() 197 msg, err := proto.Marshal(wm) 198 assert.NoError(t, err) 199 200 assert.NotPanics(t, func() { 201 reactor.Receive(ChunkChannel, peer, msg) 202 }) 203 }