/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package webhook

import (
	"encoding/json"
	"fmt"
	"testing"

	volumegroupsnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumegroupsnapshot/v1alpha1"
	groupsnapshotlisters "github.com/kubernetes-csi/external-snapshotter/client/v7/listers/volumegroupsnapshot/v1alpha1"
	"github.com/kubernetes-csi/external-snapshotter/v7/pkg/utils"
	v1 "k8s.io/api/admission/v1"
	core_v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
)

type fakeGroupSnapshotLister struct {
	values []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass
}

func (f *fakeGroupSnapshotLister) List(selector labels.Selector) (ret []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass, err error) {
	return f.values, nil
}

func (f *fakeGroupSnapshotLister) Get(name string) (*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass, error) {
	for _, v := range f.values {
		if v.Name == name {
			return v, nil
		}
	}
	return nil, nil
}

func TestAdmitVolumeGroupSnapshotV1Alpha1(t *testing.T) {
	selector := metav1.LabelSelector{MatchLabels: map[string]string{
		"group": "A",
	}}
	mutatedField := "changed-immutable-field"
	contentname := "groupsnapcontent1"
	emptyVolumeGroupSnapshotClassName := ""

	testCases := []struct {
		name                   string
		volumeGroupSnapshot    *volumegroupsnapshotv1alpha1.VolumeGroupSnapshot
		oldVolumeGroupSnapshot *volumegroupsnapshotv1alpha1.VolumeGroupSnapshot
		shouldAdmit            bool
		msg                    string
		operation              v1.Operation
	}{
		{
			name:                   "Delete: new and old are nil. Should admit",
			volumeGroupSnapshot:    nil,
			oldVolumeGroupSnapshot: nil,
			shouldAdmit:            true,
			operation:              v1.Delete,
		},
		{
			name: "Create: old is nil and new is valid, with contentname",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			oldVolumeGroupSnapshot: nil,
			shouldAdmit:            true,
			msg:                    "",
			operation:              v1.Create,
		},
		{
			name: "Create: old is nil and new is valid, with selector",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						Selector: &selector,
					},
				},
			},
			oldVolumeGroupSnapshot: nil,
			shouldAdmit:            true,
			msg:                    "",
			operation:              v1.Create,
		},
		{
			name: "Update: old is valid and new is invalid",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
					VolumeGroupSnapshotClassName: &emptyVolumeGroupSnapshotClassName,
				},
			},
			oldVolumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			shouldAdmit: false,
			operation:   v1.Update,
			msg:         "Spec.VolumeGroupSnapshotClassName must not be the empty string",
		},
		{
			name: "Update: old is valid and new is valid",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			oldVolumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			shouldAdmit: true,
			operation:   v1.Update,
		},
		{
			name: "Update: old is valid and new is valid but changes immutable field spec.source",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &mutatedField,
					},
				},
			},
			oldVolumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			shouldAdmit: false,
			operation:   v1.Update,
			msg:         fmt.Sprintf("Spec.Source.VolumeGroupSnapshotContentName is immutable but was changed from %s to %s", contentname, mutatedField),
		},
		{
			name: "Update: old is invalid and new is valid",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
					},
				},
			},
			oldVolumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
						Selector:                       &selector,
					},
				},
			},
			shouldAdmit: false,
			operation:   v1.Update,
			msg:         fmt.Sprintf("Spec.Source.Selector is immutable but was changed from %v to %v", &selector, "nil"),
		},
		{
			// will be handled by schema validation
			name: "Update: old is invalid and new is invalid",
			volumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
						Selector:                       &selector,
					},
				},
			},
			oldVolumeGroupSnapshot: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshot{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotSource{
						VolumeGroupSnapshotContentName: &contentname,
						Selector:                       &selector,
					},
				},
			},
			shouldAdmit: true,
			operation:   v1.Update,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			groupSnapshot := tc.volumeGroupSnapshot
			raw, err := json.Marshal(groupSnapshot)
			if err != nil {
				t.Fatal(err)
			}
			oldGroupSnapshot := tc.oldVolumeGroupSnapshot
			oldRaw, err := json.Marshal(oldGroupSnapshot)
			if err != nil {
				t.Fatal(err)
			}
			review := v1.AdmissionReview{
				Request: &v1.AdmissionRequest{
					Object: runtime.RawExtension{
						Raw: raw,
					},
					OldObject: runtime.RawExtension{
						Raw: oldRaw,
					},
					Resource:  GroupSnapshotV1Alpha1GVR,
					Operation: tc.operation,
				},
			}
			sa := NewGroupSnapshotAdmitter(nil)
			response := sa.Admit(review)
			shouldAdmit := response.Allowed
			msg := response.Result.Message

			expectedResponse := tc.shouldAdmit
			expectedMsg := tc.msg

			if shouldAdmit != expectedResponse {
				t.Errorf("expected \"%v\" to equal \"%v\": %v", shouldAdmit, expectedResponse, msg)
			}
			if msg != expectedMsg {
				t.Errorf("expected \"%v\" to equal \"%v\"", msg, expectedMsg)
			}
		})
	}
}

