You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
5.9 KiB

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