1
1
package storage
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
6
7
"fmt"
@@ -15,7 +16,10 @@ import (
15
16
"testing"
16
17
"testing/fstest"
17
18
19
+ "github.com/stretchr/testify/assert"
18
20
"github.com/stretchr/testify/require"
21
+
22
+ "github.com/operator-framework/operator-registry/alpha/declcfg"
19
23
)
20
24
21
25
const urlPrefix = "/catalogs/"
@@ -169,15 +173,45 @@ func TestLocalDirStoraget(t *testing.T) {
169
173
}
170
174
171
175
func TestLocalDirServerHandler (t * testing.T ) {
176
+ testMultiLineData := `{
177
+ "defaultChannel": "stable-v6.x",
178
+ "name": "cockroachdb",
179
+ "schema": "olm.package"
180
+ }
181
+ {
182
+ "entries": [
183
+ {
184
+ "name": "cockroachdb.v5.0.3"
185
+ },
186
+ {
187
+ "name": "cockroachdb.v5.0.4",
188
+ "replaces": "cockroachdb.v5.0.3"
189
+ }
190
+ ],
191
+ "name": "stable-5.x",
192
+ "package": "cockroachdb",
193
+ "schema": "olm.channel"
194
+ }
195
+ `
196
+
172
197
store := & LocalDirV1 {RootDir : t .TempDir (), RootURL : & url.URL {Path : urlPrefix }}
173
198
testFS := fstest.MapFS {
174
199
"meta.json" : & fstest.MapFile {
175
200
Data : []byte (`{"foo":"bar"}` ),
176
201
},
177
202
}
203
+ testMultiLineFS := fstest.MapFS {
204
+ "multi-line-data.json" : & fstest.MapFile {
205
+ Data : []byte (testMultiLineData ),
206
+ },
207
+ }
178
208
if store .Store (context .Background (), "test-catalog" , testFS ) != nil {
179
209
t .Fatal ("failed to store test catalog and start server" )
180
210
}
211
+ if store .Store (context .Background (), "test-multi-line-catalog" , testMultiLineFS ) != nil {
212
+ t .Fatal ("failed to store test multi-line catalog and start server" )
213
+ }
214
+
181
215
testServer := httptest .NewServer (store .StorageServerHandler ())
182
216
defer testServer .Close ()
183
217
@@ -247,6 +281,12 @@ func TestLocalDirServerHandler(t *testing.T) {
247
281
expectedContent : `{"foo":"bar"}` ,
248
282
URLPath : "/catalogs/test-catalog/api/v1/all" ,
249
283
},
284
+ {
285
+ name : "Server returns 200 and json-lines formatted data when path '/catalogs/<catalog>/api/v1/all' is queried, when catalog exists" ,
286
+ expectedStatusCode : http .StatusOK ,
287
+ expectedContent : generateJSONLinesOrFail (t , []byte (testMultiLineData )),
288
+ URLPath : "/catalogs/test-multi-line-catalog/api/v1/all" ,
289
+ },
250
290
} {
251
291
t .Run (tc .name , func (t * testing.T ) {
252
292
req , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s/%s" , testServer .URL , tc .URLPath ), nil )
@@ -256,11 +296,14 @@ func TestLocalDirServerHandler(t *testing.T) {
256
296
require .NoError (t , err )
257
297
258
298
require .Equal (t , tc .expectedStatusCode , resp .StatusCode )
299
+ if resp .StatusCode == http .StatusOK {
300
+ assert .Equal (t , "application/jsonl" , resp .Header .Get ("Content-Type" ))
301
+ }
259
302
260
- var actualContent []byte
261
- actualContent , err = io .ReadAll (resp .Body )
303
+ actualContent , err := io .ReadAll (resp .Body )
262
304
require .NoError (t , err )
263
- require .Equal (t , tc .expectedContent , strings .TrimSpace (string (actualContent )))
305
+
306
+ require .Equal (t , strings .TrimSpace (tc .expectedContent ), strings .TrimSpace (string (actualContent )))
264
307
require .NoError (t , resp .Body .Close ())
265
308
})
266
309
}
@@ -322,6 +365,12 @@ func TestQueryEndpoint(t *testing.T) {
322
365
expectedStatusCode : http .StatusOK ,
323
366
expectedContent : `{"image":"quaydock.io/namespace/bundle:0.0.3","name":"bundle.v0.0.1","package":"webhook_operator_test","properties":[{"type":"olm.bundle.object","value":{"data":"dW5pbXBvcnRhbnQK"}},{"type":"some.other","value":{"data":"arbitrary-info"}}],"relatedImages":[{"image":"testimage:latest","name":"test"}],"schema":"olm.bundle"}` ,
324
367
},
368
+ {
369
+ name : "valid query with multi-json, json-lines formatted response" ,
370
+ queryParams : "?schema=olm.channel" ,
371
+ expectedStatusCode : http .StatusOK ,
372
+ expectedContent : `{"entries":[{"name":"bundle.v0.0.1"}],"name":"preview_test","package":"webhook_operator_test","schema":"olm.channel"}` + "\n " + `{"entries":[{"name":"multi-bundle.v0.0.1"}],"name":"review_test","package":"multi_operator_test","schema":"olm.channel"}` ,
373
+ },
325
374
{
326
375
name : "query with non-existent schema" ,
327
376
queryParams : "?schema=non_existent_schema" ,
@@ -354,6 +403,9 @@ func TestQueryEndpoint(t *testing.T) {
354
403
defer resp .Body .Close ()
355
404
356
405
require .Equal (t , tc .expectedStatusCode , resp .StatusCode )
406
+ if resp .StatusCode == http .StatusOK {
407
+ assert .Equal (t , "application/jsonl" , resp .Header .Get ("Content-Type" ))
408
+ }
357
409
358
410
actualContent , err := io .ReadAll (resp .Body )
359
411
require .NoError (t , err )
@@ -540,12 +592,45 @@ entries:
540
592
testPackageName := "webhook_operator_test"
541
593
testChannelName := "preview_test"
542
594
595
+ testMultiPackageName := "multi_operator_test"
596
+ testMultiChannelName := "review_test"
597
+ testMultiBundleName := "multi-bundle.v0.0.1"
598
+
543
599
testPackage := fmt .Sprintf (testPackageTemplate , testPackageDefaultChannel , testPackageName )
544
600
testBundle := fmt .Sprintf (testBundleTemplate , testBundleImage , testBundleName , testPackageName , testBundleRelatedImageName , testBundleRelatedImageImage , testBundleObjectData )
545
601
testChannel := fmt .Sprintf (testChannelTemplate , testPackageName , testChannelName , testBundleName )
602
+ testMultiChannel := fmt .Sprintf (testChannelTemplate , testMultiPackageName , testMultiChannelName , testMultiBundleName )
546
603
return & fstest.MapFS {
547
- "bundle.yaml" : {Data : []byte (testBundle ), Mode : os .ModePerm },
548
- "package.yaml" : {Data : []byte (testPackage ), Mode : os .ModePerm },
549
- "channel.yaml" : {Data : []byte (testChannel ), Mode : os .ModePerm },
604
+ "bundle.yaml" : {Data : []byte (testBundle ), Mode : os .ModePerm },
605
+ "package.yaml" : {Data : []byte (testPackage ), Mode : os .ModePerm },
606
+ "channel.yaml" : {Data : []byte (testChannel ), Mode : os .ModePerm },
607
+ "multi-channel.yaml" : {Data : []byte (testMultiChannel ), Mode : os .ModePerm },
550
608
}
551
609
}
610
+
611
+ // generateJSONLines takes a byte slice of concatenated JSON objects and returns a JSONlines-formatted string.
612
+ func generateJSONLinesOrFail (t * testing.T , in []byte ) string {
613
+ var out strings.Builder
614
+ reader := bytes .NewReader (in )
615
+
616
+ err := declcfg .WalkMetasReader (reader , func (meta * declcfg.Meta , err error ) error {
617
+ if err != nil {
618
+ return err
619
+ }
620
+
621
+ if meta != nil && meta .Blob != nil {
622
+ if meta .Blob [len (meta .Blob )- 1 ] != '\n' {
623
+ return fmt .Errorf ("blob does not end with newline" )
624
+ }
625
+ }
626
+
627
+ _ , err = out .Write (meta .Blob )
628
+ if err != nil {
629
+ return err
630
+ }
631
+ return nil
632
+ })
633
+ require .NoError (t , err )
634
+
635
+ return out .String ()
636
+ }
0 commit comments