func TestAdmitVolumeGroupSnapshotContentV1Alpha1(t *testing.T) {
	volumeHandle := "volumeHandle1"
	modifiedRefName := "modified-ref-name"
	groupSnapshotHandle := "groupsnapshotHandle1"
	volumeSnapshotHandles := []string{"volumeSnapshotHandle1"}
	groupSnapshotHandles := &volumegroupsnapshotv1alpha1.GroupSnapshotHandles{
		VolumeGroupSnapshotHandle: groupSnapshotHandle,
		VolumeSnapshotHandles:     volumeSnapshotHandles,
	}
	modifiedGroupSnapshotHandles := &volumegroupsnapshotv1alpha1.GroupSnapshotHandles{
		VolumeGroupSnapshotHandle: groupSnapshotHandle,
		VolumeSnapshotHandles:     append(volumeSnapshotHandles, "volumeSnapshotHandle2"),
	}
	volumeGroupSnapshotClassName := "volume-snapshot-class-1"
	validContent := &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent{
		Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSpec{
			Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSource{
				GroupSnapshotHandles: groupSnapshotHandles,
			},
			VolumeGroupSnapshotRef: core_v1.ObjectReference{
				Name:      "group-snapshot-ref",
				Namespace: "default-ns",
			},
			VolumeGroupSnapshotClassName: &volumeGroupSnapshotClassName,
		},
	}
	invalidContent := &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent{
		Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSpec{
			Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSource{
				GroupSnapshotHandles: groupSnapshotHandles,
				VolumeHandles:        []string{volumeHandle},
			},
			VolumeGroupSnapshotRef: core_v1.ObjectReference{
				Name:      "",
				Namespace: "default-ns",
			}},
	}

	testCases := []struct {
		name                string
		groupSnapContent    *volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent
		oldGroupSnapContent *volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent
		shouldAdmit         bool
		msg                 string
		operation           v1.Operation
	}{
		{
			name:                "Delete: both new and old are nil",
			groupSnapContent:    nil,
			oldGroupSnapContent: nil,
			shouldAdmit:         true,
			operation:           v1.Delete,
		},
		{
			name:                "Create: old is nil and new is valid",
			groupSnapContent:    validContent,
			oldGroupSnapContent: nil,
			shouldAdmit:         true,
			operation:           v1.Create,
		},
		{
			name:                "Update: old is valid and new is invalid",
			groupSnapContent:    invalidContent,
			oldGroupSnapContent: validContent,
			shouldAdmit:         false,
			operation:           v1.Update,
			msg:                 fmt.Sprintf("Spec.Source.VolumeHandles is immutable but was changed from %s to %s", []string{}, []string{volumeHandle}),
		},
		{
			name:                "Update: old is valid and new is valid",
			groupSnapContent:    validContent,
			oldGroupSnapContent: validContent,
			shouldAdmit:         true,
			operation:           v1.Update,
		},
		{
			name: "Update: old is valid and new is valid but modifies immutable field",
			groupSnapContent: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSource{
						GroupSnapshotHandles: modifiedGroupSnapshotHandles,
					},
					VolumeGroupSnapshotRef: core_v1.ObjectReference{
						Name:      "snapshot-ref",
						Namespace: "default-ns",
					},
				},
			},
			oldGroupSnapContent: validContent,
			shouldAdmit:         false,
			operation:           v1.Update,
			msg:                 fmt.Sprintf("Spec.Source.GroupSnapshotHandles is immutable but was changed from %s to %s", groupSnapshotHandles, modifiedGroupSnapshotHandles),
		},
		{
			name: "Update: old is valid and new is valid but modifies immutable ref",
			groupSnapContent: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContent{
				Spec: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSpec{
					Source: volumegroupsnapshotv1alpha1.VolumeGroupSnapshotContentSource{
						GroupSnapshotHandles: &volumegroupsnapshotv1alpha1.GroupSnapshotHandles{
							VolumeGroupSnapshotHandle: groupSnapshotHandle,
							VolumeSnapshotHandles:     volumeSnapshotHandles,
						},
					},
					VolumeGroupSnapshotRef: core_v1.ObjectReference{
						Name:      modifiedRefName,
						Namespace: "default-ns",
					},
				},
			},
			oldGroupSnapContent: validContent,
			shouldAdmit:         false,
			operation:           v1.Update,
			msg: fmt.Sprintf("Spec.VolumeGroupSnapshotRef.Name is immutable but was changed from %s to %s",
				validContent.Spec.VolumeGroupSnapshotRef.Name, modifiedRefName),
		},
		{
			name:                "Update: old is invalid and new is valid",
			groupSnapContent:    validContent,
			oldGroupSnapContent: invalidContent,
			shouldAdmit:         false,
			operation:           v1.Update,
			msg:                 fmt.Sprintf("Spec.Source.VolumeHandles is immutable but was changed from %s to %s", []string{volumeHandle}, []string{}),
		},
		{
			name:                "Update: old is invalid and new is invalid",
			groupSnapContent:    invalidContent,
			oldGroupSnapContent: invalidContent,
			shouldAdmit:         false,
			operation:           v1.Update,
			msg:                 "both Spec.VolumeGroupSnapshotRef.Name =  and Spec.VolumeGroupSnapshotRef.Namespace = default-ns must be set",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			groupSnapContent := tc.groupSnapContent
			raw, err := json.Marshal(groupSnapContent)
			if err != nil {
				t.Fatal(err)
			}
			oldGroupSnapContent := tc.oldGroupSnapContent
			oldRaw, err := json.Marshal(oldGroupSnapContent)
			if err != nil {
				t.Fatal(err)
			}
			review := v1.AdmissionReview{
				Request: &v1.AdmissionRequest{
					Object: runtime.RawExtension{
						Raw: raw,
					},
					OldObject: runtime.RawExtension{
						Raw: oldRaw,
					},
					Resource:  GroupSnapshotContentV1Apha1GVR,
					Operation: tc.operation,
				},
			}
			sa := NewGroupSnapshotAdmitter(nil)
			response := sa.Admit(review)
			shouldAdmit := response.Allowed
			msg := response.Result.Message

			expectedResponse := tc.shouldAdmit
			expectedMsg := tc.msg

			if shouldAdmit != expectedResponse {
				t.Errorf("expected \"%v\" to equal \"%v\"", shouldAdmit, expectedResponse)
			}
			if msg != expectedMsg {
				t.Errorf("expected \"%v\" to equal \"%v\"", msg, expectedMsg)
			}
		})
	}
}

