package endpointservice

import (
	"context"
	"errors"
	"os"
	"testing"

	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6"
	ec2api "github.com/aws/aws-sdk-go-v2/service/ec2"
	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
	elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"
	observabilitypingcapcomv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/metrics"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/pkg/common/utils"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/fake"
	mock_manager "github.com/tidbcloud/observability/pkg/resourceworker/controllers/fake/manager"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/reconciler"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers"
	mocks "github.com/tidbcloud/observability/test/mockresourceworker/providers"
	"go.uber.org/mock/gomock"
	computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
	k8sapiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	ctrlconfig "sigs.k8s.io/controller-runtime/pkg/config"
)

type args struct {
	cli    func(g Gomega) client.Client
	epscli func(ctrl *gomock.Controller, g Gomega) providers.EndpointServiceClient
	elbcli func(ctrl *gomock.Controller, g Gomega) providers.ELBClient
	gcpcli func(ctrl *gomock.Controller, g Gomega) providers.PSCClient
	azcli  func(ctrl *gomock.Controller, g Gomega) providers.PrivateLinkSvcClient
	// alicli   func(ctrl *gomock.Controller, g Gomega) providers.AliEndpointServiceClient
	resource func() client.Object
	vendor   string
}

type want struct {
	result  interface{}
	err     error
	paniced bool
}
type caseType map[string]struct {
	args args
	want want
}

func TestEPSControllerBuildStruct(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	manager := mock_manager.NewMockManager(ctl)
	errBoom := errors.New("boom")
	mockCli := &fake.MockClient{
		MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
			return errBoom
		}),
	}
	scheme := runtime.NewScheme()
	log := ctrl.Log.WithName("controllers").WithName("EndpointService")
	region := "testregion"
	serviceinfo := structs.ServiceInfo{
		Region: region,
	}
	manager.EXPECT().GetClient().AnyTimes().Return(mockCli)
	eps := NewEndpointServiceReconciler(manager, log, serviceinfo)
	g.Expect(eps.Log).Should(Equal(log))
	g.Expect(eps.reconciler.Client).Should(Equal(mockCli))
	utilruntime.Must(observabilitypingcapcomv1.AddToScheme(scheme))
	// manager.EXPECT().SetFields(gomock.Any()).AnyTimes().Return(nil)
	manager.EXPECT().Add(gomock.Any()).AnyTimes().Return(nil)
	manager.EXPECT().GetControllerOptions().Return(ctrlconfig.Controller{}).AnyTimes()
	manager.EXPECT().GetScheme().Return(scheme)
	manager.EXPECT().GetCache().Return(nil)
	manager.EXPECT().GetLogger().Return(log)
	g.Expect(eps.SetupWithManager(manager)).Should(Succeed())
	g.Expect(eps.Reconcile(context.TODO(), ctrl.Request{})).Error().Should(HaveOccurred())
}

func TestEPSControllerConnect(t *testing.T) {
	os.Setenv("AWS_ACCESS_KEY_ID", "test1")
	os.Setenv("AWS_SECRET_ACCESS_KEY", "testsec")
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "../../../../test/files/application_default_credentials.json")
	defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
	g := NewGomegaWithT(t)
	var connect = endpointServiceConnector{
		serviceInfo: structs.ServiceInfo{
			Vendor: string(constants.AWS),
		},
	}
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.EndpointService{})).Error().Should(Succeed())
	connect.serviceInfo.Vendor = string(constants.GCP)
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.EndpointService{})).Error().Should(Succeed())
	connect.serviceInfo.Vendor = string(constants.AliCloud)
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.EndpointService{})).Error().Should(Succeed())
}

