/*
Copyright 2023 The Karmada 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 dependenciesdistributor

import (
	"context"
	"fmt"
	"reflect"
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/dynamic"
	dynamicfake "k8s.io/client-go/dynamic/fake"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/event"

	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
	policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
	workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
	"github.com/karmada-io/karmada/pkg/util"
	"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
	"github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
)

type MockAsyncWorker struct {
	queue []interface{}
}

// Note: This is a dummy implementation of Add for testing purposes.
func (m *MockAsyncWorker) Add(item interface{}) {
	// No actual work is done in the mock; we just simulate running
	m.queue = append(m.queue, item)
}

// Note: This is a dummy implementation of AddAfter for testing purposes.
func (m *MockAsyncWorker) AddAfter(item interface{}, duration time.Duration) {
	// No actual work is done in the mock; we just simulate running
	fmt.Printf("%v", duration)
	m.queue = append(m.queue, item)
}

// Note: This is a dummy implementation of Enqueue for testing purposes.
func (m *MockAsyncWorker) Enqueue(obj interface{}) {
	// Assuming KeyFunc is used to generate a key; for simplicity, we use obj directly
	m.queue = append(m.queue, obj)
}

// Note: This is a dummy implementation of Run for testing purposes.
func (m *MockAsyncWorker) Run(workerNumber int, stopChan <-chan struct{}) {
	// No actual work is done in the mock; we just simulate running
	fmt.Printf("%v", workerNumber)
	fmt.Printf("%v", <-stopChan)
}

// GetQueue returns the current state of the queue
func (m *MockAsyncWorker) GetQueue() []interface{} {
	return m.queue
}

func Test_OnUpdate(t *testing.T) {
	type args struct {
		oldObj interface{}
		newObj interface{}
	}
	tests := []struct {
		name          string
		args          args
		wantQueueSize int
	}{
		{
			name: "update the object, specification changed",
			args: args{
				oldObj: &corev1.Node{
					TypeMeta: metav1.TypeMeta{
						Kind:       "Node",
						APIVersion: "v1",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name: "bar",
					},
				},
				newObj: &corev1.Node{
					TypeMeta: metav1.TypeMeta{
						Kind:       "Node",
						APIVersion: "v1",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name: "foo",
					},
				},
			},
			wantQueueSize: 2,
		},
		{
			name: "do not update the object, no specification changed",
			args: args{
				oldObj: &corev1.Node{
					TypeMeta: metav1.TypeMeta{
						Kind:       "Node",
						APIVersion: "v1",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name: "bar",
					},
				},
				newObj: &corev1.Node{
					TypeMeta: metav1.TypeMeta{
						Kind:       "Node",
						APIVersion: "v1",
					},
					ObjectMeta: metav1.ObjectMeta{
						Name: "bar",
					},
				},
			},
			wantQueueSize: 0,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mockWorker := &MockAsyncWorker{}
			d := &DependenciesDistributor{
				resourceProcessor: mockWorker,
			}

			d.OnUpdate(tt.args.oldObj, tt.args.newObj)

			gotQueueSize := len(mockWorker.GetQueue())
			if gotQueueSize != tt.wantQueueSize {
				t.Errorf("OnUpdate() want queue size %v, got %v", tt.wantQueueSize, gotQueueSize)
			}
		})
	}
}

func Test_reconcileResourceTemplate(t *testing.T) {
	type args struct {
		key util.QueueKey
	}
	type fields struct {
		Client client.Client
	}
	tests := []struct {
		name                   string
		args                   args
		fields                 fields
		wantGenericEventLength int
		wantErr                bool
	}{
		{
			name: "reconcile resource template",
			args: args{
				key: &LabelsKey{
					ClusterWideKey: keys.ClusterWideKey{
						Group:     "apps",
						Version:   "v1",
						Kind:      "Deployment",
						Name:      "demo-app",
						Namespace: "test",
					},
					Labels: map[string]string{
						"app": "test",
					},
				},
			},
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"app": "test",
							},
							Annotations: map[string]string{
								dependenciesAnnotationKey: "[{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"namespace\":\"test\",\"name\":\"demo-app\"}]",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "test",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			wantGenericEventLength: 1,
			wantErr:                false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client:       tt.fields.Client,
				genericEvent: make(chan event.TypedGenericEvent[*workv1alpha2.ResourceBinding], 1),
			}

			err := d.reconcileResourceTemplate(tt.args.key)
			if (err != nil) != tt.wantErr {
				t.Errorf("reconcileResourceTemplate() error = %v, wantErr %v", err, tt.wantErr)
			}

			gotGenericEventLength := len(d.genericEvent)
			if gotGenericEventLength != tt.wantGenericEventLength {
				t.Errorf("reconcileResourceTemplate() length of genericEvent = %v, want length %v", gotGenericEventLength, tt.wantGenericEventLength)
			}
		})
	}
}

func Test_dependentObjectReferenceMatches(t *testing.T) {
	type args struct {
		objectKey        *LabelsKey
		referenceBinding *workv1alpha2.ResourceBinding
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "test custom resource",
			args: args{
				objectKey: &LabelsKey{
					ClusterWideKey: keys.ClusterWideKey{
						Group:     "example-stgzr.karmada.io",
						Version:   "v1alpha1",
						Kind:      "Foot5zmh",
						Namespace: "karmadatest-vpvll",
						Name:      "cr-fxzq6",
					},
					Labels: nil,
				},
				referenceBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"example-stgzr.karmada.io/v1alpha1\",\"kind\":\"Foot5zmh\",\"namespace\":\"karmadatest-vpvll\",\"name\":\"cr-fxzq6\"}]",
					}},
				},
			},
			want: true,
		},
		{
			name: "test configmap",
			args: args{
				objectKey: &LabelsKey{
					ClusterWideKey: keys.ClusterWideKey{
						Group:     "",
						Version:   "v1",
						Kind:      "ConfigMap",
						Namespace: "karmadatest-h46wh",
						Name:      "configmap-8w426",
					},
					Labels: nil,
				},
				referenceBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"namespace\":\"karmadatest-h46wh\",\"name\":\"configmap-8w426\"}]",
					}},
				},
			},
			want: true,
		},
		{
			name: "test labels",
			args: args{
				objectKey: &LabelsKey{
					ClusterWideKey: keys.ClusterWideKey{
						Group:     "",
						Version:   "v1",
						Kind:      "ConfigMap",
						Namespace: "test",
					},
					Labels: map[string]string{
						"app": "test",
					},
				},
				referenceBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"namespace\":\"test\",\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"test\"]}]}}]",
					}},
				},
			},
			want: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := matchesWithBindingDependencies(tt.args.objectKey, tt.args.referenceBinding)
			if got != tt.want {
				t.Errorf("matchesWithBindingDependencies() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_addFinalizer(t *testing.T) {
	type fields struct {
		Client client.Client
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *workv1alpha2.ResourceBinding
		wantErr bool
	}{
		{
			name: "add finalizer",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
					},
				},
			},
			want: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Finalizers:      []string{util.BindingDependenciesDistributorFinalizer},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.fields.Client,
			}
			err := d.addFinalizer(context.Background(), tt.args.independentBinding)
			if (err != nil) != tt.wantErr {
				t.Errorf("addFinalizer() error = %v, wantErr %v", err, tt.wantErr)
			}

			bindingKey := client.ObjectKey{Namespace: tt.args.independentBinding.Namespace, Name: tt.args.independentBinding.Name}
			got := &workv1alpha2.ResourceBinding{}
			err = d.Client.Get(context.Background(), bindingKey, got)
			if (err != nil) != tt.wantErr {
				t.Errorf("Client.Get() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Client.Get() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_removeFinalizer(t *testing.T) {
	type fields struct {
		Client client.Client
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *workv1alpha2.ResourceBinding
		wantErr bool
	}{
		{
			name: "remove non-empty finalizer",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Finalizers:      []string{util.BindingDependenciesDistributorFinalizer},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Finalizers:      []string{util.BindingDependenciesDistributorFinalizer},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
					},
				},
			},
			want: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.fields.Client,
			}

			err := d.removeFinalizer(context.Background(), tt.args.independentBinding)
			if (err != nil) != tt.wantErr {
				t.Errorf("removeFinalizer() error = %v, wantErr %v", err, tt.wantErr)
			}

			bindingKey := client.ObjectKey{Namespace: tt.args.independentBinding.Namespace, Name: tt.args.independentBinding.Name}
			got := &workv1alpha2.ResourceBinding{}
			err = d.Client.Get(context.Background(), bindingKey, got)
			if (err != nil) != tt.wantErr {
				t.Errorf("Client.Get() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Client.Get() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_handleIndependentBindingDeletion(t *testing.T) {
	type fields struct {
		Client client.Client
	}
	type args struct {
		id        string
		namespace string
		name      string
	}
	tests := []struct {
		name         string
		fields       fields
		args         args
		wantBindings *workv1alpha2.ResourceBindingList
		wantErr      bool
	}{
		{
			name: "handle independent binding deletion",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"app": "nginx",
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
							RequiredBy: []workv1alpha2.BindingSnapshot{
								{
									Namespace: "test",
									Name:      "test-binding",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "foo",
											Replicas: 1,
										},
									},
								},
								{
									Namespace: "default-1",
									Name:      "default-binding-1",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "member1",
											Replicas: 2,
										},
									},
								},
								{
									Namespace: "test",
									Name:      "test-binding",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "bar",
											Replicas: 1,
										},
									},
								},
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				id:        "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
				namespace: "test",
				name:      "test-binding",
			},
			wantBindings: &workv1alpha2.ResourceBindingList{
				Items: []workv1alpha2.ResourceBinding{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1001",
							Labels: map[string]string{
								"app": "nginx",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
							RequiredBy: []workv1alpha2.BindingSnapshot{
								{
									Namespace: "default-1",
									Name:      "default-binding-1",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "member1",
											Replicas: 2,
										},
									},
								},
							},
						},
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.fields.Client,
			}
			err := d.handleIndependentBindingDeletion(tt.args.id, tt.args.namespace, tt.args.name)
			if (err != nil) != tt.wantErr {
				t.Errorf("handleIndependentBindingDeletion() error = %v, wantErr %v", err, tt.wantErr)
			}

			existBindings := &workv1alpha2.ResourceBindingList{}
			err = d.Client.List(context.TODO(), existBindings)
			if (err != nil) != tt.wantErr {
				t.Errorf("handleIndependentBindingDeletion(), Client.List() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(existBindings, tt.wantBindings) {
				t.Errorf("handleIndependentBindingDeletion(), Client.List() = %v, want %v", existBindings, tt.wantBindings)
			}
		})
	}
}

func Test_removeOrphanAttachedBindings(t *testing.T) {
	type fields struct {
		Client          client.Client
		DynamicClient   dynamic.Interface
		InformerManager genericmanager.SingleClusterInformerManager
		RESTMapper      meta.RESTMapper
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
		dependencies       []configv1alpha1.DependentObjectReference
	}
	tests := []struct {
		name         string
		fields       fields
		args         args
		wantBindings *workv1alpha2.ResourceBindingList
		wantErr      bool
	}{
		{
			name: "remove orphan attached bindings",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding-1",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "test",
						Name:       "pod-test",
					},
				},
			},
			wantBindings: &workv1alpha2.ResourceBindingList{
				Items: []workv1alpha2.ResourceBinding{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding-1",
							Namespace:       "test",
							ResourceVersion: "1001",
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client:          tt.fields.Client,
				DynamicClient:   tt.fields.DynamicClient,
				InformerManager: tt.fields.InformerManager,
				RESTMapper:      tt.fields.RESTMapper,
			}
			err := d.removeOrphanAttachedBindings(context.Background(), tt.args.independentBinding, tt.args.dependencies)
			if (err != nil) != tt.wantErr {
				t.Errorf("removeOrphanAttachedBindings() error = %v, wantErr %v", err, tt.wantErr)
			}

			existBindings := &workv1alpha2.ResourceBindingList{}
			err = d.Client.List(context.TODO(), existBindings)
			if (err != nil) != tt.wantErr {
				t.Errorf("removeOrphanAttachedBindings(), Client.List() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(existBindings, tt.wantBindings) {
				t.Errorf("removeOrphanAttachedBindings(), Client.List() = %v, want %v", existBindings, tt.wantBindings)
			}
		})
	}
}

func Test_handleDependentResource(t *testing.T) {
	type fields struct {
		Client          client.Client
		DynamicClient   dynamic.Interface
		InformerManager genericmanager.SingleClusterInformerManager
		RESTMapper      meta.RESTMapper
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
		dependencies       configv1alpha1.DependentObjectReference
	}
	tests := []struct {
		name        string
		fields      fields
		args        args
		wantBinding *workv1alpha2.ResourceBinding
		wantErr     bool
	}{
		{
			name: "nil label selector, non-empty name",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
								UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
							},
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test-binding",
						Namespace: "test",
						Labels:    map[string]string{workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
							UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
						},
						Clusters: []workv1alpha2.TargetCluster{
							{
								Name:     "member1",
								Replicas: 2,
							},
						},
					},
				},
				dependencies: configv1alpha1.DependentObjectReference{
					APIVersion: "v1",
					Kind:       "Pod",
					Namespace:  "default",
					Name:       "pod",
				},
			},
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Labels:          map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "v1",
						Kind:            "Pod",
						Namespace:       "default",
						Name:            "pod",
						ResourceVersion: "22222",
						UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
					},
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
			},
			wantErr: false,
		},
		{
			name: "empty name, non-nil label selector",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
								UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
							},
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test-binding",
						Namespace: "test",
						Labels:    map[string]string{workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
							UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
						},
						Clusters: []workv1alpha2.TargetCluster{
							{
								Name:     "member1",
								Replicas: 2,
							},
						},
					},
				},
				dependencies: configv1alpha1.DependentObjectReference{
					APIVersion:    "v1",
					Kind:          "Pod",
					Namespace:     "default",
					LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}},
				},
			},
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Labels:          map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "v1",
						Kind:            "Pod",
						Namespace:       "default",
						Name:            "pod",
						ResourceVersion: "22222",
						UID:             types.UID("db56a4a6-0dff-465a-b046-2c1dea42a42b"),
					},
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
			},
			wantErr: false,
		},
		{
			name: "nil label selector, empty name",
			fields: fields{
				Client: func() client.Client {
					return fake.NewClientBuilder().Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test-binding",
						Namespace: "test",
						Labels:    map[string]string{workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Clusters: []workv1alpha2.TargetCluster{
							{
								Name:     "member1",
								Replicas: 2,
							},
						},
					},
				},
				dependencies: configv1alpha1.DependentObjectReference{
					APIVersion: "v1",
					Kind:       "Pod",
					Namespace:  "default",
				},
			},
			wantBinding: &workv1alpha2.ResourceBinding{},
			wantErr:     true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client:          tt.fields.Client,
				DynamicClient:   tt.fields.DynamicClient,
				InformerManager: tt.fields.InformerManager,
				RESTMapper:      tt.fields.RESTMapper,
			}
			err := d.handleDependentResource(context.Background(), tt.args.independentBinding, tt.args.dependencies)
			if (err != nil) != tt.wantErr {
				t.Errorf("handleDependentResource() error = %v, wantErr %v", err, tt.wantErr)
			}

			existBinding := &workv1alpha2.ResourceBinding{}
			bindingKey := client.ObjectKeyFromObject(tt.args.independentBinding)
			err = d.Client.Get(context.TODO(), bindingKey, existBinding)
			if (err != nil) != tt.wantErr {
				t.Errorf("handleDependentResource(), Client.Get() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(existBinding, tt.wantBinding) {
				t.Errorf("handleDependentResource(), Client.Get() = %v, want %v", existBinding, tt.wantBinding)
			}
		})
	}
}

func Test_recordDependencies(t *testing.T) {
	type fields struct {
		Client client.Client
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
		dependencies       []configv1alpha1.DependentObjectReference
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *workv1alpha2.ResourceBinding
		wantErr bool
	}{
		{
			name: "record updated dependencies",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						Name:       "pod",
					},
				},
			},
			want: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "v1",
						Kind:            "Pod",
						Namespace:       "default",
						Name:            "pod",
						ResourceVersion: "22222",
					},
				},
			},
			wantErr: false,
		},
		{
			name: "no need to record non-updated dependencies",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Annotations: map[string]string{
								dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Annotations: map[string]string{
							dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						Name:       "pod",
					},
				},
			},
			want: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "v1",
						Kind:            "Pod",
						Namespace:       "default",
						Name:            "pod",
						ResourceVersion: "22222",
					},
				},
			},
			wantErr: false,
		},
		{
			name: "non-matching independent binding",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Annotations: map[string]string{
								dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "999",
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						Name:       "pod",
					},
				},
			},
			want: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Annotations: map[string]string{
						dependenciesAnnotationKey: "[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"namespace\":\"default\",\"name\":\"pod\"}]",
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "v1",
						Kind:            "Pod",
						Namespace:       "default",
						Name:            "pod",
						ResourceVersion: "22222",
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.fields.Client,
			}
			err := d.recordDependencies(context.Background(), tt.args.independentBinding, tt.args.dependencies)
			if (err != nil) != tt.wantErr {
				t.Errorf("recordDependencies() error = %v, wantErr %v", err, tt.wantErr)
			}

			bindingKey := client.ObjectKey{Namespace: tt.args.independentBinding.Namespace, Name: tt.args.independentBinding.Name}
			got := &workv1alpha2.ResourceBinding{}
			err = d.Client.Get(context.Background(), bindingKey, got)
			if (err != nil) != tt.wantErr {
				t.Errorf("Client.Get() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Client.Get() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_findOrphanAttachedBindings(t *testing.T) {
	type fields struct {
		Client          client.Client
		DynamicClient   dynamic.Interface
		InformerManager genericmanager.SingleClusterInformerManager
		RESTMapper      meta.RESTMapper
	}
	type args struct {
		independentBinding *workv1alpha2.ResourceBinding
		dependencies       []configv1alpha1.DependentObjectReference
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    []*workv1alpha2.ResourceBinding
		wantErr bool
	}{
		{
			name: "find orphan attached bindings - matching dependency",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding-1",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}
					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						Name:       "pod-test",
					},
				},
			},
			want: []*workv1alpha2.ResourceBinding{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding-1",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
			},
			wantErr: false,
		},
		{
			name: "find orphan attached bindings - non matching dependency",
			fields: fields{
				Client: func() client.Client {
					Scheme := runtime.NewScheme()
					utilruntime.Must(scheme.AddToScheme(Scheme))
					utilruntime.Must(workv1alpha2.Install(Scheme))
					rb := &workv1alpha2.ResourceBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding-1",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "v1",
								Kind:            "Pod",
								Namespace:       "default",
								Name:            "pod",
								ResourceVersion: "22222",
							},
						},
					}

					return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
				}(),
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				independentBinding: &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "test",
						Name:       "pod-test",
					},
				},
			},
			want: []*workv1alpha2.ResourceBinding{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding-1",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "v1",
							Kind:            "Pod",
							Namespace:       "default",
							Name:            "pod",
							ResourceVersion: "22222",
						},
					},
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client:          tt.fields.Client,
				DynamicClient:   tt.fields.DynamicClient,
				InformerManager: tt.fields.InformerManager,
				RESTMapper:      tt.fields.RESTMapper,
			}
			got, err := d.findOrphanAttachedBindings(context.Background(), tt.args.independentBinding, tt.args.dependencies)
			if (err != nil) != tt.wantErr {
				t.Errorf("findOrphanAttachedBindings() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("findOrphanAttachedBindings() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDependenciesDistributor_findOrphanAttachedBindingsByDependencies(t *testing.T) {
	type fields struct {
		DynamicClient   dynamic.Interface
		InformerManager genericmanager.SingleClusterInformerManager
		RESTMapper      meta.RESTMapper
	}
	type args struct {
		dependencies      []configv1alpha1.DependentObjectReference
		dependencyIndexes []int
		attachedBinding   *workv1alpha2.ResourceBinding
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    bool
		wantErr bool
	}{
		{
			name: "can't match labels",
			fields: fields{
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"bar": "bar"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Secret",
						Namespace:  "default",
						Name:       "test",
					},
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{
							"bar": "foo",
						}},
					},
				},
				dependencyIndexes: []int{1},
				attachedBinding: &workv1alpha2.ResourceBinding{
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion: "v1",
							Kind:       "Pod",
							Namespace:  "default",
							Name:       "pod",
						},
					},
				},
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "can't match name",
			fields: fields{
				DynamicClient: dynamicfake.NewSimpleDynamicClient(scheme.Scheme),
				InformerManager: func() genericmanager.SingleClusterInformerManager {
					c := dynamicfake.NewSimpleDynamicClient(scheme.Scheme,
						&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "default", Labels: map[string]string{"bar": "foo"}}})
					m := genericmanager.NewSingleClusterInformerManager(c, 0, context.TODO().Done())
					m.Lister(corev1.SchemeGroupVersion.WithResource("pods"))
					m.Start()
					m.WaitForCacheSync()
					return m
				}(),
				RESTMapper: func() meta.RESTMapper {
					m := meta.NewDefaultRESTMapper([]schema.GroupVersion{corev1.SchemeGroupVersion})
					m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
					return m
				}(),
			},
			args: args{
				dependencies: []configv1alpha1.DependentObjectReference{
					{
						APIVersion: "v1",
						Kind:       "Secret",
						Namespace:  "default",
						Name:       "test",
					},
					{
						APIVersion: "v1",
						Kind:       "Pod",
						Namespace:  "default",
						Name:       "pod",
					},
				},
				dependencyIndexes: []int{1},
				attachedBinding: &workv1alpha2.ResourceBinding{
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion: "v1",
							Kind:       "Pod",
							Namespace:  "default",
							Name:       "test2",
						},
					},
				},
			},
			want:    true,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				DynamicClient:   tt.fields.DynamicClient,
				InformerManager: tt.fields.InformerManager,
				RESTMapper:      tt.fields.RESTMapper,
			}
			got, err := d.isOrphanAttachedBindings(context.Background(), tt.args.dependencies, tt.args.dependencyIndexes, tt.args.attachedBinding)
			if (err != nil) != tt.wantErr {
				t.Errorf("findOrphanAttachedBindingsByDependencies() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("findOrphanAttachedBindingsByDependencies() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_listAttachedBindings(t *testing.T) {
	Scheme := runtime.NewScheme()
	utilruntime.Must(scheme.AddToScheme(Scheme))
	utilruntime.Must(workv1alpha2.Install(Scheme))
	tests := []struct {
		name             string
		bindingID        string
		bindingNamespace string
		bindingName      string
		wantErr          bool
		wantBindings     []*workv1alpha2.ResourceBinding
		setupClient      func() client.Client
	}{
		{
			name:             "list attached bindings",
			bindingID:        "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
			bindingNamespace: "test",
			bindingName:      "test-binding",
			wantErr:          false,
			wantBindings: []*workv1alpha2.ResourceBinding{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"app": "nginx",
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "foo",
										Replicas: 1,
									},
								},
							},
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "bar",
										Replicas: 1,
									},
								},
							},
						},
					},
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"app": "nginx",
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "foo",
										Replicas: 1,
									},
								},
							},
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "bar",
										Replicas: 1,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.setupClient(),
			}
			gotBindings, err := d.listAttachedBindings(tt.bindingID, tt.bindingNamespace, tt.bindingName)
			if (err != nil) != tt.wantErr {
				t.Errorf("listAttachedBindings() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(gotBindings, tt.wantBindings) {
				t.Errorf("listAttachedBindings() = %v, want %v", gotBindings, tt.wantBindings)
			}
		})
	}
}

func Test_removeScheduleResultFromAttachedBindings(t *testing.T) {
	Scheme := runtime.NewScheme()
	utilruntime.Must(scheme.AddToScheme(Scheme))
	utilruntime.Must(workv1alpha2.Install(Scheme))
	tests := []struct {
		name             string
		bindingNamespace string
		bindingName      string
		attachedBindings []*workv1alpha2.ResourceBinding
		wantErr          bool
		wantBindings     *workv1alpha2.ResourceBindingList
		setupClient      func() client.Client
	}{
		{
			name:             "remove schedule result from attached bindings",
			bindingNamespace: "test",
			bindingName:      "test-binding",
			attachedBindings: []*workv1alpha2.ResourceBinding{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"app": "nginx",
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "foo",
										Replicas: 1,
									},
								},
							},
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "bar",
										Replicas: 1,
									},
								},
							},
						},
					},
				},
			},
			wantErr: false,
			wantBindings: &workv1alpha2.ResourceBindingList{
				Items: []workv1alpha2.ResourceBinding{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1001",
							Labels: map[string]string{
								"app": "nginx",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
							RequiredBy: []workv1alpha2.BindingSnapshot{
								{
									Namespace: "default-1",
									Name:      "default-binding-1",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "member1",
											Replicas: 2,
										},
									},
								},
							},
						},
					},
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"app": "nginx",
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "foo",
										Replicas: 1,
									},
								},
							},
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "bar",
										Replicas: 1,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
		{
			name:             "empty attached bindings",
			bindingNamespace: "test",
			bindingName:      "test-binding",
			attachedBindings: []*workv1alpha2.ResourceBinding{},
			wantErr:          false,
			wantBindings: &workv1alpha2.ResourceBindingList{
				Items: []workv1alpha2.ResourceBinding{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:            "test-binding",
							Namespace:       "test",
							ResourceVersion: "1000",
							Labels: map[string]string{
								"app": "nginx",
								"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
							},
						},
						Spec: workv1alpha2.ResourceBindingSpec{
							Resource: workv1alpha2.ObjectReference{
								APIVersion:      "apps/v1",
								Kind:            "Deployment",
								Namespace:       "fake-ns",
								Name:            "demo-app",
								ResourceVersion: "22222",
							},
							RequiredBy: []workv1alpha2.BindingSnapshot{
								{
									Namespace: "test",
									Name:      "test-binding",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "foo",
											Replicas: 1,
										},
									},
								},
								{
									Namespace: "default-1",
									Name:      "default-binding-1",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "member1",
											Replicas: 2,
										},
									},
								},
								{
									Namespace: "test",
									Name:      "test-binding",
									Clusters: []workv1alpha2.TargetCluster{
										{
											Name:     "bar",
											Replicas: 1,
										},
									},
								},
							},
						},
					},
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels: map[string]string{
							"app": "nginx",
							"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9",
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						Resource: workv1alpha2.ObjectReference{
							APIVersion:      "apps/v1",
							Kind:            "Deployment",
							Namespace:       "fake-ns",
							Name:            "demo-app",
							ResourceVersion: "22222",
						},
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "foo",
										Replicas: 1,
									},
								},
							},
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "test",
								Name:      "test-binding",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "bar",
										Replicas: 1,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.setupClient(),
			}
			err := d.removeScheduleResultFromAttachedBindings(tt.bindingNamespace, tt.bindingName, tt.attachedBindings)
			if (err != nil) != tt.wantErr {
				t.Errorf("removeScheduleResultFromAttachedBindings() error = %v, wantErr %v", err, tt.wantErr)
			}
			existBindings := &workv1alpha2.ResourceBindingList{}
			err = d.Client.List(context.TODO(), existBindings)
			if (err != nil) != tt.wantErr {
				t.Errorf("removeScheduleResultFromAttachedBindings(), Client.List() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(existBindings, tt.wantBindings) {
				t.Errorf("removeScheduleResultFromAttachedBindings(), Client.List() = %v, want %v", existBindings, tt.wantBindings)
			}
		})
	}
}

func Test_createOrUpdateAttachedBinding(t *testing.T) {
	Scheme := runtime.NewScheme()
	utilruntime.Must(scheme.AddToScheme(Scheme))
	utilruntime.Must(workv1alpha2.Install(Scheme))
	tests := []struct {
		name            string
		attachedBinding *workv1alpha2.ResourceBinding
		wantErr         bool
		wantBinding     *workv1alpha2.ResourceBinding
		setupClient     func() client.Client
	}{
		{
			name: "update attached binding",
			attachedBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Labels:          map[string]string{"app": "nginx"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
				},
			},
			wantErr: false,
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Labels:          map[string]string{"app": "nginx", "foo": "bar"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "default-1",
							Name:      "default-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "default-3",
							Name:      "default-binding-3",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member3",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels:          map[string]string{"foo": "bar"},
						OwnerReferences: []metav1.OwnerReference{
							{
								UID:        "foo-bar",
								Controller: ptr.To[bool](true),
							},
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "default-2",
								Name:      "default-binding-2",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member2",
										Replicas: 3,
									},
								},
							},
							{
								Namespace: "default-3",
								Name:      "default-binding-3",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member3",
										Replicas: 4,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
		{
			name: "create attached binding",
			attachedBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "test-binding",
					Namespace: "test",
					Labels:    map[string]string{"app": "nginx"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
				},
			},
			wantErr: false,
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1",
					Labels:          map[string]string{"app": "nginx"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
				},
			},
			setupClient: func() client.Client {
				return fake.NewClientBuilder().WithScheme(Scheme).Build()
			},
		},
		{
			name: "update attached binding with ConflictResolution",
			attachedBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Labels:          map[string]string{"app": "nginx"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
					ConflictResolution: policyv1alpha1.ConflictOverwrite,
				},
			},
			wantErr: false,
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Labels:          map[string]string{"app": "nginx", "foo": "bar"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "default-1",
							Name:      "default-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
						{
							Namespace: "default-2",
							Name:      "default-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member2",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "default-3",
							Name:      "default-binding-3",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member3",
									Replicas: 4,
								},
							},
						},
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
						{
							Namespace: "test-2",
							Name:      "test-binding-2",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "bar",
									Replicas: 1,
								},
							},
						},
					},
					ConflictResolution: policyv1alpha1.ConflictOverwrite,
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels:          map[string]string{"foo": "bar"},
						OwnerReferences: []metav1.OwnerReference{
							{
								UID:        "foo-bar",
								Controller: ptr.To[bool](true),
							},
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
							{
								Namespace: "default-2",
								Name:      "default-binding-2",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member2",
										Replicas: 3,
									},
								},
							},
							{
								Namespace: "default-3",
								Name:      "default-binding-3",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member3",
										Replicas: 4,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
		{
			name: "update attached binding which is being deleted",
			attachedBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1000",
					Labels:          map[string]string{"app": "nginx"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test-1",
							Name:      "test-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "foo",
									Replicas: 1,
								},
							},
						},
					},
					ConflictResolution: policyv1alpha1.ConflictOverwrite,
				},
			},
			wantErr: true,
			wantBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:            "test-binding",
					Namespace:       "test",
					ResourceVersion: "1001",
					Labels:          map[string]string{"app": "nginx", "foo": "bar"},
					OwnerReferences: []metav1.OwnerReference{
						{
							UID:        "foo-bar",
							Controller: ptr.To[bool](true),
						},
					},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "default-1",
							Name:      "default-binding-1",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
					},
					ConflictResolution: policyv1alpha1.ConflictOverwrite,
				},
			},
			setupClient: func() client.Client {
				rb := &workv1alpha2.ResourceBinding{
					ObjectMeta: metav1.ObjectMeta{
						Name:            "test-binding",
						Namespace:       "test",
						ResourceVersion: "1000",
						Labels:          map[string]string{"foo": "bar"},
						OwnerReferences: []metav1.OwnerReference{
							{
								UID:        "bar-foo",
								Controller: ptr.To[bool](true),
							},
						},
					},
					Spec: workv1alpha2.ResourceBindingSpec{
						RequiredBy: []workv1alpha2.BindingSnapshot{
							{
								Namespace: "default-1",
								Name:      "default-binding-1",
								Clusters: []workv1alpha2.TargetCluster{
									{
										Name:     "member1",
										Replicas: 2,
									},
								},
							},
						},
					},
				}
				return fake.NewClientBuilder().WithScheme(Scheme).WithObjects(rb).Build()
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			d := &DependenciesDistributor{
				Client: tt.setupClient(),
			}
			err := d.createOrUpdateAttachedBinding(tt.attachedBinding)
			if (err != nil) != tt.wantErr {
				t.Errorf("createOrUpdateAttachedBinding() error = %v, wantErr %v", err, tt.wantErr)
			}
			if tt.wantErr {
				return
			}
			existBinding := &workv1alpha2.ResourceBinding{}
			bindingKey := client.ObjectKeyFromObject(tt.attachedBinding)
			err = d.Client.Get(context.TODO(), bindingKey, existBinding)
			if err != nil {
				t.Errorf("createOrUpdateAttachedBinding(), Client.Get() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !reflect.DeepEqual(existBinding, tt.wantBinding) {
				t.Errorf("createOrUpdateAttachedBinding(), Client.Get() = %v, want %v", existBinding, tt.wantBinding)
			}
		})
	}
}

func Test_buildAttachedBinding(t *testing.T) {
	blockOwnerDeletion := true
	isController := true
	tests := []struct {
		name                string
		independentBinding  *workv1alpha2.ResourceBinding
		object              *unstructured.Unstructured
		wantAttachedBinding *workv1alpha2.ResourceBinding
	}{
		{
			name: "build attached binding",
			independentBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "test-binding",
					Namespace: "test",
					Labels:    map[string]string{workv1alpha2.ResourceBindingPermanentIDLabel: "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
			},
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "apps/v1",
					"kind":       "Deployment",
					"metadata": map[string]interface{}{
						"name":            "demo-app",
						"namespace":       "fake-ns",
						"resourceVersion": "22222",
						"uid":             "db56a4a6-0dff-465a-b046-2c1dea42a42b",
					},
				},
			},
			wantAttachedBinding: &workv1alpha2.ResourceBinding{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "demo-app-deployment",
					Namespace: "test",
					OwnerReferences: []metav1.OwnerReference{
						{
							APIVersion:         "apps/v1",
							Kind:               "Deployment",
							Name:               "demo-app",
							UID:                "db56a4a6-0dff-465a-b046-2c1dea42a42b",
							BlockOwnerDeletion: &blockOwnerDeletion,
							Controller:         &isController,
						},
					},
					Labels:     map[string]string{"resourcebinding.karmada.io/depended-by-5dbb6dc9c8": "93162d3c-ee8e-4995-9034-05f4d5d2c2b9"},
					Finalizers: []string{util.BindingControllerFinalizer},
				},
				Spec: workv1alpha2.ResourceBindingSpec{
					Resource: workv1alpha2.ObjectReference{
						APIVersion:      "apps/v1",
						Kind:            "Deployment",
						Namespace:       "fake-ns",
						Name:            "demo-app",
						ResourceVersion: "22222",
					},
					RequiredBy: []workv1alpha2.BindingSnapshot{
						{
							Namespace: "test",
							Name:      "test-binding",
							Clusters: []workv1alpha2.TargetCluster{
								{
									Name:     "member1",
									Replicas: 2,
								},
							},
						},
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotAttachedBinding := buildAttachedBinding(tt.independentBinding, tt.object)
			if !reflect.DeepEqual(gotAttachedBinding, tt.wantAttachedBinding) {
				t.Errorf("buildAttachedBinding() = %v, want %v", gotAttachedBinding, tt.wantAttachedBinding)
			}
		})
	}
}

func Test_mergeBindingSnapshot(t *testing.T) {
	tests := []struct {
		name                string
		existSnapshot       []workv1alpha2.BindingSnapshot
		newSnapshot         []workv1alpha2.BindingSnapshot
		expectExistSnapshot []workv1alpha2.BindingSnapshot
	}{
		{
			name: "merge binding snapshot",
			existSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
			newSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "test-1",
					Name:      "test-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "foo",
							Replicas: 1,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 4,
						},
					},
				},
				{
					Namespace: "test-2",
					Name:      "test-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "bar",
							Replicas: 1,
						},
					},
				},
			},
			expectExistSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 4,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
				{
					Namespace: "test-1",
					Name:      "test-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "foo",
							Replicas: 1,
						},
					},
				},
				{
					Namespace: "test-2",
					Name:      "test-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "bar",
							Replicas: 1,
						},
					},
				},
			},
		},
		{
			name:          "no existing binding snapshot to merge",
			existSnapshot: []workv1alpha2.BindingSnapshot{},
			newSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
			expectExistSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotExistSnapshot := mergeBindingSnapshot(tt.existSnapshot, tt.newSnapshot)
			if !reflect.DeepEqual(gotExistSnapshot, tt.expectExistSnapshot) {
				t.Errorf("mergeBindingSnapshot() = %v, want %v", gotExistSnapshot, tt.expectExistSnapshot)
			}
		})
	}
}

func Test_deleteBindingFromSnapshot(t *testing.T) {
	tests := []struct {
		name                string
		bindingNamespace    string
		bindingName         string
		existSnapshot       []workv1alpha2.BindingSnapshot
		expectExistSnapshot []workv1alpha2.BindingSnapshot
	}{
		{
			name:             "delete matching binding from snapshot",
			bindingNamespace: "test",
			bindingName:      "test-binding",
			existSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "test",
					Name:      "test-binding",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "foo",
							Replicas: 1,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "test",
					Name:      "test-binding",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "bar",
							Replicas: 1,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
			expectExistSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
		},
		{
			name:             "non-matching binding from snapshot",
			bindingNamespace: "test",
			bindingName:      "test-binding",
			existSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
			expectExistSnapshot: []workv1alpha2.BindingSnapshot{
				{
					Namespace: "default-1",
					Name:      "default-binding-1",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member1",
							Replicas: 2,
						},
					},
				},
				{
					Namespace: "default-2",
					Name:      "default-binding-2",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member2",
							Replicas: 3,
						},
					},
				},
				{
					Namespace: "default-3",
					Name:      "default-binding-3",
					Clusters: []workv1alpha2.TargetCluster{
						{
							Name:     "member3",
							Replicas: 4,
						},
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotExistSnapshot := deleteBindingFromSnapshot(tt.bindingNamespace, tt.bindingName, tt.existSnapshot)
			if !reflect.DeepEqual(gotExistSnapshot, tt.expectExistSnapshot) {
				t.Errorf("deleteBindingFromSnapshot() = %v, want %v", gotExistSnapshot, tt.expectExistSnapshot)
			}
		})
	}
}
