github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/jobqueue/sealed_header_reader_test.go (about)

     1  package jobqueue_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/dgraph-io/badger/v2"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/module/jobqueue"
    12  	synctest "github.com/onflow/flow-go/module/state_synchronization/requester/unittest"
    13  	"github.com/onflow/flow-go/storage"
    14  	"github.com/onflow/flow-go/utils/unittest"
    15  )
    16  
    17  // TestSealedBlockHeaderReader evaluates that block reader correctly reads stored finalized blocks from the blocks storage and
    18  // protocol state.
    19  func TestSealedBlockHeaderReader(t *testing.T) {
    20  	RunWithReader(t, 10, func(reader *jobqueue.SealedBlockHeaderReader, blocks []*flow.Block) {
    21  		// the last block seals its parent
    22  		lastSealedBlock := blocks[len(blocks)-2]
    23  
    24  		// head of the reader is the last sealed block
    25  		head, err := reader.Head()
    26  		assert.NoError(t, err)
    27  		assert.Equal(t, lastSealedBlock.Header.Height, head, "head does not match last sealed block")
    28  
    29  		// retrieved blocks from block reader should be the same as the original blocks stored in it.
    30  		// all except the last block should be sealed
    31  		lastIndex := len(blocks)
    32  		for _, expected := range blocks[:lastIndex-1] {
    33  			index := expected.Header.Height
    34  			job, err := reader.AtIndex(index)
    35  			assert.NoError(t, err)
    36  
    37  			retrieved, err := jobqueue.JobToBlockHeader(job)
    38  			assert.NoError(t, err)
    39  			assert.Equal(t, expected.ID(), retrieved.ID())
    40  		}
    41  
    42  		// ensure the last block returns a NotFound error
    43  		job, err := reader.AtIndex(uint64(lastIndex))
    44  		assert.Nil(t, job)
    45  		assert.ErrorIs(t, err, storage.ErrNotFound)
    46  	})
    47  }
    48  
    49  // RunWithReader is a test helper that sets up a block reader.
    50  // It also provides a chain of specified number of finalized blocks ready to read by block reader, i.e., the protocol state is extended with the
    51  // chain of blocks and the blocks are stored in blocks storage.
    52  func RunWithReader(
    53  	t *testing.T,
    54  	blockCount int,
    55  	withBlockReader func(*jobqueue.SealedBlockHeaderReader, []*flow.Block),
    56  ) {
    57  	require.Equal(t, blockCount%2, 0, "block count for this test should be even")
    58  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
    59  
    60  		blocks := make([]*flow.Block, blockCount)
    61  		blocksByHeight := make(map[uint64]*flow.Block, blockCount)
    62  
    63  		var seals []*flow.Header
    64  		parent := unittest.GenesisFixture().Header
    65  		for i := 0; i < blockCount; i++ {
    66  			seals = []*flow.Header{parent}
    67  			height := uint64(i) + 1
    68  
    69  			blocks[i] = unittest.BlockWithParentAndSeals(parent, seals)
    70  			blocksByHeight[height] = blocks[i]
    71  
    72  			parent = blocks[i].Header
    73  		}
    74  
    75  		snapshot := synctest.MockProtocolStateSnapshot(synctest.WithHead(seals[0]))
    76  		state := synctest.MockProtocolState(synctest.WithSealedSnapshot(snapshot))
    77  		headerStorage := synctest.MockBlockHeaderStorage(synctest.WithByHeight(blocksByHeight))
    78  
    79  		reader := jobqueue.NewSealedBlockHeaderReader(state, headerStorage)
    80  
    81  		withBlockReader(reader, blocks)
    82  	})
    83  }