package declcfg

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/fs"
	"os"
	"sync"
	"testing"
	"testing/fstest"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/util/yaml"

	"github.com/operator-framework/operator-registry/alpha/property"
)

func TestLoadReader(t *testing.T) {
	type spec struct {
		name              string
		fsys              fs.FS
		path              string
		assertion         require.ErrorAssertionFunc
		expectNumPackages int
		expectNumBundles  int
		expectNumOthers   int
	}
	specs := []spec{
		{
			name:      "Error/NotYAMLOrJSON",
			fsys:      invalidFS,
			path:      "invalid-format.txt",
			assertion: require.Error,
		},
		{
			name:      "Error/NotJSONObject",
			fsys:      invalidFS,
			path:      "not-object.json",
			assertion: require.Error,
		},
		{
			name:      "Error/NoSchema",
			fsys:      invalidFS,
			path:      "no-schema.yaml",
			assertion: require.Error,
		},
		{
			name:      "Error/InvalidPackageJSON",
			fsys:      invalidFS,
			path:      "invalid-package.json",
			assertion: require.Error,
		},
		{
			name:      "Error/InvalidBundleJSON",
			fsys:      invalidFS,
			path:      "invalid-bundle.json",
			assertion: require.Error,
		},
		{
			name:              "Success/UnrecognizedSchema",
			fsys:              validFS,
			path:              "unrecognized-schema.json",
			assertion:         require.NoError,
			expectNumPackages: 1,
			expectNumBundles:  1,
			expectNumOthers:   1,
		},
		{
			name:              "Success/ValidFile",
			fsys:              validFS,
			path:              "etcd.yaml",
			assertion:         require.NoError,
			expectNumPackages: 1,
			expectNumBundles:  6,
			expectNumOthers:   0,
		},
	}

	for _, s := range specs {
		t.Run(s.name, func(t *testing.T) {
			f, err := s.fsys.Open(s.path)
			require.NoError(t, err)

			cfg, err := LoadReader(f)
			s.assertion(t, err)
			if err == nil {
				require.NotNil(t, cfg)
				assert.Equal(t, len(cfg.Packages), s.expectNumPackages, "unexpected package count")
				assert.Equal(t, len(cfg.Bundles), s.expectNumBundles, "unexpected bundle count")
				assert.Equal(t, len(cfg.Others), s.expectNumOthers, "unexpected others count")
			}
		})
	}
}

func TestWalkMetasFS(t *testing.T) {
	type spec struct {
		name                  string
		fsys                  fs.FS
		assertion             require.ErrorAssertionFunc
		expectNumPackages     int
		expectNumChannels     int
		expectNumBundles      int
		expectNumDeprecations int
		expectNumOthers       int
	}
	specs := []spec{
		{
			name:      "Error/NilFS",
			fsys:      nil,
			assertion: require.Error,
		},
		{
			name:      "Error/NonExistentDir",
			fsys:      os.DirFS("non/existent/dir/"),
			assertion: require.Error,
		},
		{
			name:      "Error/Invalid",
			fsys:      invalidFS,
			assertion: require.Error,
		},
		{
			name:                  "Success/ValidDir",
			fsys:                  validFS,
			assertion:             require.NoError,
			expectNumPackages:     3,
			expectNumChannels:     0,
			expectNumBundles:      12,
			expectNumDeprecations: 1,
			expectNumOthers:       1,
		},
	}

	for _, s := range specs {
		t.Run(s.name, func(t *testing.T) {
			var mu sync.Mutex
			numPackages, numChannels, numBundles, numDeprecations, numOthers := 0, 0, 0, 0, 0
			err := WalkMetasFS(context.Background(), s.fsys, func(path string, meta *Meta, err error) error {
				if err != nil {
					return err
				}
				mu.Lock()
				defer mu.Unlock()
				switch meta.Schema {
				case SchemaPackage:
					numPackages++
				case SchemaChannel:
					numChannels++
				case SchemaBundle:
					numBundles++
				case SchemaDeprecation:
					numDeprecations++
				default:
					numOthers++
				}
				return nil
			})
			s.assertion(t, err)
			if err == nil {
				assert.Equal(t, s.expectNumPackages, numPackages, "unexpected package count")
				assert.Equal(t, s.expectNumChannels, numChannels, "unexpected channel count")
				assert.Equal(t, s.expectNumBundles, numBundles, "unexpected bundle count")
				assert.Equal(t, s.expectNumOthers, numOthers, "unexpected others count")
			}
		})
	}
}