func TestEPSControllerObserve(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"ObserveErrorResourceType": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					return mocks.NewMockEndpointServiceClient(ctl)
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				result: reconciler.ExternalObservation{Droped: true},
				err:    errors.New(errNotEndpointService),
			},
		},
		"ObserveFinalStatus": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					return mocks.NewMockEndpointServiceClient(ctl)
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Status.Phase = constants.CREATION_FAILED
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
			},
		},
		"ObserveSharedWaitEndpointServiceID": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true, ResourceUpToDate: false, PollInterval: pollInterval},
			},
		},
		"ObserveSharedWaitServiceID": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveSharedSuccess": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Status.ServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true},
			},
		},
		"ObserveWaitPromesvcError": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {

							return errBoom
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveWaitPromesvc": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {

							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false, ResourceUpToDate: false, PollInterval: pollInterval},
				err:    nil,
			},
		},
		"ObserveGetELBFailed": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(nil, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveGetELBStatFailed": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(&elbtypes.LoadBalancer{
						State: &elbtypes.LoadBalancerState{
							Code: elbtypes.LoadBalancerStateEnumFailed,
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
			},
		},
		"ObserveGetELBStatWait": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(&elbtypes.LoadBalancer{
						State: &elbtypes.LoadBalancerState{
							Code: elbtypes.LoadBalancerStateEnumProvisioning,
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false, ResourceUpToDate: false, PollInterval: pollInterval},
			},
		},
		"ObserveNeddCreate": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(&elbtypes.LoadBalancer{
						State: &elbtypes.LoadBalancerState{
							Code: elbtypes.LoadBalancerStateEnumActive,
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveUpdate": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Namespace = "test1"
					res.Spec.ServiceName = "test1"
					res.Status.ServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true},
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			ctl := gomock.NewController(t)
			defer ctl.Finish()
			r := endpointService{
				epscli: tc.args.epscli(ctl, g),
				elbcli: tc.args.elbcli(ctl, g),
				cli:    tc.args.cli(g),
			}
			if tc.want.paniced {
				g.Expect(func() { r.Observe(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotEndpointService))
			} else {
				got, err := r.Observe(context.Background(), tc.args.resource())
				if tc.want.err == nil {
					g.Expect(err).Should(BeNil())
				} else {
					g.Expect(err).Should(Equal(tc.want.err))
				}
				g.Expect(got).Should(Equal(tc.want.result))
			}

		})
	}
}

