github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/rpc/backend/backend_stream_block_headers_test.go (about) 1 package backend 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/mock" 9 "github.com/stretchr/testify/require" 10 "github.com/stretchr/testify/suite" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 14 "github.com/onflow/flow-go/engine/access/subscription" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/utils/unittest" 17 ) 18 19 type BackendBlockHeadersSuite struct { 20 BackendBlocksSuite 21 } 22 23 func TestBackendBlockHeadersSuite(t *testing.T) { 24 suite.Run(t, new(BackendBlockHeadersSuite)) 25 } 26 27 // SetupTest initializes the test suite with required dependencies. 28 func (s *BackendBlockHeadersSuite) SetupTest() { 29 s.BackendBlocksSuite.SetupTest() 30 } 31 32 // TestSubscribeBlockHeadersFromStartBlockID tests the SubscribeBlockHeadersFromStartBlockID method. 33 func (s *BackendBlockHeadersSuite) TestSubscribeBlockHeadersFromStartBlockID() { 34 s.blockTracker.On( 35 "GetStartHeightFromBlockID", 36 mock.AnythingOfType("flow.Identifier"), 37 ).Return(func(startBlockID flow.Identifier) (uint64, error) { 38 return s.blockTrackerReal.GetStartHeightFromBlockID(startBlockID) 39 }, nil) 40 41 call := func(ctx context.Context, startValue interface{}, blockStatus flow.BlockStatus) subscription.Subscription { 42 return s.backend.SubscribeBlockHeadersFromStartBlockID(ctx, startValue.(flow.Identifier), blockStatus) 43 } 44 45 s.subscribe(call, s.requireBlockHeaders, s.subscribeFromStartBlockIdTestCases()) 46 } 47 48 // TestSubscribeBlockHeadersFromStartHeight tests the SubscribeBlockHeadersFromStartHeight method. 49 func (s *BackendBlockHeadersSuite) TestSubscribeBlockHeadersFromStartHeight() { 50 s.blockTracker.On( 51 "GetStartHeightFromHeight", 52 mock.AnythingOfType("uint64"), 53 ).Return(func(startHeight uint64) (uint64, error) { 54 return s.blockTrackerReal.GetStartHeightFromHeight(startHeight) 55 }, nil) 56 57 call := func(ctx context.Context, startValue interface{}, blockStatus flow.BlockStatus) subscription.Subscription { 58 return s.backend.SubscribeBlockHeadersFromStartHeight(ctx, startValue.(uint64), blockStatus) 59 } 60 61 s.subscribe(call, s.requireBlockHeaders, s.subscribeFromStartHeightTestCases()) 62 } 63 64 // TestSubscribeBlockHeadersFromLatest tests the SubscribeBlockHeadersFromLatest method. 65 func (s *BackendBlockHeadersSuite) TestSubscribeBlockHeadersFromLatest() { 66 s.blockTracker.On( 67 "GetStartHeightFromLatest", 68 mock.Anything, 69 ).Return(func(ctx context.Context) (uint64, error) { 70 return s.blockTrackerReal.GetStartHeightFromLatest(ctx) 71 }, nil) 72 73 call := func(ctx context.Context, startValue interface{}, blockStatus flow.BlockStatus) subscription.Subscription { 74 return s.backend.SubscribeBlockHeadersFromLatest(ctx, blockStatus) 75 } 76 77 s.subscribe(call, s.requireBlockHeaders, s.subscribeFromLatestTestCases()) 78 } 79 80 // requireBlockHeaders ensures that the received block header information matches the expected data. 81 func (s *BackendBlockHeadersSuite) requireBlockHeaders(v interface{}, expectedBlock *flow.Block) { 82 actualHeader, ok := v.(*flow.Header) 83 require.True(s.T(), ok, "unexpected response type: %T", v) 84 85 s.Require().Equal(expectedBlock.Header.Height, actualHeader.Height) 86 s.Require().Equal(expectedBlock.Header.ID(), actualHeader.ID()) 87 s.Require().Equal(*expectedBlock.Header, *actualHeader) 88 } 89 90 // TestSubscribeBlockHeadersHandlesErrors tests error handling scenarios for the SubscribeBlockHeadersFromStartBlockID and SubscribeBlockHeadersFromStartHeight methods in the Backend. 91 // It ensures that the method correctly returns errors for various invalid input cases. 92 // 93 // Test Cases: 94 // 95 // 1. Returns error for unindexed start block id: 96 // - Tests that subscribing to block headers with an unindexed start block ID results in a NotFound error. 97 // 98 // 2. Returns error for start height before root height: 99 // - Validates that attempting to subscribe to block headers with a start height before the root height results in an InvalidArgument error. 100 // 101 // 3. Returns error for unindexed start height: 102 // - Tests that subscribing to block headers with an unindexed start height results in a NotFound error. 103 // 104 // Each test case checks for specific error conditions and ensures that the methods responds appropriately. 105 func (s *BackendBlockHeadersSuite) TestSubscribeBlockHeadersHandlesErrors() { 106 ctx, cancel := context.WithCancel(context.Background()) 107 defer cancel() 108 109 // mock block tracker for GetStartHeightFromBlockID 110 s.blockTracker.On( 111 "GetStartHeightFromBlockID", 112 mock.AnythingOfType("flow.Identifier"), 113 ).Return(func(startBlockID flow.Identifier) (uint64, error) { 114 return s.blockTrackerReal.GetStartHeightFromBlockID(startBlockID) 115 }, nil) 116 117 s.Run("returns error for unknown start block id is provided", func() { 118 subCtx, subCancel := context.WithCancel(ctx) 119 defer subCancel() 120 121 sub := s.backend.SubscribeBlockHeadersFromStartBlockID(subCtx, unittest.IdentifierFixture(), flow.BlockStatusFinalized) 122 assert.Equal(s.T(), codes.NotFound, status.Code(sub.Err()), "expected %s, got %v: %v", codes.NotFound, status.Code(sub.Err()).String(), sub.Err()) 123 }) 124 125 // mock block tracker for GetStartHeightFromHeight 126 s.blockTracker.On( 127 "GetStartHeightFromHeight", 128 mock.AnythingOfType("uint64"), 129 ).Return(func(startHeight uint64) (uint64, error) { 130 return s.blockTrackerReal.GetStartHeightFromHeight(startHeight) 131 }, nil) 132 133 s.Run("returns error if start height before root height", func() { 134 subCtx, subCancel := context.WithCancel(ctx) 135 defer subCancel() 136 137 sub := s.backend.SubscribeBlockHeadersFromStartHeight(subCtx, s.rootBlock.Header.Height-1, flow.BlockStatusFinalized) 138 assert.Equal(s.T(), codes.InvalidArgument, status.Code(sub.Err()), "expected %s, got %v: %v", codes.InvalidArgument, status.Code(sub.Err()).String(), sub.Err()) 139 }) 140 141 s.Run("returns error for unknown start height is provided", func() { 142 subCtx, subCancel := context.WithCancel(ctx) 143 defer subCancel() 144 145 sub := s.backend.SubscribeBlockHeadersFromStartHeight(subCtx, s.blocksArray[len(s.blocksArray)-1].Header.Height+10, flow.BlockStatusFinalized) 146 assert.Equal(s.T(), codes.NotFound, status.Code(sub.Err()), "expected %s, got %v: %v", codes.NotFound, status.Code(sub.Err()).String(), sub.Err()) 147 }) 148 }