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.

158 lines
5.8 KiB

  1. package statesync
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/mock"
  8. "github.com/stretchr/testify/require"
  9. abci "github.com/tendermint/tendermint/abci/types"
  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", context.Background(), 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. r := NewReactor(conn, nil, "")
  58. err := r.Start()
  59. require.NoError(t, err)
  60. t.Cleanup(func() {
  61. if err := r.Stop(); err != nil {
  62. t.Error(err)
  63. }
  64. })
  65. r.Receive(ChunkChannel, peer, mustEncodeMsg(tc.request))
  66. time.Sleep(100 * time.Millisecond)
  67. assert.Equal(t, tc.expectResponse, response)
  68. conn.AssertExpectations(t)
  69. peer.AssertExpectations(t)
  70. })
  71. }
  72. }
  73. func TestReactor_Receive_SnapshotsRequest(t *testing.T) {
  74. testcases := map[string]struct {
  75. snapshots []*abci.Snapshot
  76. expectResponses []*ssproto.SnapshotsResponse
  77. }{
  78. "no snapshots": {nil, []*ssproto.SnapshotsResponse{}},
  79. ">10 unordered snapshots": {
  80. []*abci.Snapshot{
  81. {Height: 1, Format: 2, Chunks: 7, Hash: []byte{1, 2}, Metadata: []byte{1}},
  82. {Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}},
  83. {Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}},
  84. {Height: 1, Format: 1, Chunks: 7, Hash: []byte{1, 1}, Metadata: []byte{4}},
  85. {Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}},
  86. {Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}},
  87. {Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}},
  88. {Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}},
  89. {Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}},
  90. {Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}},
  91. {Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}},
  92. {Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}},
  93. },
  94. []*ssproto.SnapshotsResponse{
  95. {Height: 3, Format: 4, Chunks: 7, Hash: []byte{3, 4}, Metadata: []byte{9}},
  96. {Height: 3, Format: 3, Chunks: 7, Hash: []byte{3, 3}, Metadata: []byte{12}},
  97. {Height: 3, Format: 2, Chunks: 7, Hash: []byte{3, 2}, Metadata: []byte{3}},
  98. {Height: 3, Format: 1, Chunks: 7, Hash: []byte{3, 1}, Metadata: []byte{6}},
  99. {Height: 2, Format: 4, Chunks: 7, Hash: []byte{2, 4}, Metadata: []byte{8}},
  100. {Height: 2, Format: 3, Chunks: 7, Hash: []byte{2, 3}, Metadata: []byte{11}},
  101. {Height: 2, Format: 2, Chunks: 7, Hash: []byte{2, 2}, Metadata: []byte{2}},
  102. {Height: 2, Format: 1, Chunks: 7, Hash: []byte{2, 1}, Metadata: []byte{5}},
  103. {Height: 1, Format: 4, Chunks: 7, Hash: []byte{1, 4}, Metadata: []byte{7}},
  104. {Height: 1, Format: 3, Chunks: 7, Hash: []byte{1, 3}, Metadata: []byte{10}},
  105. },
  106. },
  107. }
  108. for name, tc := range testcases {
  109. tc := tc
  110. t.Run(name, func(t *testing.T) {
  111. // Mock ABCI connection to return local snapshots
  112. conn := &proxymocks.AppConnSnapshot{}
  113. conn.On("ListSnapshotsSync", context.Background(), abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{
  114. Snapshots: tc.snapshots,
  115. }, nil)
  116. // Mock peer to catch responses and store them in a slice
  117. responses := []*ssproto.SnapshotsResponse{}
  118. peer := &p2pmocks.Peer{}
  119. if len(tc.expectResponses) > 0 {
  120. peer.On("ID").Return(p2p.ID("id"))
  121. peer.On("Send", SnapshotChannel, mock.Anything).Run(func(args mock.Arguments) {
  122. msg, err := decodeMsg(args[1].([]byte))
  123. require.NoError(t, err)
  124. responses = append(responses, msg.(*ssproto.SnapshotsResponse))
  125. }).Return(true)
  126. }
  127. // Start a reactor and send a SnapshotsRequestMessage, then wait for and check responses
  128. r := NewReactor(conn, nil, "")
  129. err := r.Start()
  130. require.NoError(t, err)
  131. t.Cleanup(func() {
  132. if err := r.Stop(); err != nil {
  133. t.Error(err)
  134. }
  135. })
  136. r.Receive(SnapshotChannel, peer, mustEncodeMsg(&ssproto.SnapshotsRequest{}))
  137. time.Sleep(100 * time.Millisecond)
  138. assert.Equal(t, tc.expectResponses, responses)
  139. conn.AssertExpectations(t)
  140. peer.AssertExpectations(t)
  141. })
  142. }
  143. }