func TestEPSControllerCreate(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"CreateErrorResourceType": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					return mocks.NewMockEndpointServiceClient(ctl)
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				err: errors.New(errNotEndpointService),
			},
		},
		"CreateSharedGetEndpointServiceFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().GetEndpointService(context.Background(), "test1").Return(nil, errBoom)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateSharedGetEndpointServiceFailedGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					cli.EXPECT().GetPSCService(gomock.Any(), gomock.Any()).Return(nil, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},

		"CreateSharedGetEndpointServiceFailedAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azcli: func(ctrl *gomock.Controller, g Gomega) providers.PrivateLinkSvcClient {
					cli := mocks.NewMockPrivateLinkSvcClient(ctrl)
					cli.EXPECT().Get(context.Background(), "test1").Return(nil, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateSharedARNisNull": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().GetEndpointService(context.Background(), "test1").Return(&ec2types.ServiceConfiguration{
						ServiceId:   utils.StringPtr("test1"),
						ServiceName: utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errors.New(errNoUserArnInSharedMode),
			},
		},
		"CreateSharedUpdateFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Labels[labelID]).Should(Equal("test1"))
							g.Expect(eps.Labels[labelShared]).Should(Equal("test1"))
							return errBoom
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().GetEndpointService(context.Background(), "test1").Return(&ec2types.ServiceConfiguration{
						ServiceId:   utils.StringPtr("test1"),
						ServiceName: utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.UserARNs = append(res.Spec.UserARNs, "arn::aws::iam::test1")
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateUpdateStatusFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.CREATING))
							return errBoom
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Labels[labelID]).Should(Equal("test1"))
							g.Expect(eps.Labels[labelShared]).Should(Equal("test1"))
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().GetEndpointService(context.Background(), "test1").Return(&ec2types.ServiceConfiguration{
						ServiceId:   utils.StringPtr("test1"),
						ServiceName: utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.UserARNs = append(res.Spec.UserARNs, "arn::aws::iam::test1")
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateSharedEndpointSuccessGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Labels[labelID]).Should(Equal("test1"))
							g.Expect(eps.Labels[labelShared]).Should(Equal("test1"))
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					cli.EXPECT().GetPSCService(gomock.Any(), gomock.Any()).Return(&computepb.ServiceAttachment{
						Name:     utils.StringPtr("test1"),
						SelfLink: utils.StringPtr("https://www.googleapis.com/compute/v1/test1"),
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.UserARNs = append(res.Spec.UserARNs, "arn::aws::iam::test1")
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: nil,
			},
		},

		"CreateSharedEndpointSuccessAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Labels[labelID]).Should(Equal("test1"))
							g.Expect(eps.Labels[labelShared]).Should(Equal("test1"))
							return nil
						}),
					}
				},
				azcli: func(ctrl *gomock.Controller, g types.Gomega) providers.PrivateLinkSvcClient {
					cli := mocks.NewMockPrivateLinkSvcClient(ctrl)
					cli.EXPECT().Get(gomock.Any(), gomock.Any()).Return(&armnetwork.PrivateLinkService{
						Name: utils.StringPtr("test1"),
						ID:   utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					res.Spec.UserARNs = append(res.Spec.UserARNs, "arn::aws::iam::test1")
					res.Spec.Shared = true
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"CreateGetServiceFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							return errBoom
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateServiceHostisNull": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				err: errors.New(errNoServiceHostName),
			},
		},
		"CreateGetLoadBalancerFailed": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(nil, errBoom)
					return cli
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateCreateEndpointServiceFailed": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().CreateEndpointService(context.Background(), "test1", []string{"test1"}).Return(nil, errBoom)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(&elbtypes.LoadBalancer{
						LoadBalancerArn: utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateSuccess": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							promsvc := obj.(*k8sapiv1.Service)
							promsvc.Status.LoadBalancer.Ingress = append(promsvc.Status.LoadBalancer.Ingress, k8sapiv1.LoadBalancerIngress{
								Hostname: "test1-xxx",
							})
							return nil
						}),
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().CreateEndpointService(context.Background(), "test1", []string{"test1"}).Return(&ec2api.CreateVpcEndpointServiceConfigurationOutput{
						ServiceConfiguration: &ec2types.ServiceConfiguration{
							ServiceId:   utils.StringPtr("test1"),
							ServiceName: utils.StringPtr("test1"),
						},
					}, nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					cli := mocks.NewMockELBClient(ctl)
					cli.EXPECT().GetLoadBalancer(context.Background(), "test1").Return(&elbtypes.LoadBalancer{
						LoadBalancerArn: utils.StringPtr("test1"),
					}, nil)
					return cli
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			ctl := gomock.NewController(t)
			defer ctl.Finish()
			r := endpointService{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}
			if tc.args.epscli != nil {
				r.epscli = tc.args.epscli(ctl, g)
			}
			if tc.args.elbcli != nil {
				r.elbcli = tc.args.elbcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.args.azcli != nil {
				r.azcli = tc.args.azcli(ctl, g)
			}

			if tc.want.paniced {
				g.Expect(func() { r.Create(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotEndpointService))
			} else {
				err := r.Create(context.Background(), tc.args.resource())
				if tc.want.err == nil {
					g.Expect(err).Should(BeNil())
				} else {
					g.Expect(err).Should(Equal(tc.want.err))
				}
			}

		})
	}
}

