package cache

import (
	"context"
	"encoding/json"
	"io/fs"
	"testing"
	"testing/fstest"

	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/types/known/structpb"

	"github.com/operator-framework/operator-registry/pkg/lib/log"
	"github.com/operator-framework/operator-registry/pkg/registry"
)

func TestCache_GetBundle(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			b, err := testQuerier.GetBundle(context.TODO(), "etcd", "singlenamespace-alpha", "etcdoperator.v0.9.4")
			require.NoError(t, err)
			require.Equal(t, "etcd", b.PackageName)
			require.Equal(t, "singlenamespace-alpha", b.ChannelName)
			require.Equal(t, "etcdoperator.v0.9.4", b.CsvName)
		})
	}
}

func TestCache_GetBundleForChannel(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			b, err := testQuerier.GetBundleForChannel(context.TODO(), "etcd", "singlenamespace-alpha")

			require.NoError(t, err)
			require.NotNil(t, b)
			require.Equal(t, "etcd", b.PackageName)
			require.Equal(t, "singlenamespace-alpha", b.ChannelName)
			require.Equal(t, "etcdoperator.v0.9.4", b.CsvName)
		})
	}
}

func TestCache_GetBundleThatProvides(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			b, err := testQuerier.GetBundleThatProvides(context.TODO(), "etcd.database.coreos.com", "v1beta2", "EtcdBackup")
			require.NoError(t, err)
			require.NotNil(t, b)
			require.Equal(t, "etcd", b.PackageName)
			require.Equal(t, "singlenamespace-alpha", b.ChannelName)
			require.Equal(t, "etcdoperator.v0.9.4", b.CsvName)
		})
	}
}

func TestCache_GetBundleThatReplaces(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			b, err := testQuerier.GetBundleThatReplaces(context.TODO(), "etcdoperator.v0.9.0", "etcd", "singlenamespace-alpha")
			require.NoError(t, err)
			require.NotNil(t, b)
			require.Equal(t, "etcd", b.PackageName)
			require.Equal(t, "singlenamespace-alpha", b.ChannelName)
			require.Equal(t, "etcdoperator.v0.9.2", b.CsvName)
		})
	}
}

func TestCache_GetChannelEntriesThatProvide(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			entries, err := testQuerier.GetChannelEntriesThatProvide(context.TODO(), "etcd.database.coreos.com", "v1beta2", "EtcdBackup")
			require.NoError(t, err)
			require.NotNil(t, entries)
			require.ElementsMatch(t, []*registry.ChannelEntry{
				{
					PackageName: "etcd",
					ChannelName: "singlenamespace-alpha",
					BundleName:  "etcdoperator.v0.9.0",
					Replaces:    "",
				},
				{
					PackageName: "etcd",
					ChannelName: "singlenamespace-alpha",
					BundleName:  "etcdoperator.v0.9.4",
					Replaces:    "etcdoperator.v0.9.2",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.0",
					Replaces:    "",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.2-clusterwide",
					Replaces:    "etcdoperator.v0.9.0",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.2-clusterwide",
					Replaces:    "etcdoperator.v0.6.1",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.4-clusterwide",
					Replaces:    "etcdoperator.v0.9.2-clusterwide",
				},
			}, entries)
		})
	}
}

func TestCache_GetChannelEntriesThatReplace(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			entries, err := testQuerier.GetChannelEntriesThatReplace(context.TODO(), "etcdoperator.v0.9.0")
			require.NoError(t, err)
			require.NotNil(t, entries)
			require.ElementsMatch(t, []*registry.ChannelEntry{
				{
					PackageName: "etcd",
					ChannelName: "singlenamespace-alpha",
					BundleName:  "etcdoperator.v0.9.2",
					Replaces:    "etcdoperator.v0.9.0",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.2-clusterwide",
					Replaces:    "etcdoperator.v0.9.0",
				},
			}, entries)
		})
	}
}

