/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/

package sums

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCreateParse(t *testing.T) {
	testCases := map[string]struct {
		refs     []PinnedImageReference
		wantErr  bool
		wantOut  string
		wantRefs []PinnedImageReference
	}{
		"single image": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v1.2.3\n",
		},
		"no prefix": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/foo-service:v1.2.3\n",
		},
		"no tag": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service\n",
		},
		"multiple images": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "bar-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "production",
					Name:     "baz-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v2.0.0",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "backup.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantRefs: []PinnedImageReference{
				{
					Registry: "backup.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "production",
					Name:     "baz-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "bar-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v2.0.0",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  backup.example.com/staging/foo-service:v1.2.3\n" +
				"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/production/baz-service:v1.2.3\n" +
				"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/bar-service:v1.2.3\n" +
				"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v1.2.3\n" +
				"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v2.0.0\n",
		},
		"duplicate images": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantRefs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v1.2.3\n",
		},
		"duplicate images with different hashes": {
			refs: []PinnedImageReference{
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:1111111111111111111111111111111111111111111111111111111111111111",
				},
				{
					Registry: "registry.example.com",
					Prefix:   "staging",
					Name:     "foo-service",
					Tag:      "v1.2.3",
					Digest:   "sha256:0000000000000000000000000000000000000000000000000000000000000000",
				},
			},
			wantErr: true,
		},
	}

	for name, tc := range testCases {
		t.Run(name+"_create", func(t *testing.T) {
			assert := assert.New(t)
			require := require.New(t)

			var buf bytes.Buffer
			err := Create(tc.refs, &buf)
			if tc.wantErr {
				require.Error(err)
				return
			}
			require.NoError(err)
			assert.Equal(tc.wantOut, buf.String())
		})
	}

	for name, tc := range testCases {
		if tc.wantErr {
			continue // skip inverse test cases where the forward test case is expected to fail
		}
		t.Run(name+"_parse", func(t *testing.T) {
			assert := assert.New(t)
			require := require.New(t)

			buf := bytes.NewBufferString(tc.wantOut)
			gotRefs, err := Parse(buf)
			if tc.wantErr {
				require.Error(err)
				return
			}
			require.NoError(err)
			wantRefs := tc.refs
			if len(tc.wantRefs) > 0 {
				wantRefs = tc.wantRefs
			}
			assert.Equal(wantRefs, gotRefs)
		})
	}
}

func TestMerge(t *testing.T) {
	testCases := map[string]struct {
		refs    [][]PinnedImageReference
		wantErr bool
		wantOut string
	}{
		"different images": {
			refs: [][]PinnedImageReference{
				{
					{
						Registry: "registry.example.com",
						Prefix:   "staging",
						Name:     "foo-service",
						Tag:      "v1.2.3",
						Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
					},
				},
				{
					{
						Registry: "registry.example.com",
						Prefix:   "staging",
						Name:     "bar-service",
						Tag:      "v1.2.3",
						Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
					},
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/bar-service:v1.2.3\n" + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v1.2.3\n",
		},
		"duplicate images": {
			refs: [][]PinnedImageReference{
				{
					{
						Registry: "registry.example.com",
						Prefix:   "staging",
						Name:     "foo-service",
						Tag:      "v1.2.3",
						Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
					},
				},
				{
					{
						Registry: "registry.example.com",
						Prefix:   "staging",
						Name:     "foo-service",
						Tag:      "v1.2.3",
						Digest:   "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
					},
				},
			},
			wantOut: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  registry.example.com/staging/foo-service:v1.2.3\n",
		},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			assert := assert.New(t)
			require := require.New(t)

			var buf bytes.Buffer
			err := Merge(tc.refs, &buf)
			if tc.wantErr {
				require.Error(err)
				return
			}
			require.NoError(err)
			assert.Equal(tc.wantOut, buf.String())
		})
	}
}

// TestParse has additional test cases that are not covered by TestCreateParse.
func TestParse(t *testing.T) {
	testCases := map[string]struct {
		in       string
		wantErr  bool
		wantRefs []PinnedImageReference
	}{
		"empty line": {
			in: "\n\n",
		},
		"line too short": {
			in:      "short",
			wantErr: true,
		},
		"malformed digest": {
			in:      "malformed-digest-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  registry.example.com/staging/foo-service:v1.2.3",
			wantErr: true,
		},
		"missing registry": {
			in:      "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  foo-service:v1.2.3",
			wantErr: true,
		},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			assert := assert.New(t)
			require := require.New(t)

			buf := bytes.NewBufferString(tc.in)
			gotRefs, err := Parse(buf)
			if tc.wantErr {
				require.Error(err)
				return
			}
			require.NoError(err)
			assert.Equal(tc.wantRefs, gotRefs)
		})
	}
}