github.com/Finschia/finschia-sdk@v0.49.1/store/streaming/file/service_test.go (about) 1 package file 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "os" 7 "path/filepath" 8 "sync" 9 "testing" 10 11 ocabci "github.com/Finschia/ostracon/abci/types" 12 "github.com/stretchr/testify/require" 13 abci "github.com/tendermint/tendermint/abci/types" 14 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 15 16 "github.com/Finschia/finschia-sdk/codec" 17 codecTypes "github.com/Finschia/finschia-sdk/codec/types" 18 "github.com/Finschia/finschia-sdk/store/types" 19 sdk "github.com/Finschia/finschia-sdk/types" 20 ) 21 22 var ( 23 interfaceRegistry = codecTypes.NewInterfaceRegistry() 24 testMarshaller = codec.NewProtoCodec(interfaceRegistry) 25 testStreamingService *StreamingService 26 testListener1, testListener2 types.WriteListener 27 emptyContext = sdk.Context{} 28 29 // test abci message types 30 mockHash = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} 31 testBeginBlockReq = ocabci.RequestBeginBlock{ 32 Header: tmproto.Header{ 33 Height: 1, 34 }, 35 ByzantineValidators: []abci.Evidence{}, 36 Hash: mockHash, 37 LastCommitInfo: abci.LastCommitInfo{ 38 Round: 1, 39 Votes: []abci.VoteInfo{}, 40 }, 41 } 42 testBeginBlockRes = abci.ResponseBeginBlock{ 43 Events: []abci.Event{ 44 { 45 Type: "testEventType1", 46 }, 47 { 48 Type: "testEventType2", 49 }, 50 }, 51 } 52 testEndBlockReq = abci.RequestEndBlock{ 53 Height: 1, 54 } 55 testEndBlockRes = abci.ResponseEndBlock{ 56 Events: []abci.Event{}, 57 ConsensusParamUpdates: &abci.ConsensusParams{}, 58 ValidatorUpdates: []abci.ValidatorUpdate{}, 59 } 60 mockTxBytes1 = []byte{9, 8, 7, 6, 5, 4, 3, 2, 1} 61 testDeliverTxReq1 = abci.RequestDeliverTx{ 62 Tx: mockTxBytes1, 63 } 64 mockTxBytes2 = []byte{8, 7, 6, 5, 4, 3, 2} 65 testDeliverTxReq2 = abci.RequestDeliverTx{ 66 Tx: mockTxBytes2, 67 } 68 mockTxResponseData1 = []byte{1, 3, 5, 7, 9} 69 testDeliverTxRes1 = abci.ResponseDeliverTx{ 70 Events: []abci.Event{}, 71 Code: 1, 72 Codespace: "mockCodeSpace", 73 Data: mockTxResponseData1, 74 GasUsed: 2, 75 GasWanted: 3, 76 Info: "mockInfo", 77 Log: "mockLog", 78 } 79 mockTxResponseData2 = []byte{1, 3, 5, 7, 9} 80 testDeliverTxRes2 = abci.ResponseDeliverTx{ 81 Events: []abci.Event{}, 82 Code: 1, 83 Codespace: "mockCodeSpace", 84 Data: mockTxResponseData2, 85 GasUsed: 2, 86 GasWanted: 3, 87 Info: "mockInfo", 88 Log: "mockLog", 89 } 90 91 // mock store keys 92 mockStoreKey1 = sdk.NewKVStoreKey("mockStore1") 93 mockStoreKey2 = sdk.NewKVStoreKey("mockStore2") 94 95 // file stuff 96 testPrefix = "testPrefix" 97 testDir = "./.test" 98 99 // mock state changes 100 mockKey1 = []byte{1, 2, 3} 101 mockValue1 = []byte{3, 2, 1} 102 mockKey2 = []byte{2, 3, 4} 103 mockValue2 = []byte{4, 3, 2} 104 mockKey3 = []byte{3, 4, 5} 105 mockValue3 = []byte{5, 4, 3} 106 ) 107 108 func TestIntermediateWriter(t *testing.T) { 109 outChan := make(chan []byte) 110 iw := NewIntermediateWriter(outChan) 111 require.IsType(t, &IntermediateWriter{}, iw) 112 testBytes := []byte{1, 2, 3, 4, 5} 113 var length int 114 var err error 115 waitChan := make(chan struct{}) 116 go func() { 117 length, err = iw.Write(testBytes) 118 waitChan <- struct{}{} 119 }() 120 receivedBytes := <-outChan 121 <-waitChan 122 require.Equal(t, len(testBytes), length) 123 require.Equal(t, testBytes, receivedBytes) 124 require.Nil(t, err) 125 } 126 127 func TestFileStreamingService(t *testing.T) { 128 if os.Getenv("CI") != "" { 129 t.Skip("Skipping TestFileStreamingService in CI environment") 130 } 131 err := os.Mkdir(testDir, 0o700) 132 require.Nil(t, err) 133 defer os.RemoveAll(testDir) 134 135 testKeys := []types.StoreKey{mockStoreKey1, mockStoreKey2} 136 testStreamingService, err = NewStreamingService(testDir, testPrefix, testKeys, testMarshaller) 137 require.Nil(t, err) 138 require.IsType(t, &StreamingService{}, testStreamingService) 139 require.Equal(t, testPrefix, testStreamingService.filePrefix) 140 require.Equal(t, testDir, testStreamingService.writeDir) 141 require.Equal(t, testMarshaller, testStreamingService.codec) 142 testListener1 = testStreamingService.listeners[mockStoreKey1][0] 143 testListener2 = testStreamingService.listeners[mockStoreKey2][0] 144 wg := new(sync.WaitGroup) 145 err = testStreamingService.Stream(wg) 146 require.NoError(t, err) 147 testListenBeginBlock(t) 148 testListenDeliverTx1(t) 149 testListenDeliverTx2(t) 150 testListenEndBlock(t) 151 err = testStreamingService.Close() 152 require.NoError(t, err) 153 wg.Wait() 154 } 155 156 func testListenBeginBlock(t *testing.T) { 157 t.Helper() 158 expectedBeginBlockReqBytes, err := testMarshaller.Marshal(&testBeginBlockReq) 159 require.Nil(t, err) 160 expectedBeginBlockResBytes, err := testMarshaller.Marshal(&testBeginBlockRes) 161 require.Nil(t, err) 162 163 // write state changes 164 err = testListener1.OnWrite(mockStoreKey1, mockKey1, mockValue1, false) 165 require.NoError(t, err) 166 err = testListener2.OnWrite(mockStoreKey2, mockKey2, mockValue2, false) 167 require.NoError(t, err) 168 err = testListener1.OnWrite(mockStoreKey1, mockKey3, mockValue3, false) 169 require.NoError(t, err) 170 171 // expected KV pairs 172 expectedKVPair1, err := testMarshaller.Marshal(&types.StoreKVPair{ 173 StoreKey: mockStoreKey1.Name(), 174 Key: mockKey1, 175 Value: mockValue1, 176 Delete: false, 177 }) 178 require.Nil(t, err) 179 expectedKVPair2, err := testMarshaller.Marshal(&types.StoreKVPair{ 180 StoreKey: mockStoreKey2.Name(), 181 Key: mockKey2, 182 Value: mockValue2, 183 Delete: false, 184 }) 185 require.Nil(t, err) 186 expectedKVPair3, err := testMarshaller.Marshal(&types.StoreKVPair{ 187 StoreKey: mockStoreKey1.Name(), 188 Key: mockKey3, 189 Value: mockValue3, 190 Delete: false, 191 }) 192 require.Nil(t, err) 193 194 // send the ABCI messages 195 err = testStreamingService.ListenBeginBlock(emptyContext, testBeginBlockReq, testBeginBlockRes) 196 require.Nil(t, err) 197 198 // load the file, checking that it was created with the expected name 199 fileName := fmt.Sprintf("%s-block-%d-begin", testPrefix, testBeginBlockReq.GetHeader().Height) 200 fileBytes, err := readInFile(fileName) 201 require.Nil(t, err) 202 203 // segment the file into the separate gRPC messages and check the correctness of each 204 segments, err := segmentBytes(fileBytes) 205 require.Nil(t, err) 206 require.Equal(t, 5, len(segments)) 207 require.Equal(t, expectedBeginBlockReqBytes, segments[0]) 208 require.Equal(t, expectedKVPair1, segments[1]) 209 require.Equal(t, expectedKVPair2, segments[2]) 210 require.Equal(t, expectedKVPair3, segments[3]) 211 require.Equal(t, expectedBeginBlockResBytes, segments[4]) 212 } 213 214 func testListenDeliverTx1(t *testing.T) { 215 t.Helper() 216 expectedDeliverTxReq1Bytes, err := testMarshaller.Marshal(&testDeliverTxReq1) 217 require.Nil(t, err) 218 expectedDeliverTxRes1Bytes, err := testMarshaller.Marshal(&testDeliverTxRes1) 219 require.Nil(t, err) 220 221 // write state changes 222 err = testListener1.OnWrite(mockStoreKey1, mockKey1, mockValue1, false) 223 require.NoError(t, err) 224 err = testListener2.OnWrite(mockStoreKey2, mockKey2, mockValue2, false) 225 require.NoError(t, err) 226 err = testListener1.OnWrite(mockStoreKey2, mockKey3, mockValue3, false) 227 require.NoError(t, err) 228 229 // expected KV pairs 230 expectedKVPair1, err := testMarshaller.Marshal(&types.StoreKVPair{ 231 StoreKey: mockStoreKey1.Name(), 232 Key: mockKey1, 233 Value: mockValue1, 234 Delete: false, 235 }) 236 require.Nil(t, err) 237 expectedKVPair2, err := testMarshaller.Marshal(&types.StoreKVPair{ 238 StoreKey: mockStoreKey2.Name(), 239 Key: mockKey2, 240 Value: mockValue2, 241 Delete: false, 242 }) 243 require.Nil(t, err) 244 expectedKVPair3, err := testMarshaller.Marshal(&types.StoreKVPair{ 245 StoreKey: mockStoreKey2.Name(), 246 Key: mockKey3, 247 Value: mockValue3, 248 Delete: false, 249 }) 250 require.Nil(t, err) 251 252 // send the ABCI messages 253 err = testStreamingService.ListenDeliverTx(emptyContext, testDeliverTxReq1, testDeliverTxRes1) 254 require.Nil(t, err) 255 256 // load the file, checking that it was created with the expected name 257 fileName := fmt.Sprintf("%s-block-%d-tx-%d", testPrefix, testBeginBlockReq.GetHeader().Height, 0) 258 fileBytes, err := readInFile(fileName) 259 require.Nil(t, err) 260 261 // segment the file into the separate gRPC messages and check the correctness of each 262 segments, err := segmentBytes(fileBytes) 263 require.Nil(t, err) 264 require.Equal(t, 5, len(segments)) 265 require.Equal(t, expectedDeliverTxReq1Bytes, segments[0]) 266 require.Equal(t, expectedKVPair1, segments[1]) 267 require.Equal(t, expectedKVPair2, segments[2]) 268 require.Equal(t, expectedKVPair3, segments[3]) 269 require.Equal(t, expectedDeliverTxRes1Bytes, segments[4]) 270 } 271 272 func testListenDeliverTx2(t *testing.T) { 273 t.Helper() 274 expectedDeliverTxReq2Bytes, err := testMarshaller.Marshal(&testDeliverTxReq2) 275 require.Nil(t, err) 276 expectedDeliverTxRes2Bytes, err := testMarshaller.Marshal(&testDeliverTxRes2) 277 require.Nil(t, err) 278 279 // write state changes 280 err = testListener1.OnWrite(mockStoreKey2, mockKey1, mockValue1, false) 281 require.NoError(t, err) 282 err = testListener2.OnWrite(mockStoreKey1, mockKey2, mockValue2, false) 283 require.NoError(t, err) 284 err = testListener1.OnWrite(mockStoreKey2, mockKey3, mockValue3, false) 285 require.NoError(t, err) 286 287 // expected KV pairs 288 expectedKVPair1, err := testMarshaller.Marshal(&types.StoreKVPair{ 289 StoreKey: mockStoreKey2.Name(), 290 Key: mockKey1, 291 Value: mockValue1, 292 Delete: false, 293 }) 294 require.Nil(t, err) 295 expectedKVPair2, err := testMarshaller.Marshal(&types.StoreKVPair{ 296 StoreKey: mockStoreKey1.Name(), 297 Key: mockKey2, 298 Value: mockValue2, 299 Delete: false, 300 }) 301 require.Nil(t, err) 302 expectedKVPair3, err := testMarshaller.Marshal(&types.StoreKVPair{ 303 StoreKey: mockStoreKey2.Name(), 304 Key: mockKey3, 305 Value: mockValue3, 306 Delete: false, 307 }) 308 require.Nil(t, err) 309 310 // send the ABCI messages 311 err = testStreamingService.ListenDeliverTx(emptyContext, testDeliverTxReq2, testDeliverTxRes2) 312 require.Nil(t, err) 313 314 // load the file, checking that it was created with the expected name 315 fileName := fmt.Sprintf("%s-block-%d-tx-%d", testPrefix, testBeginBlockReq.GetHeader().Height, 1) 316 fileBytes, err := readInFile(fileName) 317 require.Nil(t, err) 318 319 // segment the file into the separate gRPC messages and check the correctness of each 320 segments, err := segmentBytes(fileBytes) 321 require.Nil(t, err) 322 require.Equal(t, 5, len(segments)) 323 require.Equal(t, expectedDeliverTxReq2Bytes, segments[0]) 324 require.Equal(t, expectedKVPair1, segments[1]) 325 require.Equal(t, expectedKVPair2, segments[2]) 326 require.Equal(t, expectedKVPair3, segments[3]) 327 require.Equal(t, expectedDeliverTxRes2Bytes, segments[4]) 328 } 329 330 func testListenEndBlock(t *testing.T) { 331 t.Helper() 332 expectedEndBlockReqBytes, err := testMarshaller.Marshal(&testEndBlockReq) 333 require.Nil(t, err) 334 expectedEndBlockResBytes, err := testMarshaller.Marshal(&testEndBlockRes) 335 require.Nil(t, err) 336 337 // write state changes 338 err = testListener1.OnWrite(mockStoreKey1, mockKey1, mockValue1, false) 339 require.NoError(t, err) 340 err = testListener2.OnWrite(mockStoreKey1, mockKey2, mockValue2, false) 341 require.NoError(t, err) 342 err = testListener1.OnWrite(mockStoreKey2, mockKey3, mockValue3, false) 343 require.NoError(t, err) 344 345 // expected KV pairs 346 expectedKVPair1, err := testMarshaller.Marshal(&types.StoreKVPair{ 347 StoreKey: mockStoreKey1.Name(), 348 Key: mockKey1, 349 Value: mockValue1, 350 Delete: false, 351 }) 352 require.Nil(t, err) 353 expectedKVPair2, err := testMarshaller.Marshal(&types.StoreKVPair{ 354 StoreKey: mockStoreKey1.Name(), 355 Key: mockKey2, 356 Value: mockValue2, 357 Delete: false, 358 }) 359 require.Nil(t, err) 360 expectedKVPair3, err := testMarshaller.Marshal(&types.StoreKVPair{ 361 StoreKey: mockStoreKey2.Name(), 362 Key: mockKey3, 363 Value: mockValue3, 364 Delete: false, 365 }) 366 require.Nil(t, err) 367 368 // send the ABCI messages 369 err = testStreamingService.ListenEndBlock(emptyContext, testEndBlockReq, testEndBlockRes) 370 require.Nil(t, err) 371 372 // load the file, checking that it was created with the expected name 373 fileName := fmt.Sprintf("%s-block-%d-end", testPrefix, testEndBlockReq.Height) 374 fileBytes, err := readInFile(fileName) 375 require.Nil(t, err) 376 377 // segment the file into the separate gRPC messages and check the correctness of each 378 segments, err := segmentBytes(fileBytes) 379 require.Nil(t, err) 380 require.Equal(t, 5, len(segments)) 381 require.Equal(t, expectedEndBlockReqBytes, segments[0]) 382 require.Equal(t, expectedKVPair1, segments[1]) 383 require.Equal(t, expectedKVPair2, segments[2]) 384 require.Equal(t, expectedKVPair3, segments[3]) 385 require.Equal(t, expectedEndBlockResBytes, segments[4]) 386 } 387 388 func readInFile(name string) ([]byte, error) { 389 path := filepath.Join(testDir, name) 390 return os.ReadFile(path) 391 } 392 393 // Returns all of the protobuf messages contained in the byte array as an array of byte arrays 394 // The messages have their length prefix removed 395 func segmentBytes(bz []byte) ([][]byte, error) { 396 var err error 397 segments := make([][]byte, 0) 398 for len(bz) > 0 { 399 var segment []byte 400 segment, bz, err = getHeadSegment(bz) 401 if err != nil { 402 return nil, err 403 } 404 segments = append(segments, segment) 405 } 406 return segments, nil 407 } 408 409 // Returns the bytes for the leading protobuf object in the byte array (removing the length prefix) and returns the remainder of the byte array 410 func getHeadSegment(bz []byte) ([]byte, []byte, error) { 411 size, prefixSize := binary.Uvarint(bz) 412 if prefixSize < 0 { 413 return nil, nil, fmt.Errorf("invalid number of bytes read from length-prefixed encoding: %d", prefixSize) 414 } 415 if size > uint64(len(bz)-prefixSize) { 416 return nil, nil, fmt.Errorf("not enough bytes to read; want: %v, got: %v", size, len(bz)-prefixSize) 417 } 418 return bz[prefixSize:(uint64(prefixSize) + size)], bz[uint64(prefixSize)+size:], nil 419 }