func TestCache_GetLatestChannelEntriesThatProvide(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			entries, err := testQuerier.GetLatestChannelEntriesThatProvide(context.TODO(), "etcd.database.coreos.com", "v1beta2", "EtcdBackup")
			require.NoError(t, err)
			require.NotNil(t, entries)
			require.ElementsMatch(t, []*registry.ChannelEntry{
				{
					PackageName: "etcd",
					ChannelName: "singlenamespace-alpha",
					BundleName:  "etcdoperator.v0.9.4",
					Replaces:    "etcdoperator.v0.9.2",
				},
				{
					PackageName: "etcd",
					ChannelName: "clusterwide-alpha",
					BundleName:  "etcdoperator.v0.9.4-clusterwide",
					Replaces:    "etcdoperator.v0.9.2-clusterwide",
				},
			}, entries)
		})
	}
}

func TestCache_GetPackage(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			p, err := testQuerier.GetPackage(context.TODO(), "etcd")
			require.NoError(t, err)
			require.NotNil(t, p)

			expected := &registry.PackageManifest{
				PackageName:        "etcd",
				DefaultChannelName: "singlenamespace-alpha",
				Channels: []registry.PackageChannel{
					{
						Name:           "singlenamespace-alpha",
						CurrentCSVName: "etcdoperator.v0.9.4",
					},
					{
						Name:           "clusterwide-alpha",
						CurrentCSVName: "etcdoperator.v0.9.4-clusterwide",
					},
					{
						Name:           "alpha",
						CurrentCSVName: "etcdoperator-community.v0.6.1",
					},
				},
			}

			require.ElementsMatch(t, expected.Channels, p.Channels)
			expected.Channels, p.Channels = nil, nil
			require.Equal(t, expected, p)
		})
	}
}

func TestCache_ListBundles(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			bundles, err := testQuerier.ListBundles(context.TODO())
			require.NoError(t, err)
			require.NotNil(t, bundles)
			require.Len(t, bundles, 12)
			for _, b := range bundles {
				require.Empty(t, b.CsvJson)
				require.Empty(t, b.Object)
			}
		})
	}
}

func TestCache_ListPackages(t *testing.T) {
	for name, testQuerier := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			packages, err := testQuerier.ListPackages(context.TODO())
			require.NoError(t, err)
			require.NotNil(t, packages)
			require.Len(t, packages, 2)
		})
	}
}

var customSchemaFS = fstest.MapFS{
	".": &fstest.MapFile{
		Mode: fs.ModeDir,
	},
	"catalog.json": &fstest.MapFile{
		Data: []byte(`{
    "schema": "olm.package",
    "name": "testpkg",
    "defaultChannel": "stable"
}
{
    "schema": "olm.channel",
    "package": "testpkg",
    "name": "stable",
    "entries": [{"name": "testpkg.v1.0.0"}]
}
{
    "schema": "olm.bundle",
    "name": "testpkg.v1.0.0",
    "package": "testpkg",
    "image": "quay.io/test/testpkg:v1.0.0",
    "properties": [{"type": "olm.package", "value": {"packageName": "testpkg", "version": "1.0.0"}}]
}
{
    "schema": "custom.operator.io",
    "package": "testpkg",
    "name": "my-custom-resource",
    "customField": "customValue"
}
{
    "schema": "custom.operator.io",
    "package": "testpkg",
    "name": "another-custom-resource",
    "data": {"key": "value"}
}
{
    "schema": "other.custom.schema",
    "package": "testpkg",
    "name": "other-custom",
    "info": "test"
}
`),
	},
}