func TestLoadFS(t *testing.T) {
	type spec struct {
		name      string
		fsys      fs.FS
		assertion require.ErrorAssertionFunc
		expected  *DeclarativeConfig
	}
	specs := []spec{
		{
			name:      "Error/NilFS",
			fsys:      nil,
			assertion: require.Error,
		},
		{
			name:      "Error/NonExistentDir",
			fsys:      os.DirFS("non/existent/dir/"),
			assertion: require.Error,
		},
		{
			name:      "Error/Invalid",
			fsys:      invalidFS,
			assertion: require.Error,
		},
		{
			name:      "Success/ValidDir",
			fsys:      validFS,
			assertion: require.NoError,
			expected: &DeclarativeConfig{
				Packages: []Package{
					{Schema: "olm.package", Name: "cockroachdb", DefaultChannel: "stable-5.x", Icon: &Icon{Data: []uint8{0x3c, 0x73, 0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x33, 0x31, 0x2e, 0x38, 0x32, 0x20, 0x33, 0x32, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x34, 0x38, 0x36, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x32, 0x35, 0x30, 0x30, 0x22, 0x3e, 0x3c, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0x43, 0x4c, 0x3c, 0x2f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x39, 0x2e, 0x34, 0x32, 0x20, 0x39, 0x2e, 0x31, 0x37, 0x61, 0x31, 0x35, 0x2e, 0x33, 0x39, 0x20, 0x31, 0x35, 0x2e, 0x33, 0x39, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x33, 0x2e, 0x35, 0x31, 0x2e, 0x34, 0x20, 0x31, 0x35, 0x2e, 0x34, 0x36, 0x20, 0x31, 0x35, 0x2e, 0x34, 0x36, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x33, 0x2e, 0x35, 0x31, 0x2d, 0x2e, 0x34, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x33, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x33, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x33, 0x2e, 0x35, 0x31, 0x2d, 0x33, 0x2e, 0x39, 0x31, 0x20, 0x31, 0x35, 0x2e, 0x37, 0x31, 0x20, 0x31, 0x35, 0x2e, 0x37, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x33, 0x2e, 0x35, 0x31, 0x20, 0x33, 0x2e, 0x39, 0x31, 0x7a, 0x4d, 0x33, 0x30, 0x20, 0x2e, 0x35, 0x37, 0x41, 0x31, 0x37, 0x2e, 0x32, 0x32, 0x20, 0x31, 0x37, 0x2e, 0x32, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x32, 0x35, 0x2e, 0x35, 0x39, 0x20, 0x30, 0x61, 0x31, 0x37, 0x2e, 0x34, 0x20, 0x31, 0x37, 0x2e, 0x34, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x39, 0x2e, 0x36, 0x38, 0x20, 0x32, 0x2e, 0x39, 0x33, 0x41, 0x31, 0x37, 0x2e, 0x33, 0x38, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x38, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x36, 0x2e, 0x32, 0x33, 0x20, 0x30, 0x61, 0x31, 0x37, 0x2e, 0x32, 0x32, 0x20, 0x31, 0x37, 0x2e, 0x32, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x34, 0x2e, 0x34, 0x34, 0x2e, 0x35, 0x37, 0x41, 0x31, 0x36, 0x2e, 0x32, 0x32, 0x20, 0x31, 0x36, 0x2e, 0x32, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2e, 0x31, 0x33, 0x61, 0x2e, 0x30, 0x37, 0x2e, 0x30, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2e, 0x30, 0x39, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x32, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2e, 0x38, 0x33, 0x20, 0x31, 0x2e, 0x35, 0x37, 0x2e, 0x30, 0x37, 0x2e, 0x30, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2e, 0x30, 0x38, 0x20, 0x30, 0x20, 0x31, 0x36, 0x2e, 0x33, 0x39, 0x20, 0x31, 0x36, 0x2e, 0x33, 0x39, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x2e, 0x38, 0x31, 0x2d, 0x2e, 0x35, 0x34, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x35, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x31, 0x2e, 0x35, 0x39, 0x20, 0x31, 0x2e, 0x38, 0x38, 0x20, 0x31, 0x37, 0x2e, 0x35, 0x32, 0x20, 0x31, 0x37, 0x2e, 0x35, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x33, 0x2e, 0x37, 0x38, 0x20, 0x34, 0x2e, 0x34, 0x38, 0x63, 0x2d, 0x2e, 0x32, 0x2e, 0x33, 0x32, 0x2d, 0x2e, 0x33, 0x37, 0x2e, 0x36, 0x35, 0x2d, 0x2e, 0x35, 0x35, 0x20, 0x31, 0x73, 0x2d, 0x2e, 0x32, 0x32, 0x2e, 0x34, 0x35, 0x2d, 0x2e, 0x33, 0x33, 0x2e, 0x36, 0x39, 0x2d, 0x2e, 0x33, 0x31, 0x2e, 0x37, 0x32, 0x2d, 0x2e, 0x34, 0x34, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x61, 0x31, 0x37, 0x2e, 0x34, 0x36, 0x20, 0x31, 0x37, 0x2e, 0x34, 0x36, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x34, 0x2e, 0x32, 0x39, 0x20, 0x31, 0x38, 0x2e, 0x37, 0x63, 0x2e, 0x32, 0x36, 0x2e, 0x32, 0x35, 0x2e, 0x35, 0x33, 0x2e, 0x34, 0x39, 0x2e, 0x38, 0x31, 0x2e, 0x37, 0x33, 0x73, 0x2e, 0x34, 0x34, 0x2e, 0x33, 0x37, 0x2e, 0x36, 0x37, 0x2e, 0x35, 0x34, 0x2e, 0x35, 0x39, 0x2e, 0x34, 0x34, 0x2e, 0x38, 0x39, 0x2e, 0x36, 0x34, 0x61, 0x2e, 0x30, 0x37, 0x2e, 0x30, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2e, 0x30, 0x38, 0x20, 0x30, 0x63, 0x2e, 0x33, 0x2d, 0x2e, 0x32, 0x31, 0x2e, 0x36, 0x2d, 0x2e, 0x34, 0x32, 0x2e, 0x38, 0x39, 0x2d, 0x2e, 0x36, 0x34, 0x73, 0x2e, 0x34, 0x35, 0x2d, 0x2e, 0x33, 0x35, 0x2e, 0x36, 0x37, 0x2d, 0x2e, 0x35, 0x34, 0x2e, 0x35, 0x35, 0x2d, 0x2e, 0x34, 0x38, 0x2e, 0x38, 0x31, 0x2d, 0x2e, 0x37, 0x33, 0x61, 0x31, 0x37, 0x2e, 0x34, 0x35, 0x20, 0x31, 0x37, 0x2e, 0x34, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x35, 0x2e, 0x33, 0x38, 0x2d, 0x31, 0x32, 0x2e, 0x36, 0x31, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x39, 0x20, 0x31, 0x37, 0x2e, 0x33, 0x39, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x31, 0x2e, 0x30, 0x39, 0x2d, 0x36, 0x2e, 0x30, 0x39, 0x63, 0x2d, 0x2e, 0x31, 0x34, 0x2d, 0x2e, 0x33, 0x37, 0x2d, 0x2e, 0x32, 0x39, 0x2d, 0x2e, 0x37, 0x33, 0x2d, 0x2e, 0x34, 0x35, 0x2d, 0x31, 0x2e, 0x30, 0x39, 0x73, 0x2d, 0x2e, 0x32, 0x32, 0x2d, 0x2e, 0x34, 0x37, 0x2d, 0x2e, 0x33, 0x33, 0x2d, 0x2e, 0x36, 0x39, 0x2d, 0x2e, 0x33, 0x35, 0x2d, 0x2e, 0x36, 0x36, 0x2d, 0x2e, 0x35, 0x35, 0x2d, 0x31, 0x61, 0x31, 0x37, 0x2e, 0x36, 0x31, 0x20, 0x31, 0x37, 0x2e, 0x36, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x33, 0x2e, 0x37, 0x38, 0x2d, 0x34, 0x2e, 0x34, 0x38, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x35, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x31, 0x2e, 0x36, 0x2d, 0x31, 0x2e, 0x38, 0x34, 0x20, 0x31, 0x36, 0x2e, 0x31, 0x33, 0x20, 0x31, 0x36, 0x2e, 0x31, 0x33, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x2e, 0x38, 0x31, 0x2e, 0x35, 0x34, 0x2e, 0x30, 0x37, 0x2e, 0x30, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2e, 0x30, 0x38, 0x20, 0x30, 0x71, 0x2e, 0x34, 0x34, 0x2d, 0x2e, 0x37, 0x36, 0x2e, 0x38, 0x32, 0x2d, 0x31, 0x2e, 0x35, 0x36, 0x61, 0x2e, 0x30, 0x37, 0x2e, 0x30, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x2e, 0x30, 0x39, 0x41, 0x31, 0x36, 0x2e, 0x38, 0x39, 0x20, 0x31, 0x36, 0x2e, 0x38, 0x39, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x33, 0x30, 0x20, 0x2e, 0x35, 0x37, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x23, 0x31, 0x35, 0x31, 0x66, 0x33, 0x34, 0x22, 0x2f, 0x3e, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x32, 0x31, 0x2e, 0x38, 0x32, 0x20, 0x31, 0x37, 0x2e, 0x34, 0x37, 0x61, 0x31, 0x35, 0x2e, 0x35, 0x31, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x34, 0x2e, 0x32, 0x35, 0x20, 0x31, 0x30, 0x2e, 0x36, 0x39, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x36, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x36, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x2e, 0x37, 0x32, 0x2d, 0x34, 0x2e, 0x36, 0x38, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x34, 0x2e, 0x32, 0x35, 0x2d, 0x31, 0x30, 0x2e, 0x36, 0x39, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x32, 0x20, 0x31, 0x35, 0x2e, 0x36, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x2e, 0x37, 0x32, 0x20, 0x34, 0x2e, 0x36, 0x38, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x23, 0x33, 0x34, 0x38, 0x35, 0x34, 0x30, 0x22, 0x2f, 0x3e, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x35, 0x20, 0x32, 0x33, 0x2e, 0x34, 0x38, 0x61, 0x31, 0x35, 0x2e, 0x35, 0x35, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x2e, 0x37, 0x32, 0x20, 0x34, 0x2e, 0x36, 0x38, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x34, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x34, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x33, 0x2e, 0x35, 0x33, 0x2d, 0x31, 0x35, 0x2e, 0x33, 0x37, 0x41, 0x31, 0x35, 0x2e, 0x35, 0x20, 0x31, 0x35, 0x2e, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x35, 0x20, 0x32, 0x33, 0x2e, 0x34, 0x38, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x23, 0x37, 0x64, 0x62, 0x63, 0x34, 0x32, 0x22, 0x2f, 0x3e, 0x3c, 0x2f, 0x73, 0x76, 0x67, 0x3e}, MediaType: "image/svg+xml"}, Description: ""},
					{Schema: "olm.package", Name: "etcd", DefaultChannel: "singlenamespace-alpha", Icon: &Icon{Data: []uint8{0x3c, 0x73, 0x76, 0x67, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x35, 0x30, 0x30, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x32, 0x34, 0x32, 0x32, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x32, 0x35, 0x36, 0x20, 0x32, 0x34, 0x38, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x41, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x3d, 0x22, 0x78, 0x4d, 0x69, 0x64, 0x59, 0x4d, 0x69, 0x64, 0x22, 0x3e, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x32, 0x35, 0x32, 0x2e, 0x33, 0x38, 0x36, 0x20, 0x31, 0x32, 0x38, 0x2e, 0x30, 0x36, 0x34, 0x63, 0x2d, 0x31, 0x2e, 0x32, 0x30, 0x32, 0x2e, 0x31, 0x2d, 0x32, 0x2e, 0x34, 0x31, 0x2e, 0x31, 0x34, 0x37, 0x2d, 0x33, 0x2e, 0x36, 0x39, 0x33, 0x2e, 0x31, 0x34, 0x37, 0x2d, 0x37, 0x2e, 0x34, 0x34, 0x36, 0x20, 0x30, 0x2d, 0x31, 0x34, 0x2e, 0x36, 0x37, 0x2d, 0x31, 0x2e, 0x37, 0x34, 0x36, 0x2d, 0x32, 0x31, 0x2e, 0x31, 0x38, 0x37, 0x2d, 0x34, 0x2e, 0x39, 0x34, 0x34, 0x20, 0x32, 0x2e, 0x31, 0x37, 0x2d, 0x31, 0x32, 0x2e, 0x34, 0x34, 0x37, 0x20, 0x33, 0x2e, 0x30, 0x39, 0x32, 0x2d, 0x32, 0x34, 0x2e, 0x39, 0x38, 0x37, 0x20, 0x32, 0x2e, 0x38, 0x35, 0x2d, 0x33, 0x37, 0x2e, 0x34, 0x38, 0x31, 0x2d, 0x37, 0x2e, 0x30, 0x36, 0x35, 0x2d, 0x31, 0x30, 0x2e, 0x32, 0x32, 0x2d, 0x31, 0x35, 0x2e, 0x31, 0x34, 0x2d, 0x31, 0x39, 0x2e, 0x38, 0x36, 0x33, 0x2d, 0x32, 0x34, 0x2e, 0x32, 0x35, 0x36, 0x2d, 0x32, 0x38, 0x2e, 0x37, 0x34, 0x37, 0x20, 0x33, 0x2e, 0x39, 0x35, 0x35, 0x2d, 0x37, 0x2e, 0x34, 0x31, 0x35, 0x20, 0x39, 0x2e, 0x38, 0x30, 0x31, 0x2d, 0x31, 0x33, 0x2e, 0x37, 0x39, 0x35, 0x20, 0x31, 0x37, 0x2e, 0x31, 0x2d, 0x31, 0x38, 0x2e, 0x33, 0x31, 0x39, 0x6c, 0x33, 0x2e, 0x31, 0x33, 0x33, 0x2d, 0x31, 0x2e, 0x39, 0x33, 0x37, 0x2d, 0x32, 0x2e, 0x34, 0x34, 0x32, 0x2d, 0x32, 0x2e, 0x37, 0x35, 0x34, 0x63, 0x2d, 0x31, 0x32, 0x2e, 0x35, 0x38, 0x31, 0x2d, 0x31, 0x34, 0x2e, 0x31, 0x36, 0x37, 0x2d, 0x32, 0x37, 0x2e, 0x35, 0x39, 0x36, 0x2d, 0x32, 0x35, 0x2e, 0x31, 0x32, 0x2d, 0x34, 0x34, 0x2e, 0x36, 0x32, 0x2d, 0x33, 0x32, 0x2e, 0x35, 0x35, 0x32, 0x4c, 0x31, 0x37, 0x35, 0x2e, 0x38, 0x37, 0x36, 0x20, 0x30, 0x6c, 0x2d, 0x2e, 0x38, 0x36, 0x32, 0x20, 0x33, 0x2e, 0x35, 0x38, 0x38, 0x63, 0x2d, 0x32, 0x2e, 0x30, 0x33, 0x20, 0x38, 0x2e, 0x33, 0x36, 0x33, 0x2d, 0x36, 0x2e, 0x32, 0x37, 0x34, 0x20, 0x31, 0x35, 0x2e, 0x39, 0x30, 0x38, 0x2d, 0x31, 0x32, 0x2e, 0x31, 0x20, 0x32, 0x31, 0x2e, 0x39, 0x36, 0x32, 0x61, 0x31, 0x39, 0x33, 0x2e, 0x38, 0x34, 0x32, 0x20, 0x31, 0x39, 0x33, 0x2e, 0x38, 0x34, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x2d, 0x33, 0x34, 0x2e, 0x39, 0x35, 0x36, 0x2d, 0x31, 0x34, 0x2e, 0x34, 0x30, 0x35, 0x41, 0x31, 0x39, 0x34, 0x2e, 0x30, 0x31, 0x32, 0x20, 0x31, 0x39, 0x34, 0x2e, 0x30, 0x31, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x39, 0x33, 0x2e, 0x30, 0x35, 0x36, 0x20, 0x32, 0x35, 0x2e, 0x35, 0x32, 0x43, 0x38, 0x37, 0x2e, 0x32, 0x35, 0x34, 0x20, 0x31, 0x39, 0x2e, 0x34, 0x37, 0x33, 0x20, 0x38, 0x33, 0x2e, 0x30, 0x32, 0x20, 0x31, 0x31, 0x2e, 0x39, 0x34, 0x37, 0x20, 0x38, 0x30, 0x2e, 0x39, 0x39, 0x39, 0x20, 0x33, 0x2e, 0x36, 0x30, 0x38, 0x4c, 0x38, 0x30, 0x2e, 0x31, 0x33, 0x2e, 0x30, 0x32, 0x6c, 0x2d, 0x33, 0x2e, 0x33, 0x38, 0x32, 0x20, 0x31, 0x2e, 0x34, 0x37, 0x43, 0x35, 0x39, 0x2e, 0x39, 0x33, 0x39, 0x20, 0x38, 0x2e, 0x38, 0x31, 0x35, 0x20, 0x34, 0x34, 0x2e, 0x35, 0x31, 0x20, 0x32, 0x30, 0x2e, 0x30, 0x36, 0x35, 0x20, 0x33, 0x32, 0x2e, 0x31, 0x33, 0x35, 0x20, 0x33, 0x34, 0x2e, 0x30, 0x32, 0x6c, 0x2d, 0x32, 0x2e, 0x34, 0x34, 0x39, 0x20, 0x32, 0x2e, 0x37, 0x36, 0x20, 0x33, 0x2e, 0x31, 0x33, 0x20, 0x31, 0x2e, 0x39, 0x33, 0x37, 0x63, 0x37, 0x2e, 0x32, 0x37, 0x36, 0x20, 0x34, 0x2e, 0x35, 0x30, 0x36, 0x20, 0x31, 0x33, 0x2e, 0x31, 0x30, 0x36, 0x20, 0x31, 0x30, 0x2e, 0x38, 0x34, 0x39, 0x20, 0x31, 0x37, 0x2e, 0x30, 0x35, 0x34, 0x20, 0x31, 0x38, 0x2e, 0x32, 0x32, 0x33, 0x2d, 0x39, 0x2e, 0x30, 0x38, 0x38, 0x20, 0x38, 0x2e, 0x38, 0x35, 0x2d, 0x31, 0x37, 0x2e, 0x31, 0x35, 0x34, 0x20, 0x31, 0x38, 0x2e, 0x34, 0x36, 0x32, 0x2d, 0x32, 0x34, 0x2e, 0x32, 0x31, 0x34, 0x20, 0x32, 0x38, 0x2e, 0x36, 0x33, 0x35, 0x2d, 0x2e, 0x32, 0x37, 0x35, 0x20, 0x31, 0x32, 0x2e, 0x34, 0x38, 0x39, 0x2e, 0x36, 0x20, 0x32, 0x35, 0x2e, 0x31, 0x32, 0x20, 0x32, 0x2e, 0x37, 0x38, 0x20, 0x33, 0x37, 0x2e, 0x37, 0x34, 0x2d, 0x36, 0x2e, 0x34, 0x38, 0x34, 0x20, 0x33, 0x2e, 0x31, 0x36, 0x37, 0x2d, 0x31, 0x33, 0x2e, 0x36, 0x36, 0x38, 0x20, 0x34, 0x2e, 0x38, 0x39, 0x34, 0x2d, 0x32, 0x31, 0x2e, 0x30, 0x36, 0x35, 0x20, 0x34, 0x2e, 0x38, 0x39, 0x34, 0x2d, 0x31, 0x2e, 0x32, 0x39, 0x38, 0x20, 0x30, 0x2d, 0x32, 0x2e, 0x35, 0x31, 0x33, 0x2d, 0x2e, 0x30, 0x34, 0x37, 0x2d, 0x33, 0x2e, 0x36, 0x39, 0x33, 0x2d, 0x2e, 0x31, 0x34, 0x35, 0x4c, 0x30, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x37, 0x38, 0x35, 0x6c, 0x2e, 0x33, 0x34, 0x35, 0x20, 0x33, 0x2e, 0x36, 0x37, 0x31, 0x63, 0x31, 0x2e, 0x38, 0x30, 0x32, 0x20, 0x31, 0x38, 0x2e, 0x35, 0x37, 0x38, 0x20, 0x37, 0x2e, 0x35, 0x37, 0x20, 0x33, 0x36, 0x2e, 0x32, 0x34, 0x37, 0x20, 0x31, 0x37, 0x2e, 0x31, 0x35, 0x34, 0x20, 0x35, 0x32, 0x2e, 0x35, 0x32, 0x33, 0x6c, 0x31, 0x2e, 0x38, 0x37, 0x20, 0x33, 0x2e, 0x31, 0x37, 0x36, 0x20, 0x32, 0x2e, 0x38, 0x31, 0x2d, 0x32, 0x2e, 0x33, 0x38, 0x34, 0x61, 0x34, 0x38, 0x2e, 0x30, 0x34, 0x20, 0x34, 0x38, 0x2e, 0x30, 0x34, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x32, 0x2e, 0x37, 0x33, 0x37, 0x2d, 0x31, 0x30, 0x2e, 0x36, 0x35, 0x20, 0x31, 0x39, 0x34, 0x2e, 0x38, 0x36, 0x20, 0x31, 0x39, 0x34, 0x2e, 0x38, 0x36, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x39, 0x2e, 0x34, 0x36, 0x20, 0x33, 0x31, 0x2e, 0x36, 0x39, 0x36, 0x63, 0x31, 0x31, 0x2e, 0x38, 0x32, 0x38, 0x20, 0x34, 0x2e, 0x31, 0x33, 0x37, 0x20, 0x32, 0x34, 0x2e, 0x31, 0x35, 0x31, 0x20, 0x37, 0x2e, 0x32, 0x32, 0x35, 0x20, 0x33, 0x36, 0x2e, 0x38, 0x37, 0x38, 0x20, 0x39, 0x2e, 0x30, 0x36, 0x33, 0x20, 0x31, 0x2e, 0x32, 0x32, 0x20, 0x38, 0x2e, 0x34, 0x31, 0x37, 0x2e, 0x32, 0x34, 0x38, 0x20, 0x31, 0x37, 0x2e, 0x31, 0x32, 0x32, 0x2d, 0x33, 0x2e, 0x30, 0x37, 0x32, 0x20, 0x32, 0x35, 0x2e, 0x31, 0x37, 0x31, 0x6c, 0x2d, 0x31, 0x2e, 0x34, 0x20, 0x33, 0x2e, 0x34, 0x31, 0x31, 0x20, 0x33, 0x2e, 0x36, 0x2e, 0x37, 0x39, 0x33, 0x63, 0x39, 0x2e, 0x32, 0x32, 0x20, 0x32, 0x2e, 0x30, 0x32, 0x37, 0x20, 0x31, 0x38, 0x2e, 0x35, 0x32, 0x33, 0x20, 0x33, 0x2e, 0x30, 0x36, 0x20, 0x32, 0x37, 0x2e, 0x36, 0x33, 0x31, 0x20, 0x33, 0x2e, 0x30, 0x36, 0x6c, 0x32, 0x37, 0x2e, 0x36, 0x32, 0x33, 0x2d, 0x33, 0x2e, 0x30, 0x36, 0x20, 0x33, 0x2e, 0x36, 0x30, 0x34, 0x2d, 0x2e, 0x37, 0x39, 0x33, 0x2d, 0x31, 0x2e, 0x34, 0x30, 0x33, 0x2d, 0x33, 0x2e, 0x34, 0x31, 0x37, 0x63, 0x2d, 0x33, 0x2e, 0x33, 0x31, 0x32, 0x2d, 0x38, 0x2e, 0x30, 0x35, 0x2d, 0x34, 0x2e, 0x32, 0x38, 0x34, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x36, 0x35, 0x2d, 0x33, 0x2e, 0x30, 0x36, 0x33, 0x2d, 0x32, 0x35, 0x2e, 0x31, 0x38, 0x33, 0x20, 0x31, 0x32, 0x2e, 0x36, 0x37, 0x36, 0x2d, 0x31, 0x2e, 0x38, 0x34, 0x20, 0x32, 0x34, 0x2e, 0x39, 0x35, 0x34, 0x2d, 0x34, 0x2e, 0x39, 0x32, 0x20, 0x33, 0x36, 0x2e, 0x37, 0x33, 0x38, 0x2d, 0x39, 0x2e, 0x30, 0x34, 0x35, 0x61, 0x31, 0x39, 0x35, 0x2e, 0x31, 0x30, 0x38, 0x20, 0x31, 0x39, 0x35, 0x2e, 0x31, 0x30, 0x38, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x39, 0x2e, 0x34, 0x38, 0x32, 0x2d, 0x33, 0x31, 0x2e, 0x37, 0x32, 0x36, 0x20, 0x34, 0x38, 0x2e, 0x32, 0x35, 0x34, 0x20, 0x34, 0x38, 0x2e, 0x32, 0x35, 0x34, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x32, 0x2e, 0x38, 0x34, 0x38, 0x20, 0x31, 0x30, 0x2e, 0x36, 0x36, 0x6c, 0x32, 0x2e, 0x38, 0x30, 0x39, 0x20, 0x32, 0x2e, 0x33, 0x38, 0x20, 0x31, 0x2e, 0x38, 0x36, 0x32, 0x2d, 0x33, 0x2e, 0x31, 0x36, 0x38, 0x63, 0x39, 0x2e, 0x36, 0x2d, 0x31, 0x36, 0x2e, 0x32, 0x39, 0x37, 0x20, 0x31, 0x35, 0x2e, 0x33, 0x36, 0x38, 0x2d, 0x33, 0x33, 0x2e, 0x39, 0x36, 0x35, 0x20, 0x31, 0x37, 0x2e, 0x31, 0x34, 0x32, 0x2d, 0x35, 0x32, 0x2e, 0x35, 0x31, 0x33, 0x6c, 0x2e, 0x33, 0x34, 0x35, 0x2d, 0x33, 0x2e, 0x36, 0x36, 0x35, 0x2d, 0x33, 0x2e, 0x36, 0x31, 0x34, 0x2e, 0x32, 0x37, 0x39, 0x7a, 0x4d, 0x31, 0x36, 0x37, 0x2e, 0x34, 0x39, 0x20, 0x31, 0x37, 0x32, 0x2e, 0x39, 0x36, 0x63, 0x2d, 0x31, 0x33, 0x2e, 0x30, 0x36, 0x38, 0x20, 0x33, 0x2e, 0x35, 0x35, 0x34, 0x2d, 0x32, 0x36, 0x2e, 0x33, 0x34, 0x20, 0x35, 0x2e, 0x33, 0x34, 0x38, 0x2d, 0x33, 0x39, 0x2e, 0x35, 0x33, 0x32, 0x20, 0x35, 0x2e, 0x33, 0x34, 0x38, 0x2d, 0x31, 0x33, 0x2e, 0x32, 0x32, 0x38, 0x20, 0x30, 0x2d, 0x32, 0x36, 0x2e, 0x34, 0x38, 0x33, 0x2d, 0x31, 0x2e, 0x37, 0x39, 0x33, 0x2d, 0x33, 0x39, 0x2e, 0x35, 0x36, 0x33, 0x2d, 0x35, 0x2e, 0x33, 0x34, 0x38, 0x61, 0x31, 0x35, 0x33, 0x2e, 0x32, 0x35, 0x35, 0x20, 0x31, 0x35, 0x33, 0x2e, 0x32, 0x35, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x31, 0x36, 0x2e, 0x39, 0x33, 0x32, 0x2d, 0x33, 0x35, 0x2e, 0x36, 0x37, 0x63, 0x2d, 0x34, 0x2e, 0x30, 0x36, 0x36, 0x2d, 0x31, 0x32, 0x2e, 0x35, 0x31, 0x37, 0x2d, 0x36, 0x2e, 0x34, 0x34, 0x35, 0x2d, 0x32, 0x35, 0x2e, 0x36, 0x33, 0x2d, 0x37, 0x2e, 0x31, 0x33, 0x35, 0x2d, 0x33, 0x39, 0x2e, 0x31, 0x33, 0x34, 0x20, 0x38, 0x2e, 0x34, 0x34, 0x36, 0x2d, 0x31, 0x30, 0x2e, 0x34, 0x34, 0x33, 0x20, 0x31, 0x38, 0x2e, 0x30, 0x35, 0x32, 0x2d, 0x31, 0x39, 0x2e, 0x35, 0x39, 0x31, 0x20, 0x32, 0x38, 0x2e, 0x36, 0x36, 0x35, 0x2d, 0x32, 0x37, 0x2e, 0x32, 0x39, 0x33, 0x61, 0x31, 0x35, 0x32, 0x2e, 0x36, 0x32, 0x20, 0x31, 0x35, 0x32, 0x2e, 0x36, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x33, 0x34, 0x2e, 0x39, 0x36, 0x35, 0x2d, 0x31, 0x39, 0x2e, 0x30, 0x31, 0x31, 0x20, 0x31, 0x35, 0x33, 0x2e, 0x32, 0x34, 0x32, 0x20, 0x31, 0x35, 0x33, 0x2e, 0x32, 0x34, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x33, 0x34, 0x2e, 0x38, 0x39, 0x38, 0x20, 0x31, 0x38, 0x2e, 0x39, 0x37, 0x63, 0x31, 0x30, 0x2e, 0x36, 0x35, 0x34, 0x20, 0x37, 0x2e, 0x37, 0x34, 0x33, 0x20, 0x32, 0x30, 0x2e, 0x33, 0x30, 0x32, 0x20, 0x31, 0x36, 0x2e, 0x39, 0x36, 0x32, 0x20, 0x32, 0x38, 0x2e, 0x37, 0x39, 0x20, 0x32, 0x37, 0x2e, 0x34, 0x37, 0x2d, 0x2e, 0x37, 0x32, 0x34, 0x20, 0x31, 0x33, 0x2e, 0x34, 0x32, 0x37, 0x2d, 0x33, 0x2e, 0x31, 0x33, 0x32, 0x20, 0x32, 0x36, 0x2e, 0x34, 0x36, 0x35, 0x2d, 0x37, 0x2e, 0x32, 0x30, 0x34, 0x20, 0x33, 0x38, 0x2e, 0x39, 0x36, 0x31, 0x61, 0x31, 0x35, 0x32, 0x2e, 0x37, 0x36, 0x37, 0x20, 0x31, 0x35, 0x32, 0x2e, 0x37, 0x36, 0x37, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x2d, 0x31, 0x36, 0x2e, 0x39, 0x35, 0x32, 0x20, 0x33, 0x35, 0x2e, 0x37, 0x30, 0x37, 0x7a, 0x6d, 0x2d, 0x32, 0x38, 0x2e, 0x37, 0x34, 0x2d, 0x36, 0x32, 0x2e, 0x39, 0x39, 0x38, 0x63, 0x30, 0x20, 0x39, 0x2e, 0x32, 0x33, 0x32, 0x20, 0x37, 0x2e, 0x34, 0x38, 0x32, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x30, 0x32, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x39, 0x2e, 0x32, 0x31, 0x37, 0x20, 0x30, 0x20, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x2d, 0x37, 0x2e, 0x34, 0x36, 0x36, 0x20, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x30, 0x2d, 0x39, 0x2e, 0x31, 0x39, 0x36, 0x2d, 0x37, 0x2e, 0x34, 0x37, 0x33, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x32, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x32, 0x2d, 0x39, 0x2e, 0x32, 0x32, 0x20, 0x30, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x30, 0x31, 0x20, 0x37, 0x2e, 0x34, 0x39, 0x36, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x30, 0x31, 0x20, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x32, 0x7a, 0x6d, 0x2d, 0x32, 0x31, 0x2e, 0x35, 0x37, 0x38, 0x20, 0x30, 0x63, 0x30, 0x20, 0x39, 0x2e, 0x32, 0x33, 0x32, 0x2d, 0x37, 0x2e, 0x34, 0x38, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x2d, 0x39, 0x2e, 0x32, 0x32, 0x36, 0x20, 0x30, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x38, 0x35, 0x2d, 0x37, 0x2e, 0x34, 0x36, 0x36, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x38, 0x35, 0x2d, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x30, 0x2d, 0x39, 0x2e, 0x31, 0x39, 0x33, 0x20, 0x37, 0x2e, 0x34, 0x36, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x38, 0x39, 0x20, 0x31, 0x36, 0x2e, 0x36, 0x38, 0x36, 0x2d, 0x31, 0x36, 0x2e, 0x36, 0x38, 0x39, 0x20, 0x39, 0x2e, 0x32, 0x32, 0x20, 0x30, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x37, 0x2e, 0x34, 0x39, 0x36, 0x20, 0x31, 0x36, 0x2e, 0x37, 0x20, 0x31, 0x36, 0x2e, 0x36, 0x39, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x23, 0x34, 0x31, 0x39, 0x45, 0x44, 0x41, 0x22, 0x2f, 0x3e, 0x3c, 0x2f, 0x73, 0x76, 0x67, 0x3e, 0xa}, MediaType: "image/svg+xml"}, Description: "A message about etcd operator, a description of channels"},
					{Schema: "olm.package", Name: "", DefaultChannel: "", Icon: nil, Description: ""},
				},
				Bundles: []Bundle{
					{
						Schema:  "olm.bundle",
						Name:    "cockroachdb.v2.0.9",
						Package: "cockroachdb",
						Image:   "quay.io/openshift-community-operators/cockroachdb:v2.0.9",
						Properties: []property.Property{
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"stable"}`)},
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"cockroachdb","version":"2.0.9"}`)},
						},
					},
					{
						Schema:  "olm.bundle",
						Name:    "cockroachdb.v2.1.11",
						Package: "cockroachdb",
						Image:   "quay.io/openshift-community-operators/cockroachdb:v2.1.11",
						Properties: []property.Property{
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"stable","replaces":"cockroachdb.v2.1.1"}`)},
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"cockroachdb","version":"2.1.11"}`)},
						},
					},
					{
						Schema:  "olm.bundle",
						Name:    "cockroachdb.v2.1.1",
						Package: "cockroachdb",
						Image:   "quay.io/openshift-community-operators/cockroachdb:v2.1.1",
						Properties: []property.Property{
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"stable","replaces":"cockroachdb.v2.0.9"}`)},
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"cockroachdb","version":"2.1.1"}`)},
						},
					},
					{
						Schema:  "olm.bundle",
						Name:    "cockroachdb.v3.0.7",
						Package: "cockroachdb",
						Image:   "quay.io/openshift-community-operators/cockroachdb:v3.0.7",
						Properties: []property.Property{
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"stable-3.x"}`)},
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"cockroachdb","version":"3.0.7"}`)},
						},
					},
					{
						Schema:  "olm.bundle",
						Name:    "cockroachdb.v5.0.3",
						Package: "cockroachdb",
						Image:   "quay.io/openshift-community-operators/cockroachdb:v5.0.3",
						Properties: []property.Property{
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"stable-5.x"}`)},
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"cockroachdb","version":"5.0.3"}`)},
						},
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator-community.v0.6.1",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.6.1",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.6.1"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdCluster","version":"v1beta2"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"alpha"}`)},
							{Type: "olm.skipRange", Value: json.RawMessage(`"<0.6.1"`)},
							{Type: "olm.bundle.object", Value: json.RawMessage(fmt.Sprintf(`{"data": %q}`, base64.StdEncoding.EncodeToString(etcdCSV.Data)))},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.6.1", Image: "quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943"}},
						Objects:       []string{toJSON(t, etcdCSV.Data)},
						CsvJSON:       toJSON(t, etcdCSV.Data),
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator.v0.9.0",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.9.0",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.9.0"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdBackup","version":"v1beta2"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"singlenamespace-alpha"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"clusterwide-alpha"}`)},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.9.0", Image: "quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8"}},
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator.v0.9.2",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.9.2",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.9.2"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdRestore","version":"v1beta2"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"singlenamespace-alpha","replaces":"etcdoperator.v0.9.0"}`)},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.9.2", Image: "quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2"}},
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator.v0.9.2-clusterwide",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.9.2-clusterwide",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.9.2-clusterwide"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdBackup","version":"v1beta2"}`)},
							{Type: "olm.skipRange", Value: json.RawMessage(`">=0.9.0 <=0.9.1"`)},
							{Type: "olm.skips", Value: json.RawMessage(`"etcdoperator.v0.6.1"`)},
							{Type: "olm.skips", Value: json.RawMessage(`"etcdoperator.v0.9.0"`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"clusterwide-alpha","replaces":"etcdoperator.v0.9.0"}`)},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.9.2", Image: "quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2"}},
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator.v0.9.4",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.9.4",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.9.4"}`)},
							{Type: "olm.package.required", Value: json.RawMessage(`{"packageName":"test","versionRange":">=1.2.3 <2.0.0-0"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdBackup","version":"v1beta2"}`)},
							{Type: "olm.gvk.required", Value: json.RawMessage(`{"group":"testapi.coreos.com","kind":"Testapi","version":"v1"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"singlenamespace-alpha","replaces":"etcdoperator.v0.9.2"}`)},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.9.2", Image: "quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b"}},
					},
					{
						Schema:  "olm.bundle",
						Name:    "etcdoperator.v0.9.4-clusterwide",
						Package: "etcd",
						Image:   "quay.io/operatorhubio/etcd:v0.9.4-clusterwide",
						Properties: []property.Property{
							{Type: "olm.package", Value: json.RawMessage(`{"packageName":"etcd","version":"0.9.4-clusterwide"}`)},
							{Type: "olm.gvk", Value: json.RawMessage(`{"group":"etcd.database.coreos.com","kind":"EtcdBackup","version":"v1beta2"}`)},
							{Type: "olm.channel", Value: json.RawMessage(`{"name":"clusterwide-alpha","replaces":"etcdoperator.v0.9.2-clusterwide"}`)},
						},
						RelatedImages: []RelatedImage{{Name: "etcdv0.9.2", Image: "quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b"}},
					},
					{
						Schema: "olm.bundle",
					},
				},
				Deprecations: []Deprecation{
					{
						Schema:  SchemaDeprecation,
						Package: "kiali",
						Entries: []DeprecationEntry{
							{Reference: PackageScopedReference{Schema: SchemaBundle, Name: "kiali-operator.v1.68.0"}, Message: "kiali-operator.v1.68.0 is deprecated. Uninstall and install kiali-operator.v1.72.0 for support.\n"},
							{Reference: PackageScopedReference{Schema: SchemaPackage}, Message: "package kiali is end of life.  Please use 'kiali-new' package for support.\n"},
							{Reference: PackageScopedReference{Schema: SchemaChannel, Name: "alpha"}, Message: "channel alpha is no longer supported.  Please switch to channel 'stable'.\n"},
						},
					},
				},
				Others: []Meta{
					{Schema: "unexpected", Package: "", Blob: json.RawMessage(`{ "schema":  "unexpected" }`)},
				},
			},
		},
	}

	for _, s := range specs {
		t.Run(s.name, func(t *testing.T) {
			cfg, err := LoadFS(context.Background(), s.fsys)
			s.assertion(t, err)
			if err == nil {
				require.NotNil(t, cfg)
				equalsDeclarativeConfig(t, *s.expected, *cfg)
			}
		})
	}
}

