11package storage
22
33import (
4+ "bytes"
45 "context"
56 "errors"
67 "fmt"
@@ -15,7 +16,10 @@ import (
1516 "testing"
1617 "testing/fstest"
1718
19+ "github.com/stretchr/testify/assert"
1820 "github.com/stretchr/testify/require"
21+
22+ "github.com/operator-framework/operator-registry/alpha/declcfg"
1923)
2024
2125const urlPrefix = "/catalogs/"
@@ -169,15 +173,45 @@ func TestLocalDirStoraget(t *testing.T) {
169173}
170174
171175func 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+
172197 store := & LocalDirV1 {RootDir : t .TempDir (), RootURL : & url.URL {Path : urlPrefix }}
173198 testFS := fstest.MapFS {
174199 "meta.json" : & fstest.MapFile {
175200 Data : []byte (`{"foo":"bar"}` ),
176201 },
177202 }
203+ testMultiLineFS := fstest.MapFS {
204+ "multi-line-data.json" : & fstest.MapFile {
205+ Data : []byte (testMultiLineData ),
206+ },
207+ }
178208 if store .Store (context .Background (), "test-catalog" , testFS ) != nil {
179209 t .Fatal ("failed to store test catalog and start server" )
180210 }
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+
181215 testServer := httptest .NewServer (store .StorageServerHandler ())
182216 defer testServer .Close ()
183217
@@ -247,6 +281,12 @@ func TestLocalDirServerHandler(t *testing.T) {
247281 expectedContent : `{"foo":"bar"}` ,
248282 URLPath : "/catalogs/test-catalog/api/v1/all" ,
249283 },
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+ },
250290 } {
251291 t .Run (tc .name , func (t * testing.T ) {
252292 req , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s/%s" , testServer .URL , tc .URLPath ), nil )
@@ -256,11 +296,14 @@ func TestLocalDirServerHandler(t *testing.T) {
256296 require .NoError (t , err )
257297
258298 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+ }
259302
260- var actualContent []byte
261- actualContent , err = io .ReadAll (resp .Body )
303+ actualContent , err := io .ReadAll (resp .Body )
262304 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 )))
264307 require .NoError (t , resp .Body .Close ())
265308 })
266309 }
@@ -322,6 +365,12 @@ func TestQueryEndpoint(t *testing.T) {
322365 expectedStatusCode : http .StatusOK ,
323366 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"}` ,
324367 },
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+ },
325374 {
326375 name : "query with non-existent schema" ,
327376 queryParams : "?schema=non_existent_schema" ,
@@ -354,6 +403,9 @@ func TestQueryEndpoint(t *testing.T) {
354403 defer resp .Body .Close ()
355404
356405 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+ }
357409
358410 actualContent , err := io .ReadAll (resp .Body )
359411 require .NoError (t , err )
@@ -540,12 +592,45 @@ entries:
540592 testPackageName := "webhook_operator_test"
541593 testChannelName := "preview_test"
542594
595+ testMultiPackageName := "multi_operator_test"
596+ testMultiChannelName := "review_test"
597+ testMultiBundleName := "multi-bundle.v0.0.1"
598+
543599 testPackage := fmt .Sprintf (testPackageTemplate , testPackageDefaultChannel , testPackageName )
544600 testBundle := fmt .Sprintf (testBundleTemplate , testBundleImage , testBundleName , testPackageName , testBundleRelatedImageName , testBundleRelatedImageImage , testBundleObjectData )
545601 testChannel := fmt .Sprintf (testChannelTemplate , testPackageName , testChannelName , testBundleName )
602+ testMultiChannel := fmt .Sprintf (testChannelTemplate , testMultiPackageName , testMultiChannelName , testMultiBundleName )
546603 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 },
550608 }
551609}
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