Skip to content

Commit 7168168

Browse files
Use generics in submitter test
1 parent a9cb544 commit 7168168

4 files changed

Lines changed: 422 additions & 291 deletions

File tree

‎block/pending_base_test.go‎

Lines changed: 179 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -12,136 +12,197 @@ import (
1212
"github.com/stretchr/testify/mock"
1313
"github.com/stretchr/testify/require"
1414

15+
"github.com/rollkit/rollkit/pkg/store"
1516
mocksStore "github.com/rollkit/rollkit/test/mocks"
1617
"github.com/rollkit/rollkit/types"
1718
)
1819

19-
// TestPendingBase_InitAndGetLastSubmittedDataHeight verifies that pendingBase initializes correctly
20-
// and returns the correct last submitted height when no metadata is present.
21-
func TestPendingBase_InitAndGetLastSubmittedDataHeight(t *testing.T) {
22-
mockStore := mocksStore.NewStore(t)
23-
logger := log.NewNopLogger()
24-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return(nil, ds.ErrNotFound).Once()
25-
pb, err := newPendingBase(mockStore, logger, LastSubmittedDataHeightKey, fetchData)
26-
assert.NoError(t, err)
27-
assert.NotNil(t, pb)
28-
assert.Equal(t, uint64(0), pb.lastHeight.Load())
20+
// --- Generic test case struct and helpers for pendingBase tests ---
21+
type pendingBaseTestCase[T any] struct {
22+
name string
23+
key string
24+
fetch func(ctx context.Context, store store.Store, height uint64) (T, error)
25+
makeItem func(height uint64) T
26+
// For GetBlockData, returns (header, data, error) for a given height
27+
mockGetBlockData func(height uint64) (any, any, error)
2928
}
3029

31-
// TestPendingBase_GetPending_AllCases checks pendingBase.getPending for no data, all data submitted,
32-
// some pending data, and error cases.
33-
func TestPendingBase_GetPending_AllCases(t *testing.T) {
34-
mockStore := mocksStore.NewStore(t)
35-
logger := log.NewNopLogger()
36-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return(nil, ds.ErrNotFound).Once()
37-
pb, err := newPendingBase(mockStore, logger, LastSubmittedDataHeightKey, fetchData)
38-
require.NoError(t, err)
39-
40-
ctx := context.Background()
41-
42-
// Case: no data
43-
mockStore.On("Height", ctx).Return(uint64(0), nil).Once()
44-
pending, err := pb.getPending(ctx)
45-
assert.NoError(t, err)
46-
assert.Nil(t, pending)
47-
48-
// Case: all data submitted
49-
pb.lastHeight.Store(5)
50-
mockStore.On("Height", ctx).Return(uint64(5), nil).Once()
51-
pending, err = pb.getPending(ctx)
52-
assert.NoError(t, err)
53-
assert.Nil(t, pending)
54-
55-
// Case: some pending data
56-
pb.lastHeight.Store(2)
57-
mockStore.On("Height", ctx).Return(uint64(4), nil).Once()
58-
for i := uint64(3); i <= 4; i++ {
59-
data := &types.Data{Metadata: &types.Metadata{Height: i}}
60-
mockStore.On("GetBlockData", ctx, i).Return(nil, data, nil).Once()
61-
}
62-
pending, err = pb.getPending(ctx)
63-
assert.NoError(t, err)
64-
assert.Len(t, pending, 2)
65-
assert.Equal(t, uint64(3), pending[0].Height())
66-
assert.Equal(t, uint64(4), pending[1].Height())
67-
68-
// Case: error in store
69-
pb.lastHeight.Store(4)
70-
mockStore.On("Height", ctx).Return(uint64(5), nil).Once()
71-
mockStore.On("GetBlockData", ctx, uint64(5)).Return(nil, nil, errors.New("err")).Once()
72-
pending, err = pb.getPending(ctx)
73-
assert.Error(t, err)
74-
assert.Empty(t, pending)
30+
func runPendingBase_InitAndGetLastSubmittedHeight[T any](t *testing.T, tc pendingBaseTestCase[T]) {
31+
t.Run(tc.name+"/InitAndGetLastSubmittedHeight", func(t *testing.T) {
32+
mockStore := mocksStore.NewStore(t)
33+
logger := log.NewNopLogger()
34+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return(nil, ds.ErrNotFound).Once()
35+
pb, err := newPendingBase(mockStore, logger, tc.key, tc.fetch)
36+
assert.NoError(t, err)
37+
assert.NotNil(t, pb)
38+
assert.Equal(t, uint64(0), pb.lastHeight.Load())
39+
})
40+
}
41+
42+
func runPendingBase_GetPending_AllCases[T any](t *testing.T, tc pendingBaseTestCase[T]) {
43+
t.Run(tc.name+"/GetPending_AllCases", func(t *testing.T) {
44+
mockStore := mocksStore.NewStore(t)
45+
logger := log.NewNopLogger()
46+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return(nil, ds.ErrNotFound).Once()
47+
pb, err := newPendingBase(mockStore, logger, tc.key, tc.fetch)
48+
require.NoError(t, err)
49+
ctx := context.Background()
50+
51+
// Case: no items
52+
mockStore.On("Height", ctx).Return(uint64(0), nil).Once()
53+
pending, err := pb.getPending(ctx)
54+
assert.NoError(t, err)
55+
assert.Nil(t, pending)
56+
57+
// Case: all items submitted
58+
pb.lastHeight.Store(5)
59+
mockStore.On("Height", ctx).Return(uint64(5), nil).Once()
60+
pending, err = pb.getPending(ctx)
61+
assert.NoError(t, err)
62+
assert.Nil(t, pending)
63+
64+
// Case: some pending items
65+
pb.lastHeight.Store(2)
66+
mockStore.On("Height", ctx).Return(uint64(4), nil).Once()
67+
for i := uint64(3); i <= 4; i++ {
68+
ret0, ret1, retErr := tc.mockGetBlockData(i)
69+
mockStore.On("GetBlockData", ctx, i).Return(ret0, ret1, retErr).Once()
70+
}
71+
pending, err = pb.getPending(ctx)
72+
assert.NoError(t, err)
73+
assert.Len(t, pending, 2)
74+
// Use reflection to call Height() for both types
75+
getHeight := func(item any) uint64 {
76+
return item.(interface{ Height() uint64 }).Height()
77+
}
78+
assert.Equal(t, uint64(3), getHeight(pending[0]))
79+
assert.Equal(t, uint64(4), getHeight(pending[1]))
80+
81+
// Case: error in store
82+
pb.lastHeight.Store(4)
83+
mockStore.On("Height", ctx).Return(uint64(5), nil).Once()
84+
// For error case, always return error for height 5
85+
mockStore.On("GetBlockData", ctx, uint64(5)).Return(nil, nil, errors.New("err")).Once()
86+
pending, err = pb.getPending(ctx)
87+
assert.Error(t, err)
88+
assert.Empty(t, pending)
89+
})
90+
}
91+
92+
func runPendingBase_isEmpty_numPending[T any](t *testing.T, tc pendingBaseTestCase[T]) {
93+
t.Run(tc.name+"/isEmpty_numPending", func(t *testing.T) {
94+
mockStore := mocksStore.NewStore(t)
95+
logger := log.NewNopLogger()
96+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return(nil, ds.ErrNotFound).Once()
97+
pb, err := newPendingBase(mockStore, logger, tc.key, tc.fetch)
98+
require.NoError(t, err)
99+
100+
// isEmpty true
101+
pb.lastHeight.Store(10)
102+
mockStore.On("Height", mock.Anything).Return(uint64(10), nil).Once()
103+
assert.True(t, pb.isEmpty())
104+
105+
// isEmpty false
106+
pb.lastHeight.Store(5)
107+
mockStore.On("Height", mock.Anything).Return(uint64(10), nil).Once()
108+
assert.False(t, pb.isEmpty())
109+
110+
// numPending
111+
pb.lastHeight.Store(3)
112+
mockStore.On("Height", mock.Anything).Return(uint64(7), nil).Once()
113+
assert.Equal(t, uint64(4), pb.numPending())
114+
})
75115
}
76116

77-
// TestPendingBase_isEmpty_numPending verifies isEmpty and numPending methods for correct behavior
78-
// when there are no pending items, some pending items, and all items submitted.
79-
func TestPendingBase_isEmpty_numPending(t *testing.T) {
80-
mockStore := mocksStore.NewStore(t)
81-
logger := log.NewNopLogger()
82-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return(nil, ds.ErrNotFound).Once()
83-
pb, err := newPendingBase(mockStore, logger, LastSubmittedDataHeightKey, fetchData)
84-
require.NoError(t, err)
85-
86-
// isEmpty true
87-
pb.lastHeight.Store(10)
88-
mockStore.On("Height", mock.Anything).Return(uint64(10), nil).Once()
89-
assert.True(t, pb.isEmpty())
90-
91-
// isEmpty false
92-
pb.lastHeight.Store(5)
93-
mockStore.On("Height", mock.Anything).Return(uint64(10), nil).Once()
94-
assert.False(t, pb.isEmpty())
95-
96-
// numPending
97-
pb.lastHeight.Store(3)
98-
mockStore.On("Height", mock.Anything).Return(uint64(7), nil).Once()
99-
assert.Equal(t, uint64(4), pb.numPending())
117+
func runPendingBase_setLastSubmittedHeight[T any](t *testing.T, tc pendingBaseTestCase[T]) {
118+
t.Run(tc.name+"/setLastSubmittedHeight", func(t *testing.T) {
119+
mockStore := mocksStore.NewStore(t)
120+
logger := log.NewNopLogger()
121+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return(nil, ds.ErrNotFound).Once()
122+
pb, err := newPendingBase(mockStore, logger, tc.key, tc.fetch)
123+
require.NoError(t, err)
124+
125+
ctx := context.Background()
126+
pb.lastHeight.Store(2)
127+
// Should update
128+
mockStore.On("SetMetadata", ctx, tc.key, mock.Anything).Return(nil).Once()
129+
pb.setLastSubmittedHeight(ctx, 5)
130+
assert.Equal(t, uint64(5), pb.lastHeight.Load())
131+
132+
// Should not update (new <= old)
133+
pb.lastHeight.Store(5)
134+
pb.setLastSubmittedHeight(ctx, 4)
135+
assert.Equal(t, uint64(5), pb.lastHeight.Load())
136+
})
100137
}
101138

102-
// TestPendingBase_setLastSubmittedHeight checks that setLastSubmittedHeight updates the last height
103-
// and persists it only when the new height is greater than the current one.
104-
func TestPendingBase_setLastSubmittedHeight(t *testing.T) {
105-
mockStore := mocksStore.NewStore(t)
106-
logger := log.NewNopLogger()
107-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return(nil, ds.ErrNotFound).Once()
108-
pb, err := newPendingBase(mockStore, logger, LastSubmittedDataHeightKey, fetchData)
109-
require.NoError(t, err)
110-
111-
ctx := context.Background()
112-
pb.lastHeight.Store(2)
113-
// Should update
114-
mockStore.On("SetMetadata", ctx, LastSubmittedDataHeightKey, mock.Anything).Return(nil).Once()
115-
pb.setLastSubmittedHeight(ctx, 5)
116-
assert.Equal(t, uint64(5), pb.lastHeight.Load())
117-
118-
// Should not update (new <= old)
119-
pb.lastHeight.Store(5)
120-
pb.setLastSubmittedHeight(ctx, 4)
121-
assert.Equal(t, uint64(5), pb.lastHeight.Load())
139+
func runPendingBase_init_with_existing_metadata[T any](t *testing.T, tc pendingBaseTestCase[T]) {
140+
t.Run(tc.name+"/init_with_existing_metadata", func(t *testing.T) {
141+
mockStore := mocksStore.NewStore(t)
142+
logger := log.NewNopLogger()
143+
val := make([]byte, 8)
144+
binary.LittleEndian.PutUint64(val, 7)
145+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return(val, nil).Once()
146+
pb := &pendingBase[T]{store: mockStore, logger: logger, metaKey: tc.key, fetch: tc.fetch}
147+
err := pb.init()
148+
assert.NoError(t, err)
149+
assert.Equal(t, uint64(7), pb.lastHeight.Load())
150+
})
122151
}
123152

124-
// TestPendingBase_init_with_existing_metadata ensures that pendingBase initializes its last height
125-
// from existing metadata in the store.
126-
func TestPendingBase_init_with_existing_metadata(t *testing.T) {
127-
mockStore := mocksStore.NewStore(t)
128-
logger := log.NewNopLogger()
129-
val := make([]byte, 8)
130-
binary.LittleEndian.PutUint64(val, 7)
131-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return(val, nil).Once()
132-
pb := &pendingBase[*types.Data]{store: mockStore, logger: logger, metaKey: LastSubmittedDataHeightKey, fetch: fetchData}
133-
err := pb.init()
134-
assert.NoError(t, err)
135-
assert.Equal(t, uint64(7), pb.lastHeight.Load())
153+
func runPendingBase_init_invalid_length[T any](t *testing.T, tc pendingBaseTestCase[T]) {
154+
t.Run(tc.name+"/init_invalid_length", func(t *testing.T) {
155+
mockStore := mocksStore.NewStore(t)
156+
logger := log.NewNopLogger()
157+
mockStore.On("GetMetadata", mock.Anything, tc.key).Return([]byte{1, 2}, nil).Once()
158+
pb := &pendingBase[T]{store: mockStore, logger: logger, metaKey: tc.key, fetch: tc.fetch}
159+
err := pb.init()
160+
assert.Error(t, err)
161+
})
136162
}
137163

138-
// TestPendingBase_init_invalid_length checks that pendingBase.init returns an error
139-
// if the stored metadata for last height is not the expected length.
140-
func TestPendingBase_init_invalid_length(t *testing.T) {
141-
mockStore := mocksStore.NewStore(t)
142-
logger := log.NewNopLogger()
143-
mockStore.On("GetMetadata", mock.Anything, LastSubmittedDataHeightKey).Return([]byte{1, 2}, nil).Once()
144-
pb := &pendingBase[*types.Data]{store: mockStore, logger: logger, metaKey: LastSubmittedDataHeightKey, fetch: fetchData}
145-
err := pb.init()
146-
assert.Error(t, err)
164+
func TestPendingBase_Generic(t *testing.T) {
165+
dataCase := pendingBaseTestCase[*types.Data]{
166+
name: "Data",
167+
key: LastSubmittedDataHeightKey,
168+
fetch: fetchData,
169+
makeItem: func(height uint64) *types.Data {
170+
return &types.Data{Metadata: &types.Metadata{Height: height}}
171+
},
172+
mockGetBlockData: func(height uint64) (any, any, error) {
173+
return nil, &types.Data{Metadata: &types.Metadata{Height: height}}, nil
174+
},
175+
}
176+
headerCase := pendingBaseTestCase[*types.SignedHeader]{
177+
name: "Header",
178+
key: LastSubmittedHeaderHeightKey,
179+
fetch: fetchSignedHeader,
180+
makeItem: func(height uint64) *types.SignedHeader {
181+
return &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{Height: height}}}
182+
},
183+
mockGetBlockData: func(height uint64) (any, any, error) {
184+
return &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{Height: height}}}, nil, nil
185+
},
186+
}
187+
188+
cases := []any{dataCase, headerCase}
189+
190+
for _, c := range cases {
191+
switch tc := c.(type) {
192+
case pendingBaseTestCase[*types.Data]:
193+
runPendingBase_InitAndGetLastSubmittedHeight(t, tc)
194+
runPendingBase_GetPending_AllCases(t, tc)
195+
runPendingBase_isEmpty_numPending(t, tc)
196+
runPendingBase_setLastSubmittedHeight(t, tc)
197+
runPendingBase_init_with_existing_metadata(t, tc)
198+
runPendingBase_init_invalid_length(t, tc)
199+
case pendingBaseTestCase[*types.SignedHeader]:
200+
runPendingBase_InitAndGetLastSubmittedHeight(t, tc)
201+
runPendingBase_GetPending_AllCases(t, tc)
202+
runPendingBase_isEmpty_numPending(t, tc)
203+
runPendingBase_setLastSubmittedHeight(t, tc)
204+
runPendingBase_init_with_existing_metadata(t, tc)
205+
runPendingBase_init_invalid_length(t, tc)
206+
}
207+
}
147208
}

‎block/retriever.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ func (m *Manager) handlePotentialData(ctx context.Context, bz []byte, daHeight u
167167
dataHashStr := signedData.Data.DACommitment().String()
168168
m.dataCache.SetDAIncluded(dataHashStr)
169169
m.sendNonBlockingSignalToDAIncluderCh()
170-
m.logger.Info("signed data marked as DA included", "dataHash", dataHashStr, "daHeight", daHeight, "height", signedData.Data.Height())
170+
m.logger.Info("signed data marked as DA included", "dataHash", dataHashStr, "daHeight", daHeight, "height", signedData.Height())
171171
if !m.dataCache.IsSeen(dataHashStr) {
172172
select {
173173
case <-ctx.Done():

‎block/submitter.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func submitToDA(
185185
}
186186

187187
if !submittedAll {
188-
return fmt.Errorf("failed to submit all items to DA layer, submitted %d items (%d left) after %d attempts", numSubmitted, remLen, attempt)
188+
return fmt.Errorf("failed to submit all %s(s) to DA layer, submitted %d items (%d left) after %d attempts", itemType, numSubmitted, remLen, attempt)
189189
}
190190
return nil
191191
}

0 commit comments

Comments
 (0)