func toJSON(t *testing.T, in []byte) string {
	t.Helper()
	out, err := yaml.ToJSON(in)
	if err != nil {
		t.Fatalf("failed converting testdata to JSON: %v", err)
	}
	return string(out)
}

var (
	invalidBundle = &fstest.MapFile{
		Data: []byte(`{"schema": "olm.bundle","relatedImages": {}}`),
	}
	invalidPackage = &fstest.MapFile{
		Data: []byte(`{"schema": "olm.package","name": {}}`),
	}
	noSchema = &fstest.MapFile{
		Data: []byte(`hello: world`),
	}
	invalidFormat = &fstest.MapFile{
		Data: []byte(`[This is not yaml or json.}`),
	}
	notObject = &fstest.MapFile{
		Data: []byte(`[]`),
	}
	invalidFS = fstest.MapFS{
		"invalid-bundle.json":  invalidBundle,
		"invalid-package.json": invalidPackage,
		"no-schema.yaml":       noSchema,
		"invalid-format.txt":   invalidFormat,
		"not-object.json":      notObject,
	}

	indexIgnore = &fstest.MapFile{
		Data: []byte(`*
!*.json
!*.yaml

*.clusterserviceversion.yaml`),
	}
	cockroachdb = &fstest.MapFile{
		Data: []byte(`{
    "schema": "olm.package",
    "name": "cockroachdb",
    "defaultChannel": "stable-5.x",
    "icon": {
        "base64data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMS44MiAzMiIgd2lkdGg9IjI0ODYiIGhlaWdodD0iMjUwMCI+PHRpdGxlPkNMPC90aXRsZT48cGF0aCBkPSJNMTkuNDIgOS4xN2ExNS4zOSAxNS4zOSAwIDAgMS0zLjUxLjQgMTUuNDYgMTUuNDYgMCAwIDEtMy41MS0uNCAxNS42MyAxNS42MyAwIDAgMSAzLjUxLTMuOTEgMTUuNzEgMTUuNzEgMCAwIDEgMy41MSAzLjkxek0zMCAuNTdBMTcuMjIgMTcuMjIgMCAwIDAgMjUuNTkgMGExNy40IDE3LjQgMCAwIDAtOS42OCAyLjkzQTE3LjM4IDE3LjM4IDAgMCAwIDYuMjMgMGExNy4yMiAxNy4yMiAwIDAgMC00LjQ0LjU3QTE2LjIyIDE2LjIyIDAgMCAwIDAgMS4xM2EuMDcuMDcgMCAwIDAgMCAuMDkgMTcuMzIgMTcuMzIgMCAwIDAgLjgzIDEuNTcuMDcuMDcgMCAwIDAgLjA4IDAgMTYuMzkgMTYuMzkgMCAwIDEgMS44MS0uNTQgMTUuNjUgMTUuNjUgMCAwIDEgMTEuNTkgMS44OCAxNy41MiAxNy41MiAwIDAgMC0zLjc4IDQuNDhjLS4yLjMyLS4zNy42NS0uNTUgMXMtLjIyLjQ1LS4zMy42OS0uMzEuNzItLjQ0IDEuMDhhMTcuNDYgMTcuNDYgMCAwIDAgNC4yOSAxOC43Yy4yNi4yNS41My40OS44MS43M3MuNDQuMzcuNjcuNTQuNTkuNDQuODkuNjRhLjA3LjA3IDAgMCAwIC4wOCAwYy4zLS4yMS42LS40Mi44OS0uNjRzLjQ1LS4zNS42Ny0uNTQuNTUtLjQ4LjgxLS43M2ExNy40NSAxNy40NSAwIDAgMCA1LjM4LTEyLjYxIDE3LjM5IDE3LjM5IDAgMCAwLTEuMDktNi4wOWMtLjE0LS4zNy0uMjktLjczLS40NS0xLjA5cy0uMjItLjQ3LS4zMy0uNjktLjM1LS42Ni0uNTUtMWExNy42MSAxNy42MSAwIDAgMC0zLjc4LTQuNDggMTUuNjUgMTUuNjUgMCAwIDEgMTEuNi0xLjg0IDE2LjEzIDE2LjEzIDAgMCAxIDEuODEuNTQuMDcuMDcgMCAwIDAgLjA4IDBxLjQ0LS43Ni44Mi0xLjU2YS4wNy4wNyAwIDAgMCAwLS4wOUExNi44OSAxNi44OSAwIDAgMCAzMCAuNTd6IiBmaWxsPSIjMTUxZjM0Ii8+PHBhdGggZD0iTTIxLjgyIDE3LjQ3YTE1LjUxIDE1LjUxIDAgMCAxLTQuMjUgMTAuNjkgMTUuNjYgMTUuNjYgMCAwIDEtLjcyLTQuNjggMTUuNSAxNS41IDAgMCAxIDQuMjUtMTAuNjkgMTUuNjIgMTUuNjIgMCAwIDEgLjcyIDQuNjgiIGZpbGw9IiMzNDg1NDAiLz48cGF0aCBkPSJNMTUgMjMuNDhhMTUuNTUgMTUuNTUgMCAwIDEtLjcyIDQuNjggMTUuNTQgMTUuNTQgMCAwIDEtMy41My0xNS4zN0ExNS41IDE1LjUgMCAwIDEgMTUgMjMuNDgiIGZpbGw9IiM3ZGJjNDIiLz48L3N2Zz4=",
        "mediatype": "image/svg+xml"
    }
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v2.0.9",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v2.0.9",
    "properties": [
        {
            "type": "olm.channel",
            "value": {
                "name": "stable"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "2.0.9"
            }
        }
    ]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v2.1.11",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v2.1.11",
    "properties": [
        {
            "type": "olm.channel",
            "value": {
                "name": "stable",
                "replaces": "cockroachdb.v2.1.1"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "2.1.11"
            }
        }
    ]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v2.1.1",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v2.1.1",
    "properties": [
        {
            "type": "olm.channel",
            "value": {
                "name": "stable",
                "replaces": "cockroachdb.v2.0.9"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "2.1.1"
            }
        }
    ]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v3.0.7",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v3.0.7",
    "properties": [
        {
            "type": "olm.channel",
            "value": {
                "name": "stable-3.x"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "3.0.7"
            }
        }
    ]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v5.0.3",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v5.0.3",
    "properties": [
        {
            "type": "olm.channel",
            "value": {
                "name": "stable-5.x"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "5.0.3"
            }
        }
    ]
}`),
	}
	etcd = &fstest.MapFile{
		Data: []byte(fmt.Sprintf(`---
schema: olm.package
name: etcd
defaultChannel: singlenamespace-alpha
description: A message about etcd operator, a description of channels
icon:
  base64data: PHN2ZyB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIyNDIyIiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTI1Mi4zODYgMTI4LjA2NGMtMS4yMDIuMS0yLjQxLjE0Ny0zLjY5My4xNDctNy40NDYgMC0xNC42Ny0xLjc0Ni0yMS4xODctNC45NDQgMi4xNy0xMi40NDcgMy4wOTItMjQuOTg3IDIuODUtMzcuNDgxLTcuMDY1LTEwLjIyLTE1LjE0LTE5Ljg2My0yNC4yNTYtMjguNzQ3IDMuOTU1LTcuNDE1IDkuODAxLTEzLjc5NSAxNy4xLTE4LjMxOWwzLjEzMy0xLjkzNy0yLjQ0Mi0yLjc1NGMtMTIuNTgxLTE0LjE2Ny0yNy41OTYtMjUuMTItNDQuNjItMzIuNTUyTDE3NS44NzYgMGwtLjg2MiAzLjU4OGMtMi4wMyA4LjM2My02LjI3NCAxNS45MDgtMTIuMSAyMS45NjJhMTkzLjg0MiAxOTMuODQyIDAgMCAwLTM0Ljk1Ni0xNC40MDVBMTk0LjAxMiAxOTQuMDEyIDAgMCAwIDkzLjA1NiAyNS41MkM4Ny4yNTQgMTkuNDczIDgzLjAyIDExLjk0NyA4MC45OTkgMy42MDhMODAuMTMuMDJsLTMuMzgyIDEuNDdDNTkuOTM5IDguODE1IDQ0LjUxIDIwLjA2NSAzMi4xMzUgMzQuMDJsLTIuNDQ5IDIuNzYgMy4xMyAxLjkzN2M3LjI3NiA0LjUwNiAxMy4xMDYgMTAuODQ5IDE3LjA1NCAxOC4yMjMtOS4wODggOC44NS0xNy4xNTQgMTguNDYyLTI0LjIxNCAyOC42MzUtLjI3NSAxMi40ODkuNiAyNS4xMiAyLjc4IDM3Ljc0LTYuNDg0IDMuMTY3LTEzLjY2OCA0Ljg5NC0yMS4wNjUgNC44OTQtMS4yOTggMC0yLjUxMy0uMDQ3LTMuNjkzLS4xNDVMMCAxMjcuNzg1bC4zNDUgMy42NzFjMS44MDIgMTguNTc4IDcuNTcgMzYuMjQ3IDE3LjE1NCA1Mi41MjNsMS44NyAzLjE3NiAyLjgxLTIuMzg0YTQ4LjA0IDQ4LjA0IDAgMCAxIDIyLjczNy0xMC42NSAxOTQuODYgMTk0Ljg2IDAgMCAwIDE5LjQ2IDMxLjY5NmMxMS44MjggNC4xMzcgMjQuMTUxIDcuMjI1IDM2Ljg3OCA5LjA2MyAxLjIyIDguNDE3LjI0OCAxNy4xMjItMy4wNzIgMjUuMTcxbC0xLjQgMy40MTEgMy42Ljc5M2M5LjIyIDIuMDI3IDE4LjUyMyAzLjA2IDI3LjYzMSAzLjA2bDI3LjYyMy0zLjA2IDMuNjA0LS43OTMtMS40MDMtMy40MTdjLTMuMzEyLTguMDUtNC4yODQtMTYuNzY1LTMuMDYzLTI1LjE4MyAxMi42NzYtMS44NCAyNC45NTQtNC45MiAzNi43MzgtOS4wNDVhMTk1LjEwOCAxOTUuMTA4IDAgMCAwIDE5LjQ4Mi0zMS43MjYgNDguMjU0IDQ4LjI1NCAwIDAgMSAyMi44NDggMTAuNjZsMi44MDkgMi4zOCAxLjg2Mi0zLjE2OGM5LjYtMTYuMjk3IDE1LjM2OC0zMy45NjUgMTcuMTQyLTUyLjUxM2wuMzQ1LTMuNjY1LTMuNjE0LjI3OXpNMTY3LjQ5IDE3Mi45NmMtMTMuMDY4IDMuNTU0LTI2LjM0IDUuMzQ4LTM5LjUzMiA1LjM0OC0xMy4yMjggMC0yNi40ODMtMS43OTMtMzkuNTYzLTUuMzQ4YTE1My4yNTUgMTUzLjI1NSAwIDAgMS0xNi45MzItMzUuNjdjLTQuMDY2LTEyLjUxNy02LjQ0NS0yNS42My03LjEzNS0zOS4xMzQgOC40NDYtMTAuNDQzIDE4LjA1Mi0xOS41OTEgMjguNjY1LTI3LjI5M2ExNTIuNjIgMTUyLjYyIDAgMCAxIDM0Ljk2NS0xOS4wMTEgMTUzLjI0MiAxNTMuMjQyIDAgMCAxIDM0Ljg5OCAxOC45N2MxMC42NTQgNy43NDMgMjAuMzAyIDE2Ljk2MiAyOC43OSAyNy40Ny0uNzI0IDEzLjQyNy0zLjEzMiAyNi40NjUtNy4yMDQgMzguOTYxYTE1Mi43NjcgMTUyLjc2NyAwIDAgMS0xNi45NTIgMzUuNzA3em0tMjguNzQtNjIuOTk4YzAgOS4yMzIgNy40ODIgMTYuNyAxNi43MDIgMTYuNyA5LjIxNyAwIDE2LjY5LTcuNDY2IDE2LjY5LTE2LjcgMC05LjE5Ni03LjQ3My0xNi42OTItMTYuNjktMTYuNjkyLTkuMjIgMC0xNi43MDEgNy40OTYtMTYuNzAxIDE2LjY5MnptLTIxLjU3OCAwYzAgOS4yMzItNy40OCAxNi43LTE2LjcgMTYuNy05LjIyNiAwLTE2LjY4NS03LjQ2Ni0xNi42ODUtMTYuNyAwLTkuMTkzIDcuNDYtMTYuNjg5IDE2LjY4Ni0xNi42ODkgOS4yMiAwIDE2LjcgNy40OTYgMTYuNyAxNi42OXoiIGZpbGw9IiM0MTlFREEiLz48L3N2Zz4K
  mediatype: image/svg+xml

---
schema: olm.bundle
package: etcd
name: etcdoperator-community.v0.6.1
image: quay.io/operatorhubio/etcd:v0.6.1
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.6.1
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdCluster
      version: v1beta2
  - type: olm.channel
    value:
      name: alpha
  - type: olm.skipRange
    value: <0.6.1
  - type: olm.bundle.object
    value:
      data: %q
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943
    name: etcdv0.6.1

---
schema: olm.bundle
package: etcd
name: etcdoperator.v0.9.0
image: quay.io/operatorhubio/etcd:v0.9.0
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.9.0
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdBackup
      version: v1beta2
  - type: olm.channel
    value:
      name: singlenamespace-alpha
  - type: olm.channel
    value:
      name: clusterwide-alpha
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8
    name: etcdv0.9.0

---
schema: olm.bundle
package: etcd
name: etcdoperator.v0.9.2
image: quay.io/operatorhubio/etcd:v0.9.2
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.9.2
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdRestore
      version: v1beta2
  - type: olm.channel
    value:
      name: singlenamespace-alpha
      replaces: etcdoperator.v0.9.0
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2
    name: etcdv0.9.2

---
schema: olm.bundle
package: etcd
name: etcdoperator.v0.9.2-clusterwide
image: quay.io/operatorhubio/etcd:v0.9.2-clusterwide
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.9.2-clusterwide
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdBackup
      version: v1beta2
  - type: olm.skipRange
    value: '>=0.9.0 <=0.9.1'
  - type: olm.skips
    value: etcdoperator.v0.6.1
  - type: olm.skips
    value: etcdoperator.v0.9.0
  - type: olm.channel
    value:
      name: clusterwide-alpha
      replaces: etcdoperator.v0.9.0
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2
    name: etcdv0.9.2

---
schema: olm.bundle
package: etcd
name: etcdoperator.v0.9.4
image: quay.io/operatorhubio/etcd:v0.9.4
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.9.4
  - type: olm.package.required
    value:
      packageName: test
      versionRange: '>=1.2.3 <2.0.0-0'
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdBackup
      version: v1beta2
  - type: olm.gvk.required
    value:
      group: testapi.coreos.com
      kind: Testapi
      version: v1
  - type: olm.channel
    value:
      name: singlenamespace-alpha
      replaces: etcdoperator.v0.9.2
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b
    name: etcdv0.9.2

---
schema: olm.bundle
package: etcd
name: etcdoperator.v0.9.4-clusterwide
image: quay.io/operatorhubio/etcd:v0.9.4-clusterwide
properties:
  - type: olm.package
    value:
      packageName: etcd
      version: 0.9.4-clusterwide
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdBackup
      version: v1beta2
  - type: olm.channel
    value:
      name: clusterwide-alpha
      replaces: etcdoperator.v0.9.2-clusterwide
relatedImages:
  - image: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b
    name: etcdv0.9.2`, base64.StdEncoding.EncodeToString(etcdCSV.Data))),
	}
	etcdCSV = &fstest.MapFile{
		Data: []byte(`apiVersion: operators.coreos.com/v1alpha1
kind: ClusterServiceVersion
metadata:
  annotations:
    capabilities: Full Lifecycle
    description: etcd is a distributed key value store providing a reliable way to
      store data across a cluster of machines.
    tectonic-visibility: ocs
  name: etcdoperator.v0.6.1
  namespace: placeholder
spec:
  customresourcedefinitions:
    owned:
    - description: Represents a cluster of etcd nodes.
      displayName: etcd Cluster
      kind: EtcdCluster
      name: etcdclusters.etcd.database.coreos.com
      resources:
      - kind: Service
        version: v1
      - kind: Pod
        version: v1
      specDescriptors:
      - description: The desired number of member Pods for the etcd cluster.
        displayName: Size
        path: size
        x-descriptors:
        - urn:alm:descriptor:com.tectonic.ui:podCount
      statusDescriptors:
      - description: The status of each of the member Pods for the etcd cluster.
        displayName: Member Status
        path: members
        x-descriptors:
        - urn:alm:descriptor:com.tectonic.ui:podStatuses
      - description: The service at which the running etcd cluster can be accessed.
        displayName: Service
        path: service
        x-descriptors:
        - urn:alm:descriptor:io.kubernetes:Service
      - description: The current size of the etcd cluster.
        displayName: Cluster Size
        path: size
      - description: The current version of the etcd cluster.
        displayName: Current Version
        path: currentVersion
      - description: The target version of the etcd cluster, after upgrading.
        displayName: Target Version
        path: targetVersion
      - description: The current status of the etcd cluster.
        displayName: Status
        path: phase
        x-descriptors:
        - urn:alm:descriptor:io.kubernetes.phase
      - description: Explanation for the current status of the cluster.
        displayName: Status Details
        path: reason
        x-descriptors:
        - urn:alm:descriptor:io.kubernetes.phase:reason
      version: v1beta2
  description: "etcd is a distributed key value store that provides a reliable way\
    \ to store data across a cluster of machines. It\xE2\u20AC\u2122s open-source\
    \ and available on GitHub. etcd gracefully handles leader elections during network\
    \ partitions and will tolerate machine failure, including the leader. Your applications\
    \ can read and write data into etcd.\nA simple use-case is to store database connection\
    \ details or feature flags within etcd as key value pairs. These values can be\
    \ watched, allowing your app to reconfigure itself when they change. Advanced\
    \ uses take advantage of the consistency guarantees to implement database leader\
    \ elections or do distributed locking across a cluster of workers.\n\n_The etcd\
    \ Open Cloud Service is Public Alpha. The goal before Beta is to fully implement\
    \ backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd\
    \ though its command line utility ` + "`etcdctl`" + ` or with the API using the automatically\
    \ generated Kubernetes Service.\n\n[Read the complete guide to using the etcd\
    \ Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\
    \n### Supported Features\n**High availability**\nMultiple instances of etcd are\
    \ networked together and secured. Individual failures or networking issues are\
    \ transparently handled to keep your cluster up and running.\n**Automated updates**\n\
    Rolling out a new etcd version works like all Kubernetes rolling updates. Simply\
    \ declare the desired version, and the etcd service starts a safe rolling update\
    \ to the new version automatically.\n**Backups included**\nComing soon, the ability\
    \ to schedule backups to happen on or off cluster.\n"
  displayName: etcd
  icon:
  - base64data:  
    mediatype: image/png
  install:
    spec:
      deployments:
      - name: etcd-operator
        spec:
          replicas: 1
          selector:
            matchLabels:
              name: etcd-operator-alm-owned
          template:
            metadata:
              labels:
                name: etcd-operator-alm-owned
              name: etcd-operator-alm-owned
            spec:
              containers:
              - command:
                - etcd-operator
                - --create-crd=false
                env:
                - name: MY_POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: MY_POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                image: quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943
                name: etcd-operator
              serviceAccountName: etcd-operator
      permissions:
      - rules:
        - apiGroups:
          - etcd.database.coreos.com
          resources:
          - etcdclusters
          verbs:
          - '*'
        - apiGroups:
          - storage.k8s.io
          resources:
          - storageclasses
          verbs:
          - '*'
        - apiGroups:
          - ''
          resources:
          - pods
          - services
          - endpoints
          - persistentvolumeclaims
          - events
          verbs:
          - '*'
        - apiGroups:
          - apps
          resources:
          - deployments
          verbs:
          - '*'
        - apiGroups:
          - ''
          resources:
          - secrets
          verbs:
          - get
        serviceAccountName: etcd-operator
    strategy: deployment
  installModes:
  - supported: true
    type: OwnNamespace
  - supported: true
    type: SingleNamespace
  - supported: false
    type: MultiNamespace
  - supported: true
    type: AllNamespaces
  keywords:
  - etcd
  - key value
  - database
  - coreos
  - open source
  labels:
    alm-owner-etcd: etcdoperator
    alm-status-descriptors: etcdoperator.v0.6.1
    operated-by: etcdoperator
  links:
  - name: Blog
    url: https://coreos.com/etcd
  - name: Documentation
    url: https://coreos.com/operators/etcd/docs/latest/
  - name: etcd Operator Source Code
    url: https://github.com/coreos/etcd-operator
  maintainers:
  - email: support@coreos.com
    name: CoreOS, Inc
  maturity: alpha
  provider:
    name: CoreOS, Inc
  selector:
    matchLabels:
      alm-owner-etcd: etcdoperator
      operated-by: etcdoperator
  version: 0.6.1
`),
	}
	readme = &fstest.MapFile{
		Data: []byte(`# Valid Declarative Config

This is a README file about this declarative config. It should be ignored
when loading this directory as declarative config due to the patterns
present in the .indexignore file.`),
	}
	unrecognizedSchema = &fstest.MapFile{
		Data: []byte(`{"schema":"olm.package"}{"schema":"unexpected"}{"schema":"olm.bundle"}`),
	}
	deprecations = &fstest.MapFile{
		Data: []byte(`---
schema: olm.deprecations
package: kiali
entries:
- reference:
    schema: olm.bundle
    name: kiali-operator.v1.68.0
  message: |
     kiali-operator.v1.68.0 is deprecated. Uninstall and install kiali-operator.v1.72.0 for support.
- reference: 
    schema: olm.package
  message: |
     package kiali is end of life.  Please use 'kiali-new' package for support.
- reference:
    schema: olm.channel
    name: alpha
  message: |
       channel alpha is no longer supported.  Please switch to channel 'stable'.`),
	}

	validFS = fstest.MapFS{
		".indexignore":     indexIgnore,
		"cockroachdb.json": cockroachdb,
		"etcd.yaml":        etcd,
		"etcdoperator.v0.6.1.clusterserviceversion.yaml": etcdCSV,
		"README.md":                readme,
		"unrecognized-schema.json": unrecognizedSchema,
		"deprecations.yaml":        deprecations,
	}
)

