@@ -3,10 +3,12 @@ package store_test
33import (
44 "testing"
55
6+ "github.com/jordanschalm/lockctx"
67 "github.com/stretchr/testify/require"
78
89 "github.com/onflow/flow-go/module/metrics"
910 "github.com/onflow/flow-go/storage"
11+ "github.com/onflow/flow-go/storage/operation"
1012 "github.com/onflow/flow-go/storage/operation/dbtest"
1113 "github.com/onflow/flow-go/storage/store"
1214 "github.com/onflow/flow-go/utils/unittest"
@@ -52,3 +54,107 @@ func TestBlockStoreAndRetrieve(t *testing.T) {
5254 require .Equal (t , * block , * receivedAfterRestart )
5355 })
5456}
57+
58+ func TestBlockIndexByHeightAndRetrieve (t * testing.T ) {
59+ dbtest .RunWithDB (t , func (t * testing.T , db storage.DB ) {
60+ lockManager := storage .NewTestingLockManager ()
61+ cacheMetrics := & metrics.NoopCollector {}
62+ blocks := store .InitAll (cacheMetrics , db ).Blocks
63+ block := unittest .FullBlockFixture ()
64+ prop := unittest .ProposalFromBlock (block )
65+
66+ // First store the block
67+ unittest .WithLock (t , lockManager , storage .LockInsertBlock , func (lctx lockctx.Context ) error {
68+ return db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
69+ return blocks .BatchStore (lctx , rw , prop )
70+ })
71+ })
72+
73+ // Now index the block by height (requires LockFinalizeBlock)
74+ unittest .WithLock (t , lockManager , storage .LockFinalizeBlock , func (lctx lockctx.Context ) error {
75+ return db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
76+ return operation .IndexFinalizedBlockByHeight (lctx , rw , block .Height , block .ID ())
77+ })
78+ })
79+
80+ // Verify we can retrieve the block by height
81+ retrievedByHeight , err := blocks .ByHeight (block .Height )
82+ require .NoError (t , err )
83+ require .Equal (t , * block , * retrievedByHeight )
84+
85+ // Verify we can retrieve the proposal by height
86+ retrievedProposalByHeight , err := blocks .ProposalByHeight (block .Height )
87+ require .NoError (t , err )
88+ require .Equal (t , * prop , * retrievedProposalByHeight )
89+
90+ // Test that indexing the same height again returns ErrAlreadyExists
91+ unittest .WithLock (t , lockManager , storage .LockFinalizeBlock , func (lctx lockctx.Context ) error {
92+ err := db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
93+ return operation .IndexFinalizedBlockByHeight (lctx , rw , block .Height , block .ID ())
94+ })
95+ require .ErrorIs (t , err , storage .ErrAlreadyExists )
96+ return nil
97+ })
98+
99+ // Test that retrieving by non-existent height returns ErrNotFound
100+ _ , err = blocks .ByHeight (block .Height + 1000 )
101+ require .ErrorIs (t , err , storage .ErrNotFound )
102+
103+ // Verify after a restart, the block indexed by height is still retrievable
104+ blocksAfterRestart := store .InitAll (cacheMetrics , db ).Blocks
105+ receivedAfterRestart , err := blocksAfterRestart .ByHeight (block .Height )
106+ require .NoError (t , err )
107+ require .Equal (t , * block , * receivedAfterRestart )
108+ })
109+ }
110+
111+ func TestBlockIndexByViewAndRetrieve (t * testing.T ) {
112+ dbtest .RunWithDB (t , func (t * testing.T , db storage.DB ) {
113+ lockManager := storage .NewTestingLockManager ()
114+ cacheMetrics := & metrics.NoopCollector {}
115+ blocks := store .InitAll (cacheMetrics , db ).Blocks
116+ block := unittest .FullBlockFixture ()
117+ prop := unittest .ProposalFromBlock (block )
118+
119+ // First store the block and index by view
120+ unittest .WithLock (t , lockManager , storage .LockInsertBlock , func (lctx lockctx.Context ) error {
121+ return db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
122+ err := blocks .BatchStore (lctx , rw , prop )
123+ if err != nil {
124+ return err
125+ }
126+ // Now index the block by view (requires LockInsertBlock)
127+ return operation .IndexCertifiedBlockByView (lctx , rw , block .View , block .ID ())
128+ })
129+ })
130+
131+ // Verify we can retrieve the block by view
132+ retrievedByView , err := blocks .ByView (block .View )
133+ require .NoError (t , err )
134+ require .Equal (t , * block , * retrievedByView )
135+
136+ // Verify we can retrieve the proposal by view
137+ retrievedProposalByView , err := blocks .ProposalByView (block .View )
138+ require .NoError (t , err )
139+ require .Equal (t , * prop , * retrievedProposalByView )
140+
141+ // Test that indexing the same view again returns ErrAlreadyExists
142+ unittest .WithLock (t , lockManager , storage .LockInsertBlock , func (lctx lockctx.Context ) error {
143+ err := db .WithReaderBatchWriter (func (rw storage.ReaderBatchWriter ) error {
144+ return operation .IndexCertifiedBlockByView (lctx , rw , block .View , block .ID ())
145+ })
146+ require .ErrorIs (t , err , storage .ErrAlreadyExists )
147+ return nil
148+ })
149+
150+ // Test that retrieving by non-existent view returns ErrNotFound
151+ _ , err = blocks .ByView (block .View + 1000 )
152+ require .ErrorIs (t , err , storage .ErrNotFound )
153+
154+ // Verify after a restart, the block indexed by view is still retrievable
155+ blocksAfterRestart := store .InitAll (cacheMetrics , db ).Blocks
156+ receivedAfterRestart , err := blocksAfterRestart .ByView (block .View )
157+ require .NoError (t , err )
158+ require .Equal (t , * block , * receivedAfterRestart )
159+ })
160+ }
0 commit comments