func TestEPSControllerUpdate(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"UpdateErrorResourceType": {
			args: args{
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					return mocks.NewMockEndpointServiceClient(ctl)
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				err: errors.New(errNotEndpointService),
			},
		},
		"UpdateAddUserPermissionsFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().AddUserPermissions(context.Background(), "test1", []string{"test1"}).Return(errBoom)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"UpdateAddUserPermissionsSuccessGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					cli.EXPECT().GetPSCService(gomock.Any(), gomock.Any()).Return(&computepb.ServiceAttachment{}, nil)
					cli.EXPECT().SetPSCService(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"UpdateAddUserPermissionsSuccessAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
					}
				},
				azcli: func(ctrl *gomock.Controller, g Gomega) providers.PrivateLinkSvcClient {
					cli := mocks.NewMockPrivateLinkSvcClient(ctrl)
					cli.EXPECT().AddUserPermissions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"UpdateStatusFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.ACTIVE))
							return errBoom
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().AddUserPermissions(context.Background(), "test1", []string{"test1"}).Return(nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"UpdateSucess": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							eps := obj.(*observabilitypingcapcomv1.EndpointService)
							g.Expect(eps.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().AddUserPermissions(context.Background(), "test1", []string{"test1"}).Return(nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			ctl := gomock.NewController(t)
			defer ctl.Finish()
			r := endpointService{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}
			if tc.args.azcli != nil {
				r.azcli = tc.args.azcli(ctl, g)
			}
			if tc.args.epscli != nil {
				r.epscli = tc.args.epscli(ctl, g)
			}
			if tc.args.elbcli != nil {
				r.elbcli = tc.args.elbcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}

			if tc.want.paniced {
				g.Expect(func() { r.Update(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotEndpointService))
			} else {
				err := r.Update(context.Background(), tc.args.resource())
				if tc.want.err == nil {
					g.Expect(err).Should(BeNil())
				} else {
					g.Expect(err).Should(Equal(tc.want.err))
				}
			}

		})
	}
}

func TestEPSControllerDelete(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"DeleteErrorResourceType": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					return mocks.NewMockEndpointServiceClient(ctl)
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errors.New(errNotEndpointService),
			},
		},
		"DeleteSharedListFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return errBoom
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteSharedDeleteUserPermissionsFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().DeleteUserPermissions(context.Background(), "test1", []string{"test1"}).Return(errBoom)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteSharedSucess": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().DeleteUserPermissions(context.Background(), "test1", []string{"test1"}).Return(nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteSharedDeleteUserPermissionsErrorAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return nil
						}),
					}
				},
				azcli: func(ctrl *gomock.Controller, g Gomega) providers.PrivateLinkSvcClient {
					cli := mocks.NewMockPrivateLinkSvcClient(ctrl)
					cli.EXPECT().DeleteUserPermissions(gomock.Any(), gomock.Any(), gomock.Any()).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteSharedSucessAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return nil
						}),
					}
				},
				azcli: func(ctrl *gomock.Controller, g Gomega) providers.PrivateLinkSvcClient {
					cli := mocks.NewMockPrivateLinkSvcClient(ctrl)
					cli.EXPECT().DeleteUserPermissions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteSharedSucessGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockList: fake.MockListFn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
							return nil
						}),
					}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					cli.EXPECT().GetPSCService(gomock.Any(), gomock.Any()).Return(&computepb.ServiceAttachment{
						ConsumerAcceptLists: []*computepb.ServiceAttachmentConsumerProjectLimit{
							{
								ProjectIdOrNum: utils.StringPtr("test2"),
							},
							{
								ProjectIdOrNum: utils.StringPtr("test1"),
							},
						},
					}, nil)
					cli.EXPECT().SetPSCService(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Spec.Shared = true
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteSucess": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				epscli: func(ctl *gomock.Controller, g Gomega) providers.EndpointServiceClient {
					cli := mocks.NewMockEndpointServiceClient(ctl)
					cli.EXPECT().DeleteEndpointService(context.Background(), "test1").Return(nil)
					return cli
				},
				elbcli: func(ctl *gomock.Controller, g Gomega) providers.ELBClient {
					return mocks.NewMockELBClient(ctl)
				},
				gcpcli: func(ctl *gomock.Controller, g Gomega) providers.PSCClient {
					cli := mocks.NewMockPSCClient(ctl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.EndpointService{}
					res.Name = "test1"
					res.Status.ServiceID = "test1"
					res.Spec.UserARNs = []string{"test1"}
					res.Spec.EndpointServiceID = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			ctl := gomock.NewController(t)
			defer ctl.Finish()
			r := endpointService{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}
			if tc.args.azcli != nil {
				r.azcli = tc.args.azcli(ctl, g)
			}
			if tc.args.epscli != nil {
				r.epscli = tc.args.epscli(ctl, g)
			}
			if tc.args.elbcli != nil {
				r.elbcli = tc.args.elbcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.want.paniced {
				g.Expect(func() { r.Delete(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotEndpointService))
			} else {
				got, err := r.Delete(context.Background(), tc.args.resource())
				if tc.want.err == nil {
					g.Expect(err).Should(BeNil())
				} else {
					g.Expect(err).Should(Equal(tc.want.err))
				}
				g.Expect(got).Should(Equal(tc.want.result))
			}

		})
	}
}