func TestAdmitVolumeGroupSnapshotClassV1Alpha1(t *testing.T) {
	testCases := []struct {
		name              string
		groupSnapClass    *volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass
		oldGroupSnapClass *volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass
		shouldAdmit       bool
		msg               string
		operation         v1.Operation
		lister            groupsnapshotlisters.VolumeGroupSnapshotClassLister
	}{
		{
			name: "new default for group snapshot class with no existing group snapshot classes",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			oldGroupSnapClass: nil,
			shouldAdmit:       true,
			msg:               "",
			operation:         v1.Create,
			lister:            &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{}},
		},
		{
			name: "new default for group snapshot class for  with existing default group snapshot class with different drivers",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{},
			shouldAdmit:       true,
			msg:               "",
			operation:         v1.Create,
			lister: &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "existing.test.csi.io",
				},
			}},
		},
		{
			name: "new default for group snapshot class with existing default group snapshot class same driver",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{},
			shouldAdmit:       false,
			msg:               "default group snapshot class: driver-a already exists for driver: test.csi.io",
			operation:         v1.Create,
			lister: &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Name: "driver-a",
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
			}},
		},
		{
			name: "default for group snapshot class with existing default group snapshot class same driver update",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			shouldAdmit: true,
			msg:         "",
			operation:   v1.Update,
			lister: &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
			}},
		},
		{
			name: "new group snapshot for group snapshot class with existing default group snapshot class same driver",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta:   metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{},
				Driver:     "test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{},
			shouldAdmit:       true,
			msg:               "",
			operation:         v1.Create,
			lister: &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
			}},
		},
		{
			name: "new group snapshot for group snapshot class with existing group snapshot default classes",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{},
			shouldAdmit:       false,
			msg:               "default group snapshot class: driver-is-default already exists for driver: test.csi.io",
			operation:         v1.Create,
			lister: &fakeGroupSnapshotLister{[]*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Name: "driver-is-default",
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
			}},
		},
		{
			name: "update group snapshot class to new driver with existing default group snapshot classes",
			groupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "driver.test.csi.io",
			},
			oldGroupSnapClass: &volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						utils.IsDefaultGroupSnapshotClassAnnotation: "true",
					},
				},
				Driver: "test.csi.io",
			},
			shouldAdmit: false,
			msg:         "default group snapshot class: driver-test-default already exists for driver: driver.test.csi.io",
			operation:   v1.Update,
			lister: &fakeGroupSnapshotLister{values: []*volumegroupsnapshotv1alpha1.VolumeGroupSnapshotClass{
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Name: "driver-is-default",
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "test.csi.io",
				},
				{
					TypeMeta: metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{
						Name: "driver-test-default",
						Annotations: map[string]string{
							utils.IsDefaultGroupSnapshotClassAnnotation: "true",
						},
					},
					Driver: "driver.test.csi.io",
				},
			}},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			groupSnapContent := tc.groupSnapClass
			raw, err := json.Marshal(groupSnapContent)
			if err != nil {
				t.Fatal(err)
			}
			oldGroupSnapClass := tc.oldGroupSnapClass
			oldRaw, err := json.Marshal(oldGroupSnapClass)
			if err != nil {
				t.Fatal(err)
			}
			review := v1.AdmissionReview{
				Request: &v1.AdmissionRequest{
					Object: runtime.RawExtension{
						Raw: raw,
					},
					OldObject: runtime.RawExtension{
						Raw: oldRaw,
					},
					Resource:  GroupSnapshotClassV1Apha1GVR,
					Operation: tc.operation,
				},
			}
			sa := NewGroupSnapshotAdmitter(tc.lister)
			response := sa.Admit(review)

			shouldAdmit := response.Allowed
			msg := response.Result.Message

			expectedResponse := tc.shouldAdmit
			expectedMsg := tc.msg

			if shouldAdmit != expectedResponse {
				t.Errorf("expected \"%v\" to equal \"%v\"", shouldAdmit, expectedResponse)
			}
			if msg != expectedMsg {
				t.Errorf("expected \"%v\" to equal \"%v\"", msg, expectedMsg)
			}
		})
	}
}
