• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cinode / go / 15811194822

22 Jun 2025 09:59PM UTC coverage: 93.792% (+1.1%) from 92.74%
15811194822

Pull #50

github

byo
More test coverage for multisource
Pull Request #50: Test improvements

52 of 122 new or added lines in 3 files covered. (42.62%)

3 existing lines in 1 file now uncovered.

3339 of 3560 relevant lines covered (93.79%)

1.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.88
/pkg/datastore/interface_testsuite.go
1
/*
2
Copyright © 2025 Bartłomiej Święcki (byo)
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
        http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package datastore
18

19
import (
20
        "bytes"
21
        "context"
22
        "crypto/sha256"
23
        "errors"
24
        "fmt"
25
        "io"
26
        "sync"
27

28
        "github.com/cinode/go/pkg/blobtypes"
29
        "github.com/cinode/go/pkg/common"
30
        "github.com/cinode/go/pkg/datastore/testutils"
31
        "github.com/cinode/go/pkg/internal/blobtypes/dynamiclink"
32
        "github.com/stretchr/testify/suite"
33
)
34

35
type DatastoreTestSuite struct {
36
        suite.Suite
37

38
        // CreateDS is a function that creates a new datastore for each test.
39
        CreateDS func() (DS, error)
40

41
        // DS is the current datastore instance to test.
42
        DS DS
43
}
44

45
func (s *DatastoreTestSuite) SetupTest() {
1✔
46
        ds, err := s.CreateDS()
1✔
47
        s.Require().NoError(err)
1✔
48
        s.DS = ds
1✔
49
}
1✔
50

51
func (s *DatastoreTestSuite) TestOpenNonExisting() {
1✔
52
        for _, name := range testutils.EmptyBlobNamesOfAllTypes {
2✔
53
                s.Run(fmt.Sprint(name.Type()), func() {
2✔
54
                        r, err := s.DS.Open(context.Background(), name)
1✔
55
                        s.Require().ErrorIs(err, ErrNotFound)
1✔
56
                        s.Require().Nil(r)
1✔
57
                })
1✔
58
        }
59
}
60

61
func (s *DatastoreTestSuite) TestOpenInvalidBlobType() {
1✔
62
        bn, err := common.BlobNameFromHashAndType(sha256.New().Sum(nil), common.NewBlobType(0xFF))
1✔
63
        s.Require().NoError(err)
1✔
64

1✔
65
        r, err := s.DS.Open(context.Background(), bn)
1✔
66
        s.Require().ErrorIs(err, blobtypes.ErrUnknownBlobType)
1✔
67
        s.Require().Nil(r)
1✔
68

1✔
69
        err = s.DS.Update(context.Background(), bn, bytes.NewBuffer(nil))
1✔
70
        s.Require().ErrorIs(err, blobtypes.ErrUnknownBlobType)
1✔
71
}
1✔
72

73
func (s *DatastoreTestSuite) TestBlobValidationFailed() {
1✔
74
        for _, name := range testutils.EmptyBlobNamesOfAllTypes {
2✔
75
                s.Run(fmt.Sprint(name.Type()), func() {
2✔
76
                        err := s.DS.Update(context.Background(), name, bytes.NewReader([]byte("test")))
1✔
77
                        s.Require().ErrorIs(err, blobtypes.ErrValidationFailed)
1✔
78
                })
1✔
79
        }
80
}
81

82
func (s *DatastoreTestSuite) TestSaveSuccessfulStatic() {
1✔
83
        for _, b := range testutils.TestBlobs {
2✔
84

1✔
85
                exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
86
                s.Require().NoError(err)
1✔
87
                s.Require().False(exists)
1✔
88

1✔
89
                err = s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
90
                s.Require().NoError(err)
1✔
91

1✔
92
                exists, err = s.DS.Exists(context.Background(), b.Name)
1✔
93
                s.Require().NoError(err)
1✔
94
                s.Require().True(exists)
1✔
95

1✔
96
                // Overwrite with the same data must be fine
1✔
97
                err = s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
98
                s.Require().NoError(err)
1✔
99

1✔
100
                exists, err = s.DS.Exists(context.Background(), b.Name)
1✔
101
                s.Require().NoError(err)
1✔
102
                s.Require().True(exists)
1✔
103

1✔
104
                // Overwrite with wrong data must fail
1✔
105
                err = s.DS.Update(context.Background(), b.Name, bytes.NewReader(append([]byte{0x00}, b.Data...)))
1✔
106
                s.Require().ErrorIs(err, blobtypes.ErrValidationFailed)
1✔
107

1✔
108
                exists, err = s.DS.Exists(context.Background(), b.Name)
1✔
109
                s.Require().NoError(err)
1✔
110
                s.Require().True(exists)
1✔
111

1✔
112
                r, err := s.DS.Open(context.Background(), b.Name)
1✔
113
                s.Require().NoError(err)
1✔
114

1✔
115
                data, err := io.ReadAll(r)
1✔
116
                s.Require().NoError(err)
1✔
117
                s.Require().Equal(b.Data, data)
1✔
118

1✔
119
                err = r.Close()
1✔
120
                s.Require().NoError(err)
1✔
121
        }
1✔
122
}
123

124
func (s *DatastoreTestSuite) TestErrorWhileUpdating() {
1✔
125
        for i, b := range testutils.TestBlobs {
2✔
126
                s.Run(fmt.Sprint(i), func() {
2✔
127
                        errRet := errors.New("Test error")
1✔
128
                        err := s.DS.Update(context.Background(), b.Name, testutils.BReader(b.Data, func() error {
2✔
129
                                return errRet
1✔
130
                        }, nil))
1✔
131
                        s.Require().ErrorIs(err, errRet)
1✔
132

1✔
133
                        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
134
                        s.Require().NoError(err)
1✔
135
                        s.Require().False(exists)
1✔
136
                })
137
        }
138
}
139

140
func (s *DatastoreTestSuite) TestErrorWhileOverwriting() {
1✔
141
        for i, b := range testutils.TestBlobs {
2✔
142
                s.Run(fmt.Sprint(i), func() {
2✔
143

1✔
144
                        err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
145
                        s.Require().NoError(err)
1✔
146

1✔
147
                        errRet := errors.New("cancel")
1✔
148

1✔
149
                        err = s.DS.Update(context.Background(), b.Name, testutils.BReader(b.Data, func() error {
2✔
150
                                exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
151
                                s.Require().NoError(err)
1✔
152
                                s.Require().True(exists)
1✔
153

1✔
154
                                return errRet
1✔
155
                        }, nil))
1✔
156

157
                        s.Require().ErrorIs(err, errRet)
1✔
158

1✔
159
                        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
160
                        s.Require().NoError(err)
1✔
161
                        s.Require().True(exists)
1✔
162

1✔
163
                        r, err := s.DS.Open(context.Background(), b.Name)
1✔
164
                        s.Require().NoError(err)
1✔
165

1✔
166
                        data, err := io.ReadAll(r)
1✔
167
                        s.Require().NoError(err)
1✔
168
                        s.Require().Equal(b.Data, data)
1✔
169

1✔
170
                        err = r.Close()
1✔
171
                        s.Require().NoError(err)
1✔
172
                })
173
        }
174
}
175

176
func (s *DatastoreTestSuite) TestDeleteNonExisting() {
1✔
177
        b := testutils.TestBlobs[0]
1✔
178

1✔
179
        err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
180
        s.Require().NoError(err)
1✔
181

1✔
182
        err = s.DS.Delete(context.Background(), testutils.TestBlobs[1].Name)
1✔
183
        s.Require().ErrorIs(err, ErrNotFound)
1✔
184

1✔
185
        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
186
        s.Require().NoError(err)
1✔
187
        s.Require().True(exists)
1✔
188
}
1✔
189

190
func (s *DatastoreTestSuite) TestDeleteExisting() {
1✔
191

1✔
192
        b := testutils.TestBlobs[0]
1✔
193
        err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
194
        s.Require().NoError(err)
1✔
195

1✔
196
        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
197
        s.Require().NoError(err)
1✔
198
        s.Require().True(exists)
1✔
199

1✔
200
        err = s.DS.Delete(context.Background(), b.Name)
1✔
201
        s.Require().NoError(err)
1✔
202

1✔
203
        exists, err = s.DS.Exists(context.Background(), b.Name)
1✔
204
        s.Require().NoError(err)
1✔
205
        s.Require().False(exists)
1✔
206

1✔
207
        r, err := s.DS.Open(context.Background(), b.Name)
1✔
208
        s.Require().ErrorIs(err, ErrNotFound)
1✔
209
        s.Require().Nil(r)
1✔
210
}
1✔
211

212
func (s *DatastoreTestSuite) TestGetKind() {
1✔
213
        k := s.DS.Kind()
1✔
214
        s.Require().NotEmpty(k)
1✔
215
}
1✔
216

217
func (s *DatastoreTestSuite) TestAddress() {
1✔
218
        address := s.DS.Address()
1✔
219
        s.Require().Regexp(`^[a-zA-Z0-9_-]+://`, address)
1✔
220
}
1✔
221

222
func (s *DatastoreTestSuite) TestSimultaneousReads() {
1✔
223
        const threadCnt = 10
1✔
224
        const readCnt = 200
1✔
225

1✔
226
        // Prepare data
1✔
227
        for _, b := range testutils.TestBlobs {
2✔
228
                err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
229
                s.Require().NoError(err)
1✔
230
        }
1✔
231

232
        wg := sync.WaitGroup{}
1✔
233
        wg.Add(threadCnt)
1✔
234

1✔
235
        for i := 0; i < threadCnt; i++ {
2✔
236
                go func(i int) {
2✔
237
                        defer wg.Done()
1✔
238
                        for n := 0; n < readCnt; n++ {
2✔
239
                                b := testutils.TestBlobs[(i+n)%len(testutils.TestBlobs)]
1✔
240

1✔
241
                                r, err := s.DS.Open(context.Background(), b.Name)
1✔
242
                                s.Require().NoError(err)
1✔
243

1✔
244
                                data, err := io.ReadAll(r)
1✔
245
                                s.Require().NoError(err)
1✔
246
                                s.Require().Equal(b.Data, data)
1✔
247

1✔
248
                                err = r.Close()
1✔
249
                                s.Require().NoError(err)
1✔
250
                        }
1✔
251
                }(i)
252
        }
253

254
        wg.Wait()
1✔
255
}
256

257
func (s *DatastoreTestSuite) TestSimultaneousUpdates() {
1✔
258
        const threadCnt = 3
1✔
259

1✔
260
        b := testutils.TestBlobs[0]
1✔
261

1✔
262
        wg := sync.WaitGroup{}
1✔
263
        wg.Add(threadCnt)
1✔
264

1✔
265
        for i := 0; i < threadCnt; i++ {
2✔
266
                go func(i int) {
2✔
267
                        defer wg.Done()
1✔
268

1✔
269
                        err := s.DS.Update(context.Background(), b.Name, bytes.NewReader(b.Data))
1✔
270
                        if errors.Is(err, ErrUploadInProgress) {
1✔
UNCOV
271
                                // TODO: We should be able to handle this case
×
UNCOV
272
                                return
×
UNCOV
273
                        }
×
274

275
                        s.Require().NoError(err)
1✔
276

1✔
277
                        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
278
                        s.Require().NoError(err)
1✔
279
                        s.Require().True(exists)
1✔
280
                }(i)
281
        }
282

283
        wg.Wait()
1✔
284

1✔
285
        exists, err := s.DS.Exists(context.Background(), b.Name)
1✔
286
        s.Require().NoError(err)
1✔
287
        s.Require().True(exists)
1✔
288

1✔
289
        r, err := s.DS.Open(context.Background(), b.Name)
1✔
290
        s.Require().NoError(err)
1✔
291

1✔
292
        data, err := io.ReadAll(r)
1✔
293
        s.Require().NoError(err)
1✔
294
        s.Require().Equal(b.Data, data)
1✔
295

1✔
296
        err = r.Close()
1✔
297
        s.Require().NoError(err)
1✔
298
}
299

300
func (s *DatastoreTestSuite) updateDynamicLink(num int) {
1✔
301
        err := s.DS.Update(
1✔
302
                context.Background(),
1✔
303
                testutils.DynamicLinkPropagationData[num].Name,
1✔
304
                bytes.NewReader(testutils.DynamicLinkPropagationData[num].Data),
1✔
305
        )
1✔
306
        s.Require().NoError(err)
1✔
307
}
1✔
308

309
func (s *DatastoreTestSuite) readDynamicLinkData() []byte {
1✔
310
        r, err := s.DS.Open(context.Background(), testutils.DynamicLinkPropagationData[0].Name)
1✔
311
        s.Require().NoError(err)
1✔
312

1✔
313
        dl, err := dynamiclink.FromPublicData(testutils.DynamicLinkPropagationData[0].Name, r)
1✔
314
        s.Require().NoError(err)
1✔
315

1✔
316
        elink, err := io.ReadAll(dl.GetEncryptedLinkReader())
1✔
317
        s.Require().NoError(err)
1✔
318

1✔
319
        err = r.Close()
1✔
320
        s.Require().NoError(err)
1✔
321

1✔
322
        return elink
1✔
323
}
1✔
324

325
func (s *DatastoreTestSuite) expectDynamicLinkData(num int) {
1✔
326
        s.Require().Equal(
1✔
327
                testutils.DynamicLinkPropagationData[num].Expected,
1✔
328
                s.readDynamicLinkData(),
1✔
329
        )
1✔
330
}
1✔
331

332
func (s *DatastoreTestSuite) TestDynamicLinkPropagation() {
1✔
333
        s.updateDynamicLink(0)
1✔
334
        s.expectDynamicLinkData(0)
1✔
335

1✔
336
        s.updateDynamicLink(1)
1✔
337
        s.expectDynamicLinkData(1)
1✔
338

1✔
339
        s.updateDynamicLink(0)
1✔
340
        s.expectDynamicLinkData(1)
1✔
341

1✔
342
        s.updateDynamicLink(2)
1✔
343
        s.expectDynamicLinkData(2)
1✔
344

1✔
345
        s.updateDynamicLink(1)
1✔
346
        s.expectDynamicLinkData(2)
1✔
347

1✔
348
        s.updateDynamicLink(0)
1✔
349
        s.expectDynamicLinkData(2)
1✔
350
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc