github.com/koko1123/flow-go-1@v0.29.6/module/state_synchronization/requester/jobs/execution_data_reader_test.go (about) 1 package jobs 2 3 import ( 4 "context" 5 "errors" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/mock" 12 "github.com/stretchr/testify/require" 13 "github.com/stretchr/testify/suite" 14 15 "github.com/koko1123/flow-go-1/model/flow" 16 "github.com/koko1123/flow-go-1/module/executiondatasync/execution_data" 17 exedatamock "github.com/koko1123/flow-go-1/module/executiondatasync/execution_data/mock" 18 "github.com/koko1123/flow-go-1/module/irrecoverable" 19 synctest "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest" 20 "github.com/koko1123/flow-go-1/storage" 21 storagemock "github.com/koko1123/flow-go-1/storage/mock" 22 "github.com/koko1123/flow-go-1/utils/unittest" 23 ) 24 25 type ExecutionDataReaderSuite struct { 26 suite.Suite 27 28 reader *ExecutionDataReader 29 downloader *exedatamock.Downloader 30 headers *storagemock.Headers 31 results *storagemock.ExecutionResults 32 seals *storagemock.Seals 33 fetchTimeout time.Duration 34 35 executionDataID flow.Identifier 36 executionData *execution_data.BlockExecutionData 37 block *flow.Block 38 blocksByHeight map[uint64]*flow.Block 39 40 highestAvailableHeight func() uint64 41 } 42 43 func TestExecutionDataReaderSuite(t *testing.T) { 44 t.Parallel() 45 rand.Seed(time.Now().UnixMilli()) 46 suite.Run(t, new(ExecutionDataReaderSuite)) 47 } 48 49 func (suite *ExecutionDataReaderSuite) SetupTest() { 50 suite.fetchTimeout = time.Second 51 suite.executionDataID = unittest.IdentifierFixture() 52 53 parent := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(1)) 54 suite.block = unittest.BlockWithParentFixture(parent) 55 suite.blocksByHeight = map[uint64]*flow.Block{ 56 suite.block.Header.Height: suite.block, 57 } 58 59 suite.executionData = synctest.ExecutionDataFixture(suite.block.ID()) 60 61 suite.highestAvailableHeight = func() uint64 { return suite.block.Header.Height + 1 } 62 63 suite.reset() 64 } 65 66 func (suite *ExecutionDataReaderSuite) reset() { 67 result := unittest.ExecutionResultFixture( 68 unittest.WithBlock(suite.block), 69 unittest.WithExecutionDataID(suite.executionDataID), 70 ) 71 72 seal := unittest.Seal.Fixture( 73 unittest.Seal.WithBlockID(suite.block.ID()), 74 unittest.Seal.WithResult(result), 75 ) 76 77 suite.headers = synctest.MockBlockHeaderStorage(synctest.WithByHeight(suite.blocksByHeight)) 78 suite.results = synctest.MockResultsStorage( 79 synctest.WithResultByID(map[flow.Identifier]*flow.ExecutionResult{ 80 result.ID(): result, 81 }), 82 ) 83 suite.seals = synctest.MockSealsStorage( 84 synctest.WithSealsByBlockID(map[flow.Identifier]*flow.Seal{ 85 suite.block.ID(): seal, 86 }), 87 ) 88 89 suite.downloader = new(exedatamock.Downloader) 90 suite.reader = NewExecutionDataReader( 91 suite.downloader, 92 suite.headers, 93 suite.results, 94 suite.seals, 95 suite.fetchTimeout, 96 func() uint64 { 97 return suite.highestAvailableHeight() 98 }, 99 ) 100 } 101 102 func (suite *ExecutionDataReaderSuite) TestAtIndex() { 103 setExecutionDataGet := func(executionData *execution_data.BlockExecutionData, err error) { 104 suite.downloader.On("Download", mock.Anything, suite.executionDataID).Return( 105 func(ctx context.Context, id flow.Identifier) *execution_data.BlockExecutionData { 106 return executionData 107 }, 108 func(ctx context.Context, id flow.Identifier) error { 109 return err 110 }, 111 ) 112 } 113 114 suite.Run("returns not found when not initialized", func() { 115 // runTest not called, so context is never added 116 job, err := suite.reader.AtIndex(1) 117 assert.Nil(suite.T(), job, "job should be nil") 118 assert.Error(suite.T(), err, "error should be returned") 119 }) 120 121 suite.Run("returns not found when index out of range", func() { 122 suite.reset() 123 suite.runTest(func() { 124 job, err := suite.reader.AtIndex(suite.highestAvailableHeight() + 1) 125 assert.Nil(suite.T(), job, "job should be nil") 126 assert.Equal(suite.T(), storage.ErrNotFound, err, "expected not found error") 127 }) 128 }) 129 130 suite.Run("returns successfully", func() { 131 suite.reset() 132 suite.runTest(func() { 133 ed := synctest.ExecutionDataFixture(unittest.IdentifierFixture()) 134 setExecutionDataGet(ed, nil) 135 136 job, err := suite.reader.AtIndex(suite.block.Header.Height) 137 require.NoError(suite.T(), err) 138 139 entry, err := JobToBlockEntry(job) 140 assert.NoError(suite.T(), err) 141 142 assert.Equal(suite.T(), entry.ExecutionData, ed) 143 }) 144 }) 145 146 suite.Run("returns error from ExecutionDataService Get", func() { 147 suite.reset() 148 suite.runTest(func() { 149 // return an error while getting the execution data 150 expectedErr := errors.New("expected error: get failed") 151 setExecutionDataGet(nil, expectedErr) 152 153 job, err := suite.reader.AtIndex(suite.block.Header.Height) 154 assert.Nil(suite.T(), job, "job should be nil") 155 assert.ErrorIs(suite.T(), err, expectedErr) 156 }) 157 }) 158 159 suite.Run("returns error getting header", func() { 160 suite.reset() 161 suite.runTest(func() { 162 // search for an index that doesn't have a header in storage 163 job, err := suite.reader.AtIndex(suite.block.Header.Height + 1) 164 assert.Nil(suite.T(), job, "job should be nil") 165 assert.ErrorIs(suite.T(), err, storage.ErrNotFound) 166 }) 167 }) 168 169 suite.Run("returns error getting execution result", func() { 170 suite.reset() 171 suite.runTest(func() { 172 // add a new block without an execution result 173 newBlock := unittest.BlockWithParentFixture(suite.block.Header) 174 suite.blocksByHeight[newBlock.Header.Height] = newBlock 175 176 job, err := suite.reader.AtIndex(newBlock.Header.Height) 177 assert.Nil(suite.T(), job, "job should be nil") 178 assert.ErrorIs(suite.T(), err, storage.ErrNotFound) 179 }) 180 }) 181 } 182 183 func (suite *ExecutionDataReaderSuite) TestHead() { 184 suite.runTest(func() { 185 expectedIndex := uint64(15) 186 suite.highestAvailableHeight = func() uint64 { 187 return expectedIndex 188 } 189 index, err := suite.reader.Head() 190 assert.NoError(suite.T(), err) 191 assert.Equal(suite.T(), expectedIndex, index) 192 }) 193 } 194 195 func (suite *ExecutionDataReaderSuite) runTest(fn func()) { 196 ctx, cancel := context.WithCancel(context.Background()) 197 defer cancel() 198 199 signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) 200 201 suite.reader.AddContext(signalerCtx) 202 203 fn() 204 }