func TestCache_ListPackageCustomSchemas(t *testing.T) {
	for name, testCache := range genTestCaches(t, customSchemaFS) {
		t.Run(name, func(t *testing.T) {
			collect := func(schema, pkg string) []string {
				t.Helper()
				var blobs []string
				err := testCache.ListPackageCustomSchemas(context.TODO(), schema, pkg,
					func(st *structpb.Struct) error {
						b, err := json.Marshal(st.AsMap())
						require.NoError(t, err)
						blobs = append(blobs, string(b))
						return nil
					})
				require.NoError(t, err)
				return blobs
			}

			// Multiple blobs for same (schema, package)
			blobs := collect("custom.operator.io", "testpkg")
			require.Len(t, blobs, 2)

			// Different schema
			blobs = collect("other.custom.schema", "testpkg")
			require.Len(t, blobs, 1)
			require.Contains(t, blobs[0], `"info"`)

			// Nonexistent schema returns empty
			blobs = collect("nonexistent.schema", "testpkg")
			require.Empty(t, blobs)

			// Nonexistent package returns empty
			blobs = collect("custom.operator.io", "nonexistent")
			require.Empty(t, blobs)
		})
	}
}

func TestCache_ListPackageCustomSchemas_PackagelessBlob(t *testing.T) {
	packagelessFS := fstest.MapFS{
		".": &fstest.MapFile{
			Mode: fs.ModeDir,
		},
		"catalog.json": &fstest.MapFile{
			Data: []byte(`{
    "schema": "olm.package",
    "name": "testpkg",
    "defaultChannel": "stable"
}
{
    "schema": "olm.channel",
    "package": "testpkg",
    "name": "stable",
    "entries": [{"name": "testpkg.v1.0.0"}]
}
{
    "schema": "olm.bundle",
    "name": "testpkg.v1.0.0",
    "package": "testpkg",
    "image": "quay.io/test/testpkg:v1.0.0",
    "properties": [{"type": "olm.package", "value": {"packageName": "testpkg", "version": "1.0.0"}}]
}
{
    "schema": "custom.packageless",
    "data": "no-package-blob"
}
`),
		},
	}
	for name, testCache := range genTestCaches(t, packagelessFS) {
		t.Run(name, func(t *testing.T) {
			// Querying with empty packageName returns packageless blobs
			var blobs []string
			err := testCache.ListPackageCustomSchemas(context.TODO(), "custom.packageless", "",
				func(st *structpb.Struct) error {
					b, _ := json.Marshal(st.AsMap())
					blobs = append(blobs, string(b))
					return nil
				})
			require.NoError(t, err)
			require.Len(t, blobs, 1)
			require.Contains(t, blobs[0], `"no-package-blob"`)

			// Querying with a specific packageName does not return packageless blobs
			var blobs2 []string
			err = testCache.ListPackageCustomSchemas(context.TODO(), "custom.packageless", "testpkg",
				func(st *structpb.Struct) error {
					b, _ := json.Marshal(st.AsMap())
					blobs2 = append(blobs2, string(b))
					return nil
				})
			require.NoError(t, err)
			require.Empty(t, blobs2)

			// Packageless blobs must not leak into the package index
			packages, err := testCache.ListPackages(context.TODO())
			require.NoError(t, err)
			require.Equal(t, []string{"testpkg"}, packages)
		})
	}
}

func TestCache_ListPackageCustomSchemas_NoCustomSchemas(t *testing.T) {
	for name, testCache := range genTestCaches(t, validFS) {
		t.Run(name, func(t *testing.T) {
			count := 0
			err := testCache.ListPackageCustomSchemas(context.TODO(), "custom.operator.io", "cockroachdb",
				func(st *structpb.Struct) error {
					count++
					return nil
				})
			require.NoError(t, err)
			require.Zero(t, count)
		})
	}
}

func genTestCaches(t *testing.T, fbcFS fs.FS) map[string]Cache {
	t.Helper()

	caches := make(map[string]Cache)
	for _, format := range []string{FormatJSON, FormatPogrebV1} {
		c, err := New(t.TempDir(), WithFormat(format), WithLog(log.Null()))
		require.NoError(t, err)
		caches[format] = c
	}

	for _, c := range caches {
		err := c.Build(context.Background(), fbcFS)
		require.NoError(t, err)
		err = c.Load(context.Background())
		require.NoError(t, err)
	}

	return caches
}