func TestEPSControllerSkipEndpointServicePredicate(t *testing.T) {
	g := NewGomegaWithT(t)
	peering := structs.ServiceInfo{
		Vendor:         string(constants.AWS),
		BYOCVPCPeering: structs.BYOCVPCPeeringConfig{RouteTableIDs: []string{"rtb-1"}},
	}
	sharedEPS := func(id string) *observabilitypingcapcomv1.EndpointService {
		res := &observabilitypingcapcomv1.EndpointService{}
		res.Spec.Shared = true
		res.Spec.EndpointServiceID = id
		return res
	}
	// AWS BYOC VPC-peering region without an endpoint service id -> skip.
	g.Expect((&endpointService{serviceInfo: peering}).skipEndpointService(sharedEPS(""))).Should(BeTrue())
	// endpoint service id present -> keep the existing PrivateLink path.
	g.Expect((&endpointService{serviceInfo: peering}).skipEndpointService(sharedEPS("vpce-svc-1"))).Should(BeFalse())
	// no peering routes configured -> keep the existing PrivateLink path.
	g.Expect((&endpointService{serviceInfo: structs.ServiceInfo{Vendor: string(constants.AWS)}}).skipEndpointService(sharedEPS(""))).Should(BeFalse())
	// non-shared endpoint service -> not affected.
	dedicated := &observabilitypingcapcomv1.EndpointService{}
	dedicated.Spec.Shared = false
	g.Expect((&endpointService{serviceInfo: peering}).skipEndpointService(dedicated)).Should(BeFalse())
}

func TestEPSControllerObserveThenCreateMarksActiveForBYOCPeering(t *testing.T) {
	metrics.SetupMetrics()
	g := NewGomegaWithT(t)
	r := endpointService{
		cli: fake.NewMockClient(),
		serviceInfo: structs.ServiceInfo{
			Vendor:         string(constants.AWS),
			BYOCVPCPeering: structs.BYOCVPCPeeringConfig{RouteTableIDs: []string{"rtb-1"}},
		},
	}
	res := &observabilitypingcapcomv1.EndpointService{}
	res.Name = "test1"
	res.Spec.Shared = true

	// Observe should drive Create (ResourceExists=false) instead of waiting for an endpoint service id.
	observation, err := r.Observe(context.Background(), res)
	g.Expect(err).Should(Succeed())
	g.Expect(observation).Should(Equal(reconciler.ExternalObservation{ResourceExists: false}))

	// Create should mark the CR ACTIVE without provisioning a cloud endpoint service.
	g.Expect(r.Create(context.Background(), res)).Should(Succeed())
	g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
}