type EvaluationFunc func(*testing.T, *DeclarativeConfig)

func TestLoadFile(t *testing.T) {
	type spec struct {
		name      string
		fsys      fs.FS
		path      string
		assertion require.ErrorAssertionFunc
		expect    EvaluationFunc
	}
	specs := []spec{
		{
			name:      "Error/NonExistentDir",
			fsys:      os.DirFS("non/existent/dir/"),
			assertion: require.Error,
		},
		{
			name:      "Error/Invalid",
			fsys:      invalidFS,
			assertion: require.Error,
		},
		{
			name:      "Error/NotYAMLOrJSON",
			fsys:      invalidFS,
			path:      "invalid-format.txt",
			assertion: require.Error,
		},
		{
			name:      "Error/NotJSONObject",
			fsys:      invalidFS,
			path:      "not-object.json",
			assertion: require.Error,
		},
		{
			name:      "Error/NoSchema",
			fsys:      invalidFS,
			path:      "no-schema.yaml",
			assertion: require.Error,
		},
		{
			name:      "Error/InvalidPackageJSON",
			fsys:      invalidFS,
			path:      "invalid-package.json",
			assertion: require.Error,
		},
		{
			name:      "Error/InvalidBundleJSON",
			fsys:      invalidFS,
			path:      "invalid-bundle.json",
			assertion: require.Error,
		},
		{
			name:      "Success/UnrecognizedSchema",
			fsys:      validFS,
			path:      "unrecognized-schema.json",
			assertion: require.NoError,
			expect: func(t *testing.T, d *DeclarativeConfig) {
				require.Len(t, d.Packages, 1)
				require.Len(t, d.Bundles, 1)
				require.Len(t, d.Others, 1)
			},
		},
		{
			name:      "Success/ValidFile",
			fsys:      validFS,
			path:      "etcd.yaml",
			assertion: require.NoError,
			expect: func(t *testing.T, d *DeclarativeConfig) {
				require.Len(t, d.Packages, 1)
				require.Len(t, d.Bundles, 6)
				require.Empty(t, d.Others)
			},
		},
		{
			name:      "Success/ValidFile/Deprecations",
			fsys:      validFS,
			path:      "deprecations.yaml",
			assertion: require.NoError,
			expect: func(t *testing.T, d *DeclarativeConfig) {
				require.Empty(t, d.Packages)
				require.Empty(t, d.Bundles)
				require.Empty(t, d.Others)
				require.Len(t, d.Deprecations, 1)
			},
		},
	}

	for _, s := range specs {
		t.Run(s.name, func(t *testing.T) {
			cfg, err := LoadFile(s.fsys, s.path)
			s.assertion(t, err)
			if err == nil {
				require.NotNil(t, cfg)
				s.expect(t, cfg)
			}
		})
	}
}