var validFS = fstest.MapFS{
	".": &fstest.MapFile{
		Mode: fs.ModeDir,
	},
	"cockroachdb.json": &fstest.MapFile{
		Data: []byte(`{
    "schema": "olm.package",
    "name": "cockroachdb",
    "defaultChannel": "stable-5.x",
    "icon": {
        "base64data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMS44MiAzMiIgd2lkdGg9IjI0ODYiIGhlaWdodD0iMjUwMCI+PHRpdGxlPkNMPC90aXRsZT48cGF0aCBkPSJNMTkuNDIgOS4xN2ExNS4zOSAxNS4zOSAwIDAgMS0zLjUxLjQgMTUuNDYgMTUuNDYgMCAwIDEtMy41MS0uNCAxNS42MyAxNS42MyAwIDAgMSAzLjUxLTMuOTEgMTUuNzEgMTUuNzEgMCAwIDEgMy41MSAzLjkxek0zMCAuNTdBMTcuMjIgMTcuMjIgMCAwIDAgMjUuNTkgMGExNy40IDE3LjQgMCAwIDAtOS42OCAyLjkzQTE3LjM4IDE3LjM4IDAgMCAwIDYuMjMgMGExNy4yMiAxNy4yMiAwIDAgMC00LjQ0LjU3QTE2LjIyIDE2LjIyIDAgMCAwIDAgMS4xM2EuMDcuMDcgMCAwIDAgMCAuMDkgMTcuMzIgMTcuMzIgMCAwIDAgLjgzIDEuNTcuMDcuMDcgMCAwIDAgLjA4IDAgMTYuMzkgMTYuMzkgMCAwIDEgMS44MS0uNTQgMTUuNjUgMTUuNjUgMCAwIDEgMTEuNTkgMS44OCAxNy41MiAxNy41MiAwIDAgMC0zLjc4IDQuNDhjLS4yLjMyLS4zNy42NS0uNTUgMXMtLjIyLjQ1LS4zMy42OS0uMzEuNzItLjQ0IDEuMDhhMTcuNDYgMTcuNDYgMCAwIDAgNC4yOSAxOC43Yy4yNi4yNS41My40OS44MS43M3MuNDQuMzcuNjcuNTQuNTkuNDQuODkuNjRhLjA3LjA3IDAgMCAwIC4wOCAwYy4zLS4yMS42LS40Mi44OS0uNjRzLjQ1LS4zNS42Ny0uNTQuNTUtLjQ4LjgxLS43M2ExNy40NSAxNy40NSAwIDAgMCA1LjM4LTEyLjYxIDE3LjM5IDE3LjM5IDAgMCAwLTEuMDktNi4wOWMtLjE0LS4zNy0uMjktLjczLS40NS0xLjA5cy0uMjItLjQ3LS4zMy0uNjktLjM1LS42Ni0uNTUtMWExNy42MSAxNy42MSAwIDAgMC0zLjc4LTQuNDggMTUuNjUgMTUuNjUgMCAwIDEgMTEuNi0xLjg0IDE2LjEzIDE2LjEzIDAgMCAxIDEuODEuNTQuMDcuMDcgMCAwIDAgLjA4IDBxLjQ0LS43Ni44Mi0xLjU2YS4wNy4wNyAwIDAgMCAwLS4wOUExNi44OSAxNi44OSAwIDAgMCAzMCAuNTd6IiBmaWxsPSIjMTUxZjM0Ii8+PHBhdGggZD0iTTIxLjgyIDE3LjQ3YTE1LjUxIDE1LjUxIDAgMCAxLTQuMjUgMTAuNjkgMTUuNjYgMTUuNjYgMCAwIDEtLjcyLTQuNjggMTUuNSAxNS41IDAgMCAxIDQuMjUtMTAuNjkgMTUuNjIgMTUuNjIgMCAwIDEgLjcyIDQuNjgiIGZpbGw9IiMzNDg1NDAiLz48cGF0aCBkPSJNMTUgMjMuNDhhMTUuNTUgMTUuNTUgMCAwIDEtLjcyIDQuNjggMTUuNTQgMTUuNTQgMCAwIDEtMy41My0xNS4zN0ExNS41IDE1LjUgMCAwIDEgMTUgMjMuNDgiIGZpbGw9IiM3ZGJjNDIiLz48L3N2Zz4=",
        "mediatype": "image/svg+xml"
    }
}
{
	"schema": "olm.channel",
	"package": "cockroachdb",
	"name": "stable",
	"entries": [
		{"name": "cockroachdb.v2.0.9"},
		{"name": "cockroachdb.v2.1.1", "replaces": "cockroachdb.v2.0.9"},
		{"name": "cockroachdb.v2.1.11", "replaces": "cockroachdb.v2.1.1"}
	]
}
{
	"schema": "olm.channel",
	"package": "cockroachdb",
	"name": "stable-3.x",
	"entries": [
		{"name": "cockroachdb.v3.0.7"}
	]
}
{
	"schema": "olm.channel",
	"package": "cockroachdb",
	"name": "stable-5.x",
	"entries": [
		{"name": "cockroachdb.v5.0.3"}
	]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v2.0.9",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v2.0.9",
    "properties": [
        {
            "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.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.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.json": &fstest.MapFile{
		Data: []byte(`{
    "schema": "olm.package",
    "name": "etcd",
    "defaultChannel": "singlenamespace-alpha",
    "icon": {
        "base64data": "PHN2ZyB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIyNDIyIiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTI1Mi4zODYgMTI4LjA2NGMtMS4yMDIuMS0yLjQxLjE0Ny0zLjY5My4xNDctNy40NDYgMC0xNC42Ny0xLjc0Ni0yMS4xODctNC45NDQgMi4xNy0xMi40NDcgMy4wOTItMjQuOTg3IDIuODUtMzcuNDgxLTcuMDY1LTEwLjIyLTE1LjE0LTE5Ljg2My0yNC4yNTYtMjguNzQ3IDMuOTU1LTcuNDE1IDkuODAxLTEzLjc5NSAxNy4xLTE4LjMxOWwzLjEzMy0xLjkzNy0yLjQ0Mi0yLjc1NGMtMTIuNTgxLTE0LjE2Ny0yNy41OTYtMjUuMTItNDQuNjItMzIuNTUyTDE3NS44NzYgMGwtLjg2MiAzLjU4OGMtMi4wMyA4LjM2My02LjI3NCAxNS45MDgtMTIuMSAyMS45NjJhMTkzLjg0MiAxOTMuODQyIDAgMCAwLTM0Ljk1Ni0xNC40MDVBMTk0LjAxMiAxOTQuMDEyIDAgMCAwIDkzLjA1NiAyNS41MkM4Ny4yNTQgMTkuNDczIDgzLjAyIDExLjk0NyA4MC45OTkgMy42MDhMODAuMTMuMDJsLTMuMzgyIDEuNDdDNTkuOTM5IDguODE1IDQ0LjUxIDIwLjA2NSAzMi4xMzUgMzQuMDJsLTIuNDQ5IDIuNzYgMy4xMyAxLjkzN2M3LjI3NiA0LjUwNiAxMy4xMDYgMTAuODQ5IDE3LjA1NCAxOC4yMjMtOS4wODggOC44NS0xNy4xNTQgMTguNDYyLTI0LjIxNCAyOC42MzUtLjI3NSAxMi40ODkuNiAyNS4xMiAyLjc4IDM3Ljc0LTYuNDg0IDMuMTY3LTEzLjY2OCA0Ljg5NC0yMS4wNjUgNC44OTQtMS4yOTggMC0yLjUxMy0uMDQ3LTMuNjkzLS4xNDVMMCAxMjcuNzg1bC4zNDUgMy42NzFjMS44MDIgMTguNTc4IDcuNTcgMzYuMjQ3IDE3LjE1NCA1Mi41MjNsMS44NyAzLjE3NiAyLjgxLTIuMzg0YTQ4LjA0IDQ4LjA0IDAgMCAxIDIyLjczNy0xMC42NSAxOTQuODYgMTk0Ljg2IDAgMCAwIDE5LjQ2IDMxLjY5NmMxMS44MjggNC4xMzcgMjQuMTUxIDcuMjI1IDM2Ljg3OCA5LjA2MyAxLjIyIDguNDE3LjI0OCAxNy4xMjItMy4wNzIgMjUuMTcxbC0xLjQgMy40MTEgMy42Ljc5M2M5LjIyIDIuMDI3IDE4LjUyMyAzLjA2IDI3LjYzMSAzLjA2bDI3LjYyMy0zLjA2IDMuNjA0LS43OTMtMS40MDMtMy40MTdjLTMuMzEyLTguMDUtNC4yODQtMTYuNzY1LTMuMDYzLTI1LjE4MyAxMi42NzYtMS44NCAyNC45NTQtNC45MiAzNi43MzgtOS4wNDVhMTk1LjEwOCAxOTUuMTA4IDAgMCAwIDE5LjQ4Mi0zMS43MjYgNDguMjU0IDQ4LjI1NCAwIDAgMSAyMi44NDggMTAuNjZsMi44MDkgMi4zOCAxLjg2Mi0zLjE2OGM5LjYtMTYuMjk3IDE1LjM2OC0zMy45NjUgMTcuMTQyLTUyLjUxM2wuMzQ1LTMuNjY1LTMuNjE0LjI3OXpNMTY3LjQ5IDE3Mi45NmMtMTMuMDY4IDMuNTU0LTI2LjM0IDUuMzQ4LTM5LjUzMiA1LjM0OC0xMy4yMjggMC0yNi40ODMtMS43OTMtMzkuNTYzLTUuMzQ4YTE1My4yNTUgMTUzLjI1NSAwIDAgMS0xNi45MzItMzUuNjdjLTQuMDY2LTEyLjUxNy02LjQ0NS0yNS42My03LjEzNS0zOS4xMzQgOC40NDYtMTAuNDQzIDE4LjA1Mi0xOS41OTEgMjguNjY1LTI3LjI5M2ExNTIuNjIgMTUyLjYyIDAgMCAxIDM0Ljk2NS0xOS4wMTEgMTUzLjI0MiAxNTMuMjQyIDAgMCAxIDM0Ljg5OCAxOC45N2MxMC42NTQgNy43NDMgMjAuMzAyIDE2Ljk2MiAyOC43OSAyNy40Ny0uNzI0IDEzLjQyNy0zLjEzMiAyNi40NjUtNy4yMDQgMzguOTYxYTE1Mi43NjcgMTUyLjc2NyAwIDAgMS0xNi45NTIgMzUuNzA3em0tMjguNzQtNjIuOTk4YzAgOS4yMzIgNy40ODIgMTYuNyAxNi43MDIgMTYuNyA5LjIxNyAwIDE2LjY5LTcuNDY2IDE2LjY5LTE2LjcgMC05LjE5Ni03LjQ3My0xNi42OTItMTYuNjktMTYuNjkyLTkuMjIgMC0xNi43MDEgNy40OTYtMTYuNzAxIDE2LjY5MnptLTIxLjU3OCAwYzAgOS4yMzItNy40OCAxNi43LTE2LjcgMTYuNy05LjIyNiAwLTE2LjY4NS03LjQ2Ni0xNi42ODUtMTYuNyAwLTkuMTkzIDcuNDYtMTYuNjg5IDE2LjY4Ni0xNi42ODkgOS4yMiAwIDE2LjcgNy40OTYgMTYuNyAxNi42OXoiIGZpbGw9IiM0MTlFREEiLz48L3N2Zz4K",
        "mediatype": "image/svg+xml"
    },
    "description": "A message about etcd operator, a description of channels"
}
{
	"schema": "olm.channel",
	"package": "etcd",
	"name": "alpha",
	"entries": [
		{"name": "etcdoperator-community.v0.6.1"}
	]
}
{
	"schema": "olm.channel",
	"package": "etcd",
	"name": "singlenamespace-alpha",
	"entries": [
		{"name": "etcdoperator.v0.9.0"},
		{"name": "etcdoperator.v0.9.2", "replaces": "etcdoperator.v0.9.0"},
		{"name": "etcdoperator.v0.9.4", "replaces": "etcdoperator.v0.9.2"}
	]
}
{
	"schema": "olm.channel",
	"package": "etcd",
	"name": "clusterwide-alpha",
	"entries": [
		{"name": "etcdoperator.v0.9.0"},
		{"name": "etcdoperator.v0.9.2-clusterwide", "replaces": "etcdoperator.v0.9.0", "skips": ["etcdoperator.v0.6.1","etcdoperator.v0.9.0"], "skipRange": ">=0.9.0 <=0.9.1"},
		{"name": "etcdoperator.v0.9.4-clusterwide", "replaces": "etcdoperator.v0.9.2-clusterwide"}
	]
}
{
    "schema": "olm.bundle",
    "name": "etcdoperator-community.v0.6.1",
    "package": "etcd",
    "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"
            }
        }
    ],
    "relatedImages": [
        {
            "name": "etcdv0.6.1",
            "image": "quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943"
        }
    ]
}
{
    "schema": "olm.bundle",
    "name": "etcdoperator.v0.9.0",
    "package": "etcd",
    "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"
            }
        }
    ],
    "relatedImages" : [
        {
            "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":[
        {
            "type": "olm.package",
            "value": {
                "packageName": "etcd",
                "version": "0.9.2"
            }
        },
        {
            "type":"olm.gvk",
            "value":{
                "group": "etcd.database.coreos.com",
                "kind": "EtcdRestore",
                "version": "v1beta2"
            }
        }
    ],
    "relatedImages":[
        {
            "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":[
        {
            "type": "olm.package",
            "value": {
                "packageName": "etcd",
                "version": "0.9.2-clusterwide"
            }
        },
        {
            "type": "olm.gvk",
            "value": {
                "group": "etcd.database.coreos.com",
                "kind": "EtcdBackup",
                "version": "v1beta2"
            }
        }
    ],
    "relatedImages":[
        {
            "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":[
        {
            "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"
            }
        }
    ],
    "relatedImages":[
        {
            "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":[
        {
            "type": "olm.package",
            "value": {
                "packageName": "etcd",
                "version": "0.9.4-clusterwide"
            }
        },
        {
            "type": "olm.gvk",
            "value": {
                "group": "etcd.database.coreos.com",
                "kind": "EtcdBackup",
                "version": "v1beta2"
            }
        }
    ],
    "relatedImages":[
        {
            "name":"etcdv0.9.2",
            "image": "quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b"
        }
    ]
}`),
	},
}

var badBundleFS = fstest.MapFS{
	"cockroachdb.json": &fstest.MapFile{
		Data: []byte(`{
    "schema": "olm.package",
    "name": "cockroachdb",
    "defaultChannel": "stable-5.x",
    "icon": {
        "base64data": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMS44MiAzMiIgd2lkdGg9IjI0ODYiIGhlaWdodD0iMjUwMCI+PHRpdGxlPkNMPC90aXRsZT48cGF0aCBkPSJNMTkuNDIgOS4xN2ExNS4zOSAxNS4zOSAwIDAgMS0zLjUxLjQgMTUuNDYgMTUuNDYgMCAwIDEtMy41MS0uNCAxNS42MyAxNS42MyAwIDAgMSAzLjUxLTMuOTEgMTUuNzEgMTUuNzEgMCAwIDEgMy41MSAzLjkxek0zMCAuNTdBMTcuMjIgMTcuMjIgMCAwIDAgMjUuNTkgMGExNy40IDE3LjQgMCAwIDAtOS42OCAyLjkzQTE3LjM4IDE3LjM4IDAgMCAwIDYuMjMgMGExNy4yMiAxNy4yMiAwIDAgMC00LjQ0LjU3QTE2LjIyIDE2LjIyIDAgMCAwIDAgMS4xM2EuMDcuMDcgMCAwIDAgMCAuMDkgMTcuMzIgMTcuMzIgMCAwIDAgLjgzIDEuNTcuMDcuMDcgMCAwIDAgLjA4IDAgMTYuMzkgMTYuMzkgMCAwIDEgMS44MS0uNTQgMTUuNjUgMTUuNjUgMCAwIDEgMTEuNTkgMS44OCAxNy41MiAxNy41MiAwIDAgMC0zLjc4IDQuNDhjLS4yLjMyLS4zNy42NS0uNTUgMXMtLjIyLjQ1LS4zMy42OS0uMzEuNzItLjQ0IDEuMDhhMTcuNDYgMTcuNDYgMCAwIDAgNC4yOSAxOC43Yy4yNi4yNS41My40OS44MS43M3MuNDQuMzcuNjcuNTQuNTkuNDQuODkuNjRhLjA3LjA3IDAgMCAwIC4wOCAwYy4zLS4yMS42LS40Mi44OS0uNjRzLjQ1LS4zNS42Ny0uNTQuNTUtLjQ4LjgxLS43M2ExNy40NSAxNy40NSAwIDAgMCA1LjM4LTEyLjYxIDE3LjM5IDE3LjM5IDAgMCAwLTEuMDktNi4wOWMtLjE0LS4zNy0uMjktLjczLS40NS0xLjA5cy0uMjItLjQ3LS4zMy0uNjktLjM1LS42Ni0uNTUtMWExNy42MSAxNy42MSAwIDAgMC0zLjc4LTQuNDggMTUuNjUgMTUuNjUgMCAwIDEgMTEuNi0xLjg0IDE2LjEzIDE2LjEzIDAgMCAxIDEuODEuNTQuMDcuMDcgMCAwIDAgLjA4IDBxLjQ0LS43Ni44Mi0xLjU2YS4wNy4wNyAwIDAgMCAwLS4wOUExNi44OSAxNi44OSAwIDAgMCAzMCAuNTd6IiBmaWxsPSIjMTUxZjM0Ii8+PHBhdGggZD0iTTIxLjgyIDE3LjQ3YTE1LjUxIDE1LjUxIDAgMCAxLTQuMjUgMTAuNjkgMTUuNjYgMTUuNjYgMCAwIDEtLjcyLTQuNjggMTUuNSAxNS41IDAgMCAxIDQuMjUtMTAuNjkgMTUuNjIgMTUuNjIgMCAwIDEgLjcyIDQuNjgiIGZpbGw9IiMzNDg1NDAiLz48cGF0aCBkPSJNMTUgMjMuNDhhMTUuNTUgMTUuNTUgMCAwIDEtLjcyIDQuNjggMTUuNTQgMTUuNTQgMCAwIDEtMy41My0xNS4zN0ExNS41IDE1LjUgMCAwIDEgMTUgMjMuNDgiIGZpbGw9IiM3ZGJjNDIiLz48L3N2Zz4=",
        "mediatype": "image/svg+xml"
    }
}
{
	"schema": "olm.channel",
	"package": "cockroachdb",
	"name": "stable-5.x",
	"entries": [
		{"name": "cockroachdb.v5.0.3"}
	]
}
{
    "schema": "olm.bundle",
    "name": "cockroachdb.v5.0.3",
    "package": "cockroachdb",
    "image": "quay.io/openshift-community-operators/cockroachdb:v5.0.3",
    "properties": [
        {
            "type": "olm.package",
            "value": {
                "packageName": "cockroachdb",
                "version": "5.0.3"
            }
        },
        {
            "type": "olm.package",
            "value": {
                "packageName": "other-package",
                "version": "5.0.3"
            }
        }
    ]
}`)}}
