package iam

import (
	"context"
	"errors"
	"testing"

	ram "github.com/alibabacloud-go/ram-20150501/v2/client"
	"github.com/tidbcloud/observability/pkg/common/utils"

	iamgcpcloud "cloud.google.com/go/iam"
	"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi"
	"github.com/aws/aws-sdk-go-v2/aws"

	iamapi "github.com/aws/aws-sdk-go-v2/service/iam"
	iamtype "github.com/aws/aws-sdk-go-v2/service/iam/types"
	. "github.com/onsi/gomega"
	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/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"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/gcp"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/types"
	mocks "github.com/tidbcloud/observability/test/mockresourceworker/providers"
	"go.uber.org/mock/gomock"
	iamgcpapi "google.golang.org/api/iam/v1"
	iamgcppb "google.golang.org/genproto/googleapis/iam/v1"
	"google.golang.org/genproto/googleapis/type/expr"
	"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"
	"sigs.k8s.io/controller-runtime/pkg/log"
)

type args struct {
	cli         func(g Gomega) client.Client
	iamcli      func(ctrl *gomock.Controller, g Gomega) providers.IAMClient
	gcpcli      func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP
	azureCli    func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure
	aliCloudCli func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAliCloud
	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 TestIAMControllerBuildStruct(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("IAM")
	region := "testregion"
	serviceinfo := structs.ServiceInfo{
		Region: region,
	}
	manager.EXPECT().GetClient().AnyTimes().Return(mockCli)
	iam := NewIAMReconciler(manager, log, serviceinfo)
	g.Expect(iam.Log).Should(Equal(log))
	g.Expect(iam.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(iam.SetupWithManager(manager)).Should(Succeed())
	g.Expect(iam.Reconcile(context.TODO(), ctrl.Request{})).Error().Should(HaveOccurred())
}

func TestIAMControllerConnect(t *testing.T) {
	g := NewGomegaWithT(t)
	var connect = iamConnector{
		serviceInfo: structs.ServiceInfo{
			Vendor: string(constants.AWS),
		},
	}
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.Cluster{})).Error().To(HaveOccurred())

	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.IAM{
		Spec: observabilitypingcapcomv1.IAMSpec{
			Aws: observabilitypingcapcomv1.AWSIAM{
				Account: "test1",
			},
		},
	})).Error().Should(HaveOccurred())

	connect.serviceInfo.Vendor = string(constants.GCP)
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.IAM{})).Error().Should(Succeed())
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.IAM{
		Spec: observabilitypingcapcomv1.IAMSpec{
			Azure: &observabilitypingcapcomv1.AzureIAM{
				SubscriptionID: "test1",
				ResourceGroup:  "test1",
			},
		},
	})).Error().Should(Succeed())

	connect.serviceInfo.Vendor = string(constants.AliCloud)
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.IAM{})).Error().Should(Succeed())
	g.Expect(connect.Connect(context.TODO(), &observabilitypingcapcomv1.IAM{
		Spec: observabilitypingcapcomv1.IAMSpec{
			AliCloud: observabilitypingcapcomv1.AliCloudIAM{
				Account: "test1",
			},
		},
	})).Error().Should(HaveOccurred())

}

func TestIAMControllerObserve(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"ObserveErrorResourceType": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				result: reconciler.ExternalObservation{Droped: true},
				err:    errors.New(errNotIAM),
			},
		},
		"ObserveRoleNameNotFoundErr": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.IAM{}
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errors.New(errNotFoundRoleName),
			},
		},
		"ObserveFinalStatus": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Status.Phase = constants.CREATION_FAILED
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
			},
		},
		"ObserveGetRoleError": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{}, false, errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveGetRoleErrorAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				aliCloudCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctrl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&ram.GetRoleResponse{}, false, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveGetRoleErrorGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(nil, false, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveGetRoleErrorAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetWorkloadIdentity(gomock.Any(), gomock.Any()).Return(nil, false, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveRoleNotExists": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{}, false, nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleNotExistsGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(nil, false, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleNotExistsGCPExternal": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.Gcp.UseExternalPrincipal = true
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleNotExistsAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetWorkloadIdentity(gomock.Any(), gomock.Any()).Return(nil, false, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleNotExistsAzureExternal": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), "principal-a").Return(nil, false, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						AdditionalRoleMembers: []string{
							"principal-a",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleNotExistsAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				aliCloudCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctrl)
					cli.EXPECT().GetRole(gomock.Any(), gomock.Any()).Return(nil, false, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: false},
			},
		},
		"ObserveRoleequalPolicyError": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{
							AssumeRolePolicyDocument: aws.String("test1"),
						},
					}, true, nil)
					cli.EXPECT().ListRolePolicies(context.Background(), gomock.Any(), gomock.Any()).Return(&iamapi.ListRolePoliciesOutput{}, errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyDocument: "test1",
						PolicyName:     "test1",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errBoom,
			},
		},
		"ObserveRoleequalPolicyEqualed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{
							AssumeRolePolicyDocument: aws.String("%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3Atest%3Arole%2Fprod-test-eks-ap-northeast-1-a1f701f9%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%5D%7D"),
						},
					}, true, nil)
					cli.EXPECT().ListRolePolicies(context.Background(), gomock.Any(), gomock.Any()).Return(&iamapi.ListRolePoliciesOutput{PolicyNames: []string{"test1"}}, nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyDocument: "test1",
						PolicyName:     "test1",
					})
					res.Spec.Aws.AssumeRolePolicyDocument = "eyJWZXJzaW9uIjoiMjAxMi0xMC0xNyIsIlN0YXRlbWVudCI6W3siRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OnRlc3Q6cm9sZS9wcm9kLXRlc3QtZWtzLWFwLW5vcnRoZWFzdC0xLWExZjcwMWY5In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX1dfQ=="
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyEqualedGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test1"},
							},
						},
					}, nil)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyEqualedGCPExternal": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil).Times(2)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.UseExternalPrincipal = true
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "serviceAccount:test1", "foo:goog[bar]")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedGCPExternalMembers": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:other"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil).Times(2)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.UseExternalPrincipal = true
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "serviceAccount:test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyEqualedAzure": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetWorkloadIdentity(gomock.Any(), gomock.Any()).Return(&types.AzureWorkloadIdentity{
						Identity: &armmsi.Identity{
							ID: to.Ptr("test1"),
							Properties: &armmsi.UserAssignedIdentityProperties{
								PrincipalID: to.Ptr("test1"),
							},
						},
						FederatedIdentities: []*armmsi.FederatedIdentityCredentialProperties{
							{
								Subject: to.Ptr("test1"),
								Issuer:  to.Ptr("test1"),
							},
						},
					}, true, nil)
					cli.EXPECT().GetRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), gomock.Any()).Return(&armauthorization.RoleAssignment{
						Properties: &armauthorization.RoleAssignmentProperties{
							Condition: to.Ptr("test1"),
						},
					}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						RoleCondition:           "test1",
						WorkloadServiceAccounts: []string{"test1"},
						WorkloadOIDCIssuer:      "test1",
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyEqualedAzureExternal": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), "principal-a").Return(&armauthorization.RoleAssignment{
						Properties: &armauthorization.RoleAssignmentProperties{Condition: to.Ptr("test1")},
					}, true, nil).Times(2)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						RoleCondition:        "test1",
						AdditionalRoleMembers: []string{
							"principal-a",
						},
						LastAdditionalRoleMembers: []string{
							"principal-a",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAzureExternal": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), "principal-a").Return(&armauthorization.RoleAssignment{
						Properties: &armauthorization.RoleAssignmentProperties{Condition: to.Ptr("test2")},
					}, true, nil).Times(2)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						RoleCondition:        "test1",
						AdditionalRoleMembers: []string{
							"principal-a",
						},
						LastAdditionalRoleMembers: []string{
							"principal-a",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAzureExternalWhenLastDrifted": {
			args: args{
				vendor: string(constants.Azure),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					// getRole() checks existence on desired principal.
					cli.EXPECT().GetRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), "principal-a").Return(&armauthorization.RoleAssignment{
						Properties: &armauthorization.RoleAssignmentProperties{Condition: to.Ptr("test1")},
					}, true, nil).Times(1)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						RoleCondition:        "test1",
						AdditionalRoleMembers: []string{
							"principal-a",
						},
						LastAdditionalRoleMembers: []string{
							"principal-a",
							"principal-b",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyEqualedAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&ram.GetRoleResponse{
						Body: &ram.GetRoleResponseBody{
							Role: &ram.GetRoleResponseBodyRole{
								RoleName:                 utils.StringPtr("test1"),
								AssumeRolePolicyDocument: utils.StringPtr("%7B%22Statement%22%3A%5B%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22oidc%3Aaud%22%3A%22sts.aliyuncs.com%22%2C%22oidc%3Asub%22%3A%22system%3Aserviceaccount%3Adev-devtier-dev-devtier%3Adev-devtier-dev-devtier%22%7D%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%5B%22acs%3Aram%3A%3A5092239846523038%3Aoidc-provider%2Fack-rrsa-c21a0a5e390ff46a8b006997fbc85a054%22%5D%7D%7D%2C%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22RAM%22%3A%5B%22acs%3Aram%3A%3A5654539846535456%3Arole%2Fdev-ap-southeast-1-f01-oss-ram%22%5D%7D%7D%5D%2C%22Version%22%3A%221%22%7D"),
							},
						},
					}, true, nil)
					cli.EXPECT().ListRolePolicies(context.Background(), gomock.Any(), gomock.Any()).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test1"),
									},
								},
							},
						},
					}, nil)
					cli.EXPECT().GetCustomPolicyDocument(context.Background(), "test1").Return(`{"Version":"1","Statement":[]}`, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.AliCloud.Policies = append(res.Spec.AliCloud.Policies, observabilitypingcapcomv1.AliCloudPolicy{
						PolicyDocument: "eyJWZXJzaW9uIjoiMSIsIlN0YXRlbWVudCI6W119",
						PolicyName:     "test1",
					})
					res.Spec.AliCloud.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOlt7IkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7IlN0cmluZ0VxdWFscyI6eyJvaWRjOmF1ZCI6InN0cy5hbGl5dW5jcy5jb20iLCJvaWRjOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZXYtZGV2dGllci1kZXYtZGV2dGllcjpkZXYtZGV2dGllci1kZXYtZGV2dGllciJ9fSwiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjpbImFjczpyYW06OjUwOTIyMzk4NDY1MjMwMzg6b2lkYy1wcm92aWRlci9hY2stcnJzYS1jMjFhMGE1ZTM5MGZmNDZhOGIwMDY5OTdmYmM4NWEwNTQiXX19LHsiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9LCJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJSQU0iOlsiYWNzOnJhbTo6NTY1NDUzOTg0NjUzNTQ1Njpyb2xlL2Rldi1hcC1zb3V0aGVhc3QtMS1mMDEtb3NzLXJhbSJdfX1dLCJWZXJzaW9uIjoiMSJ9"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: true, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAssumeRolePolicyGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test2"},
							},
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAssumeRolePolicyLenthGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test2"},
							},
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1", "test2")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAssumeRolePolicyNotinGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test2"},
							},
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1", "test2", "test3")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedGCSPoliciesLengthGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test1"},
							},
						},
					}, nil)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t2"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y_t2')",
								},
							},
						},
					}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedGCSPoliciesGCP": {
			args: args{
				vendor: string(constants.GCP),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test1"},
							},
						},
					}, nil)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t2",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{
							AssumeRolePolicyDocument: aws.String("%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3Atest%3Arole%2Fprod-test-eks-ap-northeast-1-a1f701f9%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%5D%7D"),
						},
					}, true, nil)
					cli.EXPECT().ListRolePolicies(context.Background(), gomock.Any(), gomock.Any()).Return(&iamapi.ListRolePoliciesOutput{PolicyNames: []string{"test1", "test2"}}, nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyDocument: "test1",
						PolicyName:     "test1",
					})
					res.Spec.Aws.AssumeRolePolicyDocument = "eyJWZXJzaW9uIjoiMjAxMi0xMC0xNyIsIlN0YXRlbWVudCI6W3siRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OnRlc3Q6cm9sZS9wcm9kLXRlc3QtZWtzLWFwLW5vcnRoZWFzdC0xLWExZjcwMWY5In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX1dfQ=="
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveRoleequalPolicyNotEqualedAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&ram.GetRoleResponse{
						Body: &ram.GetRoleResponseBody{
							Role: &ram.GetRoleResponseBodyRole{
								RoleName:                 utils.StringPtr("test1"),
								AssumeRolePolicyDocument: utils.StringPtr("%7B%22Statement%22%3A%5B%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22oidc%3Aaud%22%3A%22sts.aliyuncs.com%22%2C%22oidc%3Asub%22%3A%22system%3Aserviceaccount%3Adev-devtier-dev-devtier%3Adev-devtier-dev-devtier%22%7D%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%5B%22acs%3Aram%3A%3A5092239846523038%3Aoidc-provider%2Fack-rrsa-c21a0a5e390ff46a8b006997fbc85a054%22%5D%7D%7D%2C%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22RAM%22%3A%5B%22acs%3Aram%3A%3A5654539846535456%3Arole%2Fdev-ap-southeast-1-f01-oss-ram%22%5D%7D%7D%5D%2C%22Version%22%3A%221%22%7D"),
							},
						},
					}, true, nil)
					cli.EXPECT().ListRolePolicies(context.Background(), gomock.Any(), gomock.Any()).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test2"),
									},
								},
							},
						},
					}, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.ACTIVE
					res.Spec.AliCloud.Policies = append(res.Spec.AliCloud.Policies, observabilitypingcapcomv1.AliCloudPolicy{
						PolicyDocument: "test1",
						PolicyName:     "test1",
					})
					res.Spec.AliCloud.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOlt7IkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7IlN0cmluZ0VxdWFscyI6eyJvaWRjOmF1ZCI6InN0cy5hbGl5dW5jcy5jb20iLCJvaWRjOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZXYtZGV2dGllci1kZXYtZGV2dGllcjpkZXYtZGV2dGllci1kZXYtZGV2dGllciJ9fSwiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjpbImFjczpyYW06OjUwOTIyMzk4NDY1MjMwMzg6b2lkYy1wcm92aWRlci9hY2stcnJzYS1jMjFhMGE1ZTM5MGZmNDZhOGIwMDY5OTdmYmM4NWEwNTQiXX19LHsiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9LCJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJSQU0iOlsiYWNzOnJhbTo6NTY1NDUzOTg0NjUzNTQ1Njpyb2xlL2Rldi1hcC1zb3V0aGVhc3QtMS1mMDEtb3NzLXJhbSJdfX1dLCJWZXJzaW9uIjoiMSJ9"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalObservation{ResourceUpToDate: false, ResourceExists: true},
				err:    nil,
			},
		},
		"ObserveUpdateStatus": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().GetRole(context.Background(), gomock.Any()).Return(&iamapi.GetRoleOutput{}, true, nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyDocument: "test1",
						PolicyName:     "test1",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "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 := IAM{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}
			if tc.args.iamcli != nil {
				r.iamcli = tc.args.iamcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.args.azureCli != nil {
				r.azureCli = tc.args.azureCli(ctl, g)
			}
			if tc.args.aliCloudCli != nil {
				r.aliCli = tc.args.aliCloudCli(ctl, g)
			}

			if tc.want.paniced {
				g.Expect(func() { r.Observe(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotIAM))
			} 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 TestIAMControllerCreate(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"CreateErrorResourceType": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				aliCloudCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctrl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				err: errors.New(errNotIAM),
			},
		},
		"CreateRoleNameNotFoundErr": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.IAM{}
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errors.New(errNotFoundRoleName),
			},
		},
		"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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return errBoom
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateCreateRoleError": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().CreateRole(context.Background(), "test1", "test1").Return(errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.Aws.AssumeRolePolicyDocument = "dGVzdDE="
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreatePutRolePolicyError": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().CreateRole(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().PutRolePolicy(context.Background(), "test1", "test1", "test1").Return(errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.Aws.AssumeRolePolicyDocument = "dGVzdDE="
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyName:     "test1",
						PolicyDocument: "dGVzdDE=",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"CreateSucess": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().CreateRole(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().PutRolePolicy(context.Background(), "test1", "test1", "test1").Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.Aws.AssumeRolePolicyDocument = "dGVzdDE="
					res.Spec.Aws.Policies = append(res.Spec.Aws.Policies, observabilitypingcapcomv1.AWSRolePolicy{
						PolicyName:     "test1",
						PolicyDocument: "dGVzdDE=",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"CreateSucessGCP": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().CreateServiceAccount(gomock.Any(), "test1").Return(&iamgcpapi.ServiceAccount{
						Email: "test1@project1.gserviceaccount.com",
					}, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), "test1").Return(&iamgcpapi.Policy{}, nil)
					cli.EXPECT().SetServiceAccountIAM(gomock.Any(), "test1", &iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test1"},
							},
						}}).Return(nil)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{}, true, nil)
					cli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", &iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1@project1.gserviceaccount.com", "test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"CreateSucessAzure": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().CreateUserIdentity(gomock.Any(), "test1").Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, nil)
					cli.EXPECT().CreateOrUpdateFederatedIdentity(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					cli.EXPECT().CreateOrUpdateRoleAssignmentForPrincipal(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"CreateSucessAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.CREATING))
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().CreateRole(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().CreateRolePolicy(context.Background(), "test1", "test1", "test1").Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					//test1
					res.Spec.AliCloud.AssumeRolePolicyDocument = "dGVzdDE="
					res.Spec.AliCloud.Policies = append(res.Spec.AliCloud.Policies, observabilitypingcapcomv1.AliCloudPolicy{
						PolicyName:     "test1",
						PolicyDocument: "dGVzdDE=",
					})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "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 := IAM{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}
			if tc.args.iamcli != nil {
				r.iamcli = tc.args.iamcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.args.azureCli != nil {
				r.azureCli = tc.args.azureCli(ctl, g)
			}
			if tc.args.aliCloudCli != nil {
				r.aliCli = tc.args.aliCloudCli(ctl, g)
			}
			if tc.want.paniced {
				g.Expect(func() { r.Create(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotIAM))
			} 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 TestIAMControllerUpdate(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")
	cases := caseType{
		"UpdateErrorResourceType": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				err: errors.New(errNotIAM),
			},
		},
		"UpdateRoleNameNotFoundErr": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.IAM{}
				},
			},
			want: want{
				result: reconciler.ExternalObservation{},
				err:    errors.New(errNotFoundRoleName),
			},
		},
		"UpdatePolicyFailed": {
			args: args{
				vendor: string(constants.AWS),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(nil, errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"UpdatePolicyAdd": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1"},
					}, nil)
					cli.EXPECT().PutRolePolicy(context.Background(), "test1", "test2", "test2").Return(nil)
					cli.EXPECT().GetRole(context.Background(), "test1").Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{},
					}, true, nil)
					cli.EXPECT().UpdateAssumeRolePolicy(context.Background(), "test1", `{"Version":"","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn"},"Action":""}]}`).Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.Aws.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="
					res.Spec.Aws.Policies = []observabilitypingcapcomv1.AWSRolePolicy{
						{
							PolicyName:     "test1",
							PolicyDocument: "dGVzdDE=",
						},
						{
							PolicyName:     "test2",
							PolicyDocument: "dGVzdDI=",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"UpdatePolicyDelete": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1", "test2"},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test2").Return(nil)
					cli.EXPECT().GetRole(context.Background(), "test1").Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{},
					}, true, nil)
					cli.EXPECT().UpdateAssumeRolePolicy(context.Background(), "test1", `{"Version":"","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn"},"Action":""}]}`).Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.Aws.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="
					res.Spec.Aws.Policies = []observabilitypingcapcomv1.AWSRolePolicy{
						{
							PolicyName:     "test1",
							PolicyDocument: "dGVzdDE=",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return errBoom
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1"},
					}, nil)
					cli.EXPECT().GetRole(context.Background(), "test1").Return(&iamapi.GetRoleOutput{
						Role: &iamtype.Role{},
					}, true, nil)
					cli.EXPECT().UpdateAssumeRolePolicy(context.Background(), "test1", `{"Version":"","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn"},"Action":""}]}`).Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Spec.Aws.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="
					res.Spec.Aws.Policies = []observabilitypingcapcomv1.AWSRolePolicy{
						{
							PolicyName:     "test1",
							PolicyDocument: "dGVzdDE=",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"UpdateSetAccountIAMFailedGCP": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{}, nil).Times(2)
					cli.EXPECT().SetServiceAccountIAM(gomock.Any(), gomock.Any(), gomock.Any()).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1")
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: errBoom,
			},
		},
		"UpdateSuccessGCP": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetServiceAccount(gomock.Any(), gomock.Any()).Return(&iamgcpapi.ServiceAccount{}, true, nil)
					cli.EXPECT().GetServiceAccountIAM(gomock.Any(), gomock.Any()).Return(&iamgcpapi.Policy{
						Bindings: []*iamgcpapi.Binding{
							{
								Role:    gcp.AccessServiceAccountRole,
								Members: []string{"test1"},
							},
						},
					}, nil).Times(2)
					cli.EXPECT().SetServiceAccountIAM(gomock.Any(), gomock.Any(), gomock.Any())
					cli.EXPECT().GetBucketPolicy(gomock.Any(), gomock.Any()).Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "wantdeleted"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      "otherpolicy",
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil)
					cli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", &iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1@.iam.gserviceaccount.com", "test1", "test2"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      "otherpolicy",
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1@.iam.gserviceaccount.com", "test1", "test2"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t2"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1", "test2")
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					},
						observabilitypingcapcomv1.GCPRolePolicy{
							PolicyName:      "t2",
							PolicyRole:      "roles/storage.admin",
							PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
						})
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"UpdateSuccessAzure": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
						MockGet: fake.MockGetFn(func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetUserIdentity(gomock.Any(), gomock.Any()).Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, true, nil)
					cli.EXPECT().CreateOrUpdateFederatedIdentity(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					cli.EXPECT().CreateOrUpdateRoleAssignmentForPrincipal(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
					cli.EXPECT().GetWorkloadIdentity(gomock.Any(), gomock.Any()).Return(&types.AzureWorkloadIdentity{
						Identity: &armmsi.Identity{
							Properties: &armmsi.UserAssignedIdentityProperties{
								ClientID:    to.Ptr("test1"),
								PrincipalID: to.Ptr("test1"),
							},
						},
					}, true, nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Status.Phase = constants.CREATING
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						SubscriptionID:          "test1",
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
						RoleScope:               "test1",
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				err: nil,
			},
		},
		"UpdatePolicyAddAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.ACTIVE))
							return nil
						}),
						MockUpdate: fake.MockUpdateFn(func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test1"),
									},
								},
							},
						},
					}, nil)
					cli.EXPECT().CreateRolePolicy(context.Background(), "test1", "test2", "test2").Return(nil)
					cli.EXPECT().UpdateRolePolicy(context.Background(), "test1", "test1", "test1").Return(nil)
					cli.EXPECT().GetRole(context.Background(), "test1").Return(&ram.GetRoleResponse{
						Body: &ram.GetRoleResponseBody{
							Role: &ram.GetRoleResponseBodyRole{
								RoleName: utils.StringPtr("test1"),
							},
						},
					}, true, nil)
					cli.EXPECT().UpdateAssumeRolePolicy(context.Background(), "test1", `{"Version":"1","Statement":[]}`).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Spec.AliCloud.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="
					res.Spec.AliCloud.Policies = []observabilitypingcapcomv1.AliCloudPolicy{
						{
							PolicyName:     "test1",
							PolicyDocument: "dGVzdDE=",
						},
						{
							PolicyName:     "test2",
							PolicyDocument: "dGVzdDI=",
						},
					}
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "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 := IAM{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}

			if tc.args.iamcli != nil {
				r.iamcli = tc.args.iamcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.args.azureCli != nil {
				r.azureCli = tc.args.azureCli(ctl, g)
			}
			if tc.args.aliCloudCli != nil {
				r.aliCli = tc.args.aliCloudCli(ctl, g)
			}
			if tc.want.paniced {
				g.Expect(func() { r.Update(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotIAM))
			} 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 TestIAMControllerDelete(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{}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},

				aliCloudCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctrl)
					return cli
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.S3{}
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errors.New(errNotIAM),
			},
		},
		"DeleteRoleNameNotFoundErr": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					return mocks.NewMockIAMClient(ctl)
				},
				resource: func() client.Object {
					return &observabilitypingcapcomv1.IAM{}
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errors.New(errNotFoundRoleName),
			},
		},
		"DeleteUpdateSattusFailed": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return errBoom
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errBoom,
			},
		},
		"DeleteListRolePoliciesFailed": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(nil, errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errBoom,
			},
		},
		"DeleteDeleteRolePolicyFailed": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1"},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errBoom,
			},
		},
		"DeleteDeleteRoleFailed": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1"},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().DeleteRole(context.Background(), "test1").Return(errBoom)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{},
				err:    errBoom,
			},
		},
		"DeleteSuccess": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
						PolicyNames: []string{"test1"},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().DeleteRole(context.Background(), "test1").Return(nil)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteSuccessGCP": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().DeleteServiceAccount(gomock.Any(), "test1").Return(nil)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1", "test2"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "wantdeleted"),
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      "otherpolicy",
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}, true, nil)
					cli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", &iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"test1"},
								Condition: &expr.Expr{
									Title:      "otherpolicy",
									Expression: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
								},
							},
						},
					}).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1", "test2")
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					},
						observabilitypingcapcomv1.GCPRolePolicy{
							PolicyName:      "wantdeleted",
							PolicyRole:      "roles/storage.admin",
							PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
						})
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteRoleFailedGCP": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				iamcli: func(ctl *gomock.Controller, g Gomega) providers.IAMClient {
					cli := mocks.NewMockIAMClient(ctl)
					return cli
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().DeleteServiceAccount(gomock.Any(), "test1").Return(errBoom)

					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Gcp.AssumeRoleAccounts = append(res.Spec.Gcp.AssumeRoleAccounts, "test1", "test2")
					res.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
					res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = append(res.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")], observabilitypingcapcomv1.GCPRolePolicy{
						PolicyName:      "t1",
						PolicyRole:      "roles/storage.admin",
						PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
					},
						observabilitypingcapcomv1.GCPRolePolicy{
							PolicyName:      "wantdeleted",
							PolicyRole:      "roles/storage.admin",
							PolicyCondition: "resource.name.startsWith('projects/_/buckets/jinwei-test-iam/objects/o11y')",
						})
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteSuccessGCPExternal": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1@project.iam.gserviceaccount.com"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
								},
							},
						},
					}, true, nil)
					cli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", &iamgcpcloud.Policy3{}).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Gcp.UseExternalPrincipal = true
					res.Spec.Gcp.Policies = map[string][]observabilitypingcapcomv1.GCPRolePolicy{
						observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3"): {
							{
								PolicyName:      "t1",
								PolicyRole:      "roles/storage.admin",
								PolicyCondition: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
							},
						},
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteRoleFailedGCPExternalSetBucketPolicy": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				gcpcli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientGCP {
					cli := mocks.NewMockIAMClientGCP(ctrl)
					cli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{
						Bindings: []*iamgcppb.Binding{
							{
								Role:    "roles/storage.admin",
								Members: []string{"serviceAccount:test1@project.iam.gserviceaccount.com"},
								Condition: &expr.Expr{
									Title:      buildGCPConditionTitle(accountConditionPrefix, "test1", "t1"),
									Expression: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
								},
							},
						},
					}, true, nil)
					cli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", &iamgcpcloud.Policy3{}).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Gcp.UseExternalPrincipal = true
					res.Spec.Gcp.Policies = map[string][]observabilitypingcapcomv1.GCPRolePolicy{
						observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3"): {
							{
								PolicyName:      "t1",
								PolicyRole:      "roles/storage.admin",
								PolicyCondition: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
							},
						},
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},

		"DeleteSuccessAzure": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetUserIdentity(gomock.Any(), gomock.Any()).Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, true, nil)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					cli.EXPECT().DeleteUserIdentity(gomock.Any(), gomock.Any()).Return(nil).Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
						RoleScope:               "test1",
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteRoleFailedAzure": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetUserIdentity(gomock.Any(), gomock.Any()).Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, true, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
						RoleScope:               "test1",
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteRoleFailedAzure2": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetUserIdentity(gomock.Any(), gomock.Any()).Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, true, nil)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), gomock.Any()).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
						RoleScope:               "test1",
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteRoleFailedAzure3": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().GetUserIdentity(gomock.Any(), gomock.Any()).Return(&armmsi.Identity{
						Properties: &armmsi.UserAssignedIdentityProperties{
							PrincipalID: to.Ptr("test1"),
						},
					}, true, nil)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
					cli.EXPECT().DeleteUserIdentity(gomock.Any(), gomock.Any()).Return(nil).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						WorkloadOIDCIssuer:      "test1",
						WorkloadServiceAccounts: []string{"test1"},
						RoleScope:               "test1",
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteSuccessAzureExternal": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), "scope1", "principal1").Return(nil)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), "scope1", "principal2").Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						AdditionalRoleMembers: []string{
							"principal1",
						},
						LastAdditionalRoleMembers: []string{
							"principal2",
						},
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteRoleFailedAzureExternal": {
			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 {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				azureCli: func(ctrl *gomock.Controller, g Gomega) providers.IAMClientAzure {
					cli := mocks.NewMockIAMClientAzure(ctrl)
					cli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), "scope1", gomock.Any()).Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					res.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
						UseExternalPrincipal: true,
						RoleScope:            "scope1",
						AdditionalRoleMembers: []string{
							"principal1",
						},
					}
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},

		"DeleteSuccessAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test1"),
									},
								},
							},
						},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().DeleteRole(context.Background(), "test1").Return(nil)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: true},
				err:    nil,
			},
		},
		"DeleteRoleFailedAliCloud": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&ram.ListPoliciesForRoleResponse{}, errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteRoleFailedAliCloud2": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test1"),
									},
								},
							},
						},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
		"DeleteRoleFailedAliCloud3": {
			args: args{
				vendor: string(constants.AliCloud),
				cli: func(g Gomega) client.Client {
					return &fake.MockClient{
						MockStatusUpdate: fake.MockStatusUpdateFn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
							res := obj.(*observabilitypingcapcomv1.IAM)
							g.Expect(res.Status.Phase).Should(Equal(constants.DELETING))
							return nil
						}),
					}
				},
				aliCloudCli: func(ctl *gomock.Controller, g Gomega) providers.IAMClientAliCloud {
					cli := mocks.NewMockIAMClientAliCloud(ctl)
					cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&ram.ListPoliciesForRoleResponse{
						Body: &ram.ListPoliciesForRoleResponseBody{
							Policies: &ram.ListPoliciesForRoleResponseBodyPolicies{
								Policy: []*ram.ListPoliciesForRoleResponseBodyPoliciesPolicy{
									{
										PolicyName: utils.StringPtr("test1"),
									},
								},
							},
						},
					}, nil)
					cli.EXPECT().DeleteRolePolicy(context.Background(), "test1", "test1").Return(nil)
					cli.EXPECT().DeleteRole(context.Background(), "test1").Return(errBoom)
					return cli
				},
				resource: func() client.Object {
					res := &observabilitypingcapcomv1.IAM{}
					res.Name = "test1"
					res.Labels = make(map[string]string)
					res.Labels[constants.LabelAlias] = "test1"
					return res
				},
			},
			want: want{
				result: reconciler.ExternalDeletion{ResourceDeleted: false},
				err:    errBoom,
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			ctl := gomock.NewController(t)
			defer ctl.Finish()
			r := IAM{
				cli:         tc.args.cli(g),
				serviceInfo: structs.ServiceInfo{Vendor: tc.args.vendor},
			}

			if tc.args.iamcli != nil {
				r.iamcli = tc.args.iamcli(ctl, g)
			}
			if tc.args.gcpcli != nil {
				r.gcpcli = tc.args.gcpcli(ctl, g)
			}
			if tc.args.azureCli != nil {
				r.azureCli = tc.args.azureCli(ctl, g)
			}

			if tc.args.aliCloudCli != nil {
				r.aliCli = tc.args.aliCloudCli(ctl, g)
			}
			if tc.want.paniced {
				g.Expect(func() { r.Delete(context.Background(), tc.args.resource()) }).Should(PanicWith(errNotIAM))
			} 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 TestHelperFunctions(t *testing.T) {
	// Test equalMembers
	t.Run("equalMembers", func(t *testing.T) {
		g := NewGomegaWithT(t)
		// Equal members
		g.Expect(equalMembers([]string{"a", "b", "c"}, []string{"a", "b", "c"})).To(BeTrue())
		g.Expect(equalMembers([]string{"a", "b", "c"}, []string{"c", "b", "a"})).To(BeTrue())
		// Different members
		g.Expect(equalMembers([]string{"a", "b"}, []string{"a", "b", "c"})).To(BeFalse())
		g.Expect(equalMembers([]string{"a", "b", "c"}, []string{"a", "b"})).To(BeFalse())
		g.Expect(equalMembers([]string{"a", "b"}, []string{"c", "d"})).To(BeFalse())
		// Empty slices
		g.Expect(equalMembers([]string{}, []string{})).To(BeTrue())
		g.Expect(equalMembers([]string{"a"}, []string{})).To(BeFalse())
	})

	// Test peekPolicy
	t.Run("peekPolicy", func(t *testing.T) {
		g := NewGomegaWithT(t)
		policies := []observabilitypingcapcomv1.AWSRolePolicy{
			{PolicyName: "policy1", PolicyDocument: "doc1"},
			{PolicyName: "policy2", PolicyDocument: "doc2"},
		}
		result := peekPolicy(policies, "policy1")
		g.Expect(result).ToNot(BeNil())
		g.Expect(result.PolicyName).To(Equal("policy1"))
		g.Expect(result.PolicyDocument).To(Equal("doc1"))

		result = peekPolicy(policies, "nonexistent")
		g.Expect(result).To(BeNil())
	})

	// Test peekPolicyAli
	t.Run("peekPolicyAli", func(t *testing.T) {
		g := NewGomegaWithT(t)
		policies := []observabilitypingcapcomv1.AliCloudPolicy{
			{PolicyName: "policy1", PolicyDocument: "doc1"},
			{PolicyName: "policy2", PolicyDocument: "doc2"},
		}
		result := peekPolicyAli(policies, "policy1")
		g.Expect(result).ToNot(BeNil())
		g.Expect(result.PolicyName).To(Equal("policy1"))
		g.Expect(result.PolicyDocument).To(Equal("doc1"))

		result = peekPolicyAli(policies, "nonexistent")
		g.Expect(result).To(BeNil())
	})

	// Test buildGCPConditionTitle
	t.Run("buildGCPConditionTitle", func(t *testing.T) {
		g := NewGomegaWithT(t)
		title := buildGCPConditionTitle("prefix", "account", "policy")
		g.Expect(title).To(Equal("prefixaccountpolicy"))
	})

	// Test isGCPPolicyBinding
	t.Run("isGCPPolicyBinding", func(t *testing.T) {
		g := NewGomegaWithT(t)
		binding := &iamgcppb.Binding{
			Condition: &expr.Expr{
				Title: "o11yraccountpolicy",
			},
		}
		g.Expect(isGCPPolicyBinding(binding, "account")).To(BeTrue())
		g.Expect(isGCPPolicyBinding(binding, "other")).To(BeFalse())

		bindingNoCondition := &iamgcppb.Binding{}
		g.Expect(isGCPPolicyBinding(bindingNoCondition, "account")).To(BeFalse())
	})

	// Test filterGCPWorkWorkloadIdentity
	t.Run("filterGCPWorkWorkloadIdentity", func(t *testing.T) {
		g := NewGomegaWithT(t)
		source := []string{"user1", "goog[project]", "user2", "goog[another]", "user3"}
		result := filterGCPWorkWorkloadIdentity(source)
		g.Expect(result).To(Equal([]string{"user1", "user2", "user3"}))

		emptySource := []string{}
		result = filterGCPWorkWorkloadIdentity(emptySource)
		g.Expect(result).To(BeNil())
	})

	// Test buildAzureRoleAssignmentProperties
	t.Run("buildAzureRoleAssignmentProperties", func(t *testing.T) {
		g := NewGomegaWithT(t)
		props := buildAzureRoleAssignmentProperties("sub1", "principal1", "", nil)
		g.Expect(props.PrincipalID).ToNot(BeNil())
		g.Expect(*props.PrincipalID).To(Equal("principal1"))
		g.Expect(props.PrincipalType).To(BeNil())
		g.Expect(props.Condition).To(BeNil())

		principalType := armauthorization.PrincipalTypeServicePrincipal
		props = buildAzureRoleAssignmentProperties("sub1", "principal1", "test-condition", &principalType)
		g.Expect(props.Condition).ToNot(BeNil())
		g.Expect(*props.Condition).To(Equal("test-condition"))
		g.Expect(props.ConditionVersion).ToNot(BeNil())
		g.Expect(*props.ConditionVersion).To(Equal("2.0"))
		g.Expect(props.PrincipalType).ToNot(BeNil())
		g.Expect(*props.PrincipalType).To(Equal(armauthorization.PrincipalTypeServicePrincipal))
	})

	// Test getGCPPolicyLenth
	t.Run("getGCPPolicyLenth", func(t *testing.T) {
		g := NewGomegaWithT(t)
		bindings := []*iamgcppb.Binding{
			{
				Condition: &expr.Expr{
					Title: "o11yraccountpolicy1",
				},
			},
			{
				Condition: &expr.Expr{
					Title: "o11yraccountpolicy2",
				},
			},
			{
				Condition: &expr.Expr{
					Title: "otherpolicy",
				},
			},
		}
		count := getGCPPolicyLenth(bindings, "account")
		g.Expect(count).To(Equal(2))

		count = getGCPPolicyLenth(bindings, "other")
		g.Expect(count).To(Equal(0))
	})

	// Test isGCPIncludePolicy
	t.Run("isGCPIncludePolicy", func(t *testing.T) {
		g := NewGomegaWithT(t)
		policies := []observabilitypingcapcomv1.GCPRolePolicy{
			{PolicyName: "policy1"},
			{PolicyName: "policy2"},
		}
		binding := &iamgcppb.Binding{
			Condition: &expr.Expr{
				Title: buildGCPConditionTitle(accountConditionPrefix, "account", "policy1"),
			},
		}
		g.Expect(isGCPIncludePolicy("account", binding, policies)).To(BeTrue())

		binding.Condition.Title = buildGCPConditionTitle(accountConditionPrefix, "account", "policy3")
		g.Expect(isGCPIncludePolicy("account", binding, policies)).To(BeFalse())
	})

	// Test peekGCPPolicy
	t.Run("peekGCPPolicy", func(t *testing.T) {
		g := NewGomegaWithT(t)
		bindings := []*iamgcppb.Binding{
			{
				Condition: &expr.Expr{
					Title: "title1",
				},
			},
			{
				Condition: &expr.Expr{
					Title: "title2",
				},
			},
		}
		index, binding := peekGCPPolicy(bindings, "title1")
		g.Expect(index).To(Equal(0))
		g.Expect(binding).ToNot(BeNil())
		g.Expect(binding.Condition.Title).To(Equal("title1"))

		index, binding = peekGCPPolicy(bindings, "nonexistent")
		g.Expect(index).To(Equal(-1))
		g.Expect(binding).To(BeNil())
	})
}

func TestUpdateRoleErrors(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")

	t.Run("UpdateAWSRoleGetRoleError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		cli := mocks.NewMockIAMClient(ctl)
		cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
			PolicyNames: []string{},
		}, nil)
		cli.EXPECT().GetRole(context.Background(), "test1").Return(nil, false, errBoom)

		r := IAM{
			iamcli:      cli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.AWS)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Aws.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="

		err := r.updateAWSRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("UpdateAWSRoleUpdateAssumeRolePolicyError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		cli := mocks.NewMockIAMClient(ctl)
		cli.EXPECT().ListRolePolicies(context.Background(), "test1", nil).Return(&iamapi.ListRolePoliciesOutput{
			PolicyNames: []string{},
		}, nil)
		cli.EXPECT().GetRole(context.Background(), "test1").Return(&iamapi.GetRoleOutput{
			Role: &iamtype.Role{},
		}, true, nil)
		cli.EXPECT().UpdateAssumeRolePolicy(context.Background(), "test1", gomock.Any()).Return(errBoom)

		r := IAM{
			iamcli:      cli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.AWS)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Aws.AssumeRolePolicyDocument = "eyJTdGF0ZW1lbnQiOiBbeyJFZmZlY3QiOiJBbGxvdyIsICJQcmluY2lwYWwiOiB7IkFXUyI6ICJhcm4ifX1dfQ=="

		err := r.updateAWSRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("UpdateGCPRoleGetServiceAccountError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().GetServiceAccount(gomock.Any(), "test1").Return(nil, false, errBoom)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"test1"}

		err := r.updateGCPRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("UpdateGCPRoleGetServiceAccountIAMError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().GetServiceAccount(gomock.Any(), "test1").Return(&iamgcpapi.ServiceAccount{}, true, nil)
		gcpcli.EXPECT().GetServiceAccountIAM(gomock.Any(), "test1").Return(nil, errBoom)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"test1"}

		err := r.updateGCPRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("UpdateGCPRoleExternalPrincipal", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{}, true, nil)
		gcpcli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", gomock.Any()).Return(nil)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         fake.NewMockClient(),
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.UseExternalPrincipal = true
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"serviceAccount:user1@test.iam.gserviceaccount.com"}
		iamv1.Spec.Gcp.Policies = map[string][]observabilitypingcapcomv1.GCPRolePolicy{
			observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3"): {
				{
					PolicyName:      "p1",
					PolicyRole:      "roles/storage.admin",
					PolicyCondition: "resource.name.startsWith('projects/_/buckets/test-s3/objects/')",
				},
			},
		}

		err := r.updateGCPRole(context.Background(), "test1", iamv1)
		g.Expect(err).Should(BeNil())
		g.Expect(iamv1.Spec.Gcp.LastAssumeRoleAccounts).Should(Equal(iamv1.Spec.Gcp.AssumeRoleAccounts))
	})

	t.Run("UpdateAzureRoleGetUserIdentityError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().GetUserIdentity(gomock.Any(), "test1").Return(nil, false, errBoom)

		r := IAM{
			azureCli:    azureCli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.Azure)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			WorkloadOIDCIssuer:      "test1",
			WorkloadServiceAccounts: []string{"test1"},
		}

		err := r.updateAzureRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("UpdateAzureRoleNotExist", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().GetUserIdentity(gomock.Any(), "test1").Return(nil, false, nil)

		r := IAM{
			azureCli:    azureCli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.Azure)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			WorkloadOIDCIssuer:      "test1",
			WorkloadServiceAccounts: []string{"test1"},
		}

		err := r.updateAzureRole(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
		g.Expect(err.Error()).To(ContainSubstring("non-existent"))
	})

	t.Run("UpdateAzureRoleExternalPrincipal", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().CreateOrUpdateRoleAssignmentForPrincipal(gomock.Any(), "scope1", gomock.Any()).Return(nil, nil)

		r := IAM{
			azureCli: azureCli,
			cli:      fake.NewMockClient(),
			serviceInfo: structs.ServiceInfo{
				Vendor: string(constants.Azure),
				AccountInfo: structs.AccountInfoConfig{
					SubscriptionID: "sub1",
				},
			},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			UseExternalPrincipal: true,
			RoleScope:            "scope1",
			AdditionalRoleMembers: []string{
				"principal1",
			},
		}

		err := r.updateAzureRole(context.Background(), "test1", iamv1)
		g.Expect(err).Should(BeNil())
	})

	t.Run("UpdateAzureRoleExternalPrincipalDeleteStaleMember", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		gomock.InOrder(
			azureCli.EXPECT().DeleteRoleAssignmentByPrincipalID(gomock.Any(), "scope1", "principal2").Return(nil),
			azureCli.EXPECT().CreateOrUpdateRoleAssignmentForPrincipal(gomock.Any(), "scope1", gomock.Any()).Return(nil, nil),
		)

		r := IAM{
			azureCli: azureCli,
			cli:      fake.NewMockClient(),
			serviceInfo: structs.ServiceInfo{
				Vendor: string(constants.Azure),
				AccountInfo: structs.AccountInfoConfig{
					SubscriptionID: "sub1",
				},
			},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			UseExternalPrincipal:      true,
			RoleScope:                 "scope1",
			AdditionalRoleMembers:     []string{"principal1"},
			LastAdditionalRoleMembers: []string{"principal1", "principal2"},
		}

		err := r.updateAzureRole(context.Background(), "test1", iamv1)
		g.Expect(err).Should(BeNil())
		g.Expect(iamv1.Spec.Azure.LastAdditionalRoleMembers).Should(Equal([]string{"principal1"}))
	})
}

func TestCreateRoleErrors(t *testing.T) {
	metrics.SetupMetrics()
	errBoom := errors.New("boom")

	t.Run("CreateGCPGetServiceAccountIAMError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().CreateServiceAccount(gomock.Any(), "test1").Return(&iamgcpapi.ServiceAccount{
			Email: "test1@project.iam.gserviceaccount.com",
		}, nil)
		gcpcli.EXPECT().GetServiceAccountIAM(gomock.Any(), "test1").Return(nil, errBoom)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"test1"}

		err := r.CreateGCP(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("CreateGCPSetServiceAccountIAMError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().CreateServiceAccount(gomock.Any(), "test1").Return(&iamgcpapi.ServiceAccount{
			Email: "test1@project.iam.gserviceaccount.com",
		}, nil)
		gcpcli.EXPECT().GetServiceAccountIAM(gomock.Any(), "test1").Return(&iamgcpapi.Policy{}, nil)
		gcpcli.EXPECT().SetServiceAccountIAM(gomock.Any(), "test1", gomock.Any()).Return(errBoom)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"test1"}

		err := r.CreateGCP(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("CreateGCPGetBucketPolicyError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().CreateServiceAccount(gomock.Any(), "test1").Return(&iamgcpapi.ServiceAccount{
			Email: "test1@project.iam.gserviceaccount.com",
		}, nil)
		gcpcli.EXPECT().GetServiceAccountIAM(gomock.Any(), "test1").Return(&iamgcpapi.Policy{}, nil)
		gcpcli.EXPECT().SetServiceAccountIAM(gomock.Any(), "test1", gomock.Any()).Return(nil)
		gcpcli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(nil, false, errBoom)

		r := IAM{
			gcpcli: gcpcli,
			cli:    &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{
				Vendor: string(constants.GCP),
				AccountInfo: structs.AccountInfoConfig{
					Project: "project",
				},
			},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"test1"}
		iamv1.Spec.Gcp.Policies = make(map[string][]observabilitypingcapcomv1.GCPRolePolicy)
		iamv1.Spec.Gcp.Policies[observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3")] = []observabilitypingcapcomv1.GCPRolePolicy{
			{
				PolicyName:      "t1",
				PolicyRole:      "roles/storage.admin",
				PolicyCondition: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
			},
		}

		err := r.CreateGCP(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("CreateGCPExternalPrincipal", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		gcpcli := mocks.NewMockIAMClientGCP(ctl)
		gcpcli.EXPECT().GetBucketPolicy(gomock.Any(), "test-s3").Return(&iamgcpcloud.Policy3{}, true, nil)
		gcpcli.EXPECT().SetBucketPolicy(gomock.Any(), "test-s3", gomock.Any()).Return(nil)

		r := IAM{
			gcpcli:      gcpcli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.GCP)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Gcp.UseExternalPrincipal = true
		iamv1.Spec.Gcp.AssumeRoleAccounts = []string{"serviceAccount:test1@project.iam.gserviceaccount.com"}
		iamv1.Spec.Gcp.Policies = map[string][]observabilitypingcapcomv1.GCPRolePolicy{
			observabilitypingcapcomv1.BuildGCPPolicyKey(observabilitypingcapcomv1.GCPResourceTypeCloudStorage, "test-s3"): {
				{
					PolicyName:      "t1",
					PolicyRole:      "roles/storage.admin",
					PolicyCondition: "resource.name.startsWith('projects/_/buckets/test/objects/o11y')",
				},
			},
		}

		err := r.CreateGCP(context.Background(), "test1", iamv1)
		g.Expect(err).To(BeNil())
	})

	t.Run("CreateAzureCreateUserIdentityError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().CreateUserIdentity(gomock.Any(), "test1").Return(nil, errBoom)

		r := IAM{
			azureCli:    azureCli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.Azure)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			WorkloadOIDCIssuer:      "test1",
			WorkloadServiceAccounts: []string{"test1"},
		}

		err := r.CreateAzure(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("CreateAzureCreateFederatedIdentityError", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().CreateUserIdentity(gomock.Any(), "test1").Return(&armmsi.Identity{
			Properties: &armmsi.UserAssignedIdentityProperties{
				PrincipalID: to.Ptr("test1"),
			},
		}, nil)
		azureCli.EXPECT().CreateOrUpdateFederatedIdentity(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errBoom)

		r := IAM{
			azureCli:    azureCli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.Azure)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			WorkloadOIDCIssuer:      "test1",
			WorkloadServiceAccounts: []string{"test1"},
		}

		err := r.CreateAzure(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
	})

	t.Run("CreateAzurePrincipalIDNil", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().CreateUserIdentity(gomock.Any(), "test1").Return(&armmsi.Identity{
			Properties: &armmsi.UserAssignedIdentityProperties{},
		}, nil)
		azureCli.EXPECT().CreateOrUpdateFederatedIdentity(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)

		r := IAM{
			azureCli:    azureCli,
			cli:         &fake.MockClient{},
			serviceInfo: structs.ServiceInfo{Vendor: string(constants.Azure)},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Name = "test1"
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			WorkloadOIDCIssuer:      "test1",
			WorkloadServiceAccounts: []string{"test1"},
		}

		err := r.CreateAzure(context.Background(), "test1", iamv1)
		g.Expect(err).To(HaveOccurred())
		g.Expect(err.Error()).To(ContainSubstring("principal id is nil"))
	})

	t.Run("CreateAzureExternalPrincipal", func(t *testing.T) {
		g := NewGomegaWithT(t)
		ctl := gomock.NewController(t)
		defer ctl.Finish()

		azureCli := mocks.NewMockIAMClientAzure(ctl)
		azureCli.EXPECT().CreateOrUpdateRoleAssignmentForPrincipal(gomock.Any(), "scope1", gomock.Any()).Return(nil, nil)

		r := IAM{
			azureCli: azureCli,
			cli:      fake.NewMockClient(),
			serviceInfo: structs.ServiceInfo{
				Vendor: string(constants.Azure),
				AccountInfo: structs.AccountInfoConfig{
					SubscriptionID: "sub1",
				},
			},
		}

		iamv1 := &observabilitypingcapcomv1.IAM{}
		iamv1.Spec.Azure = &observabilitypingcapcomv1.AzureIAM{
			UseExternalPrincipal: true,
			RoleScope:            "scope1",
			AdditionalRoleMembers: []string{
				"principal1",
			},
		}

		err := r.CreateAzure(context.Background(), "test1", iamv1)
		g.Expect(err).Should(BeNil())
	})
}

func TestEqualMembersFunction(t *testing.T) {
	g := NewGomegaWithT(t)
	// Test cases already covered in TestHelperFunctions
	g.Expect(equalMembers([]string{"a", "b"}, []string{"a", "b"})).To(BeTrue())
	g.Expect(equalMembers([]string{"a"}, []string{"b"})).To(BeFalse())
	g.Expect(equalMembers([]string{"1", "2", "3"}, []string{"1", "3", "2"})).Should(BeTrue())
	g.Expect(equalMembers([]string{"1", "2", "3"}, []string{"1", "3"})).ShouldNot(BeTrue())
	g.Expect(equalMembers([]string{"1", "2", "3"}, []string{"1", "3", "4"})).ShouldNot(BeTrue())
}

func TestFilterGCPWorkWorkloadIdentity(t *testing.T) {
	g := NewGomegaWithT(t)
	g.Expect(filterGCPWorkWorkloadIdentity([]string{"regional-server@gcp-autonomous-dev.iam.gserviceaccount.com", "gcp-autonomous-dev.svc.id.goog[tidbcloud-unknown/tidbcloud-unknown]"})).Should(Equal([]string{"regional-server@gcp-autonomous-dev.iam.gserviceaccount.com"}))
	g.Expect(filterGCPWorkWorkloadIdentity([]string{"regional-server@gcp-autonomous-dev.iam.gserviceaccount.com", "gcp-autonomous-dev.svc.id.goog[tidbcloud-unknown/tidbcloud-unknown]", "autonomous-service@pingcap.com"})).Should(Equal([]string{"regional-server@gcp-autonomous-dev.iam.gserviceaccount.com", "autonomous-service@pingcap.com"}))
}

func Test_AssumeRolePolicyDocumentCompare(t *testing.T) {
	g := NewGomegaWithT(t)
	r := IAM{}
	AWSassumeRolePolicyDocument := `%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%22arn%3Aaws%3Aiam%3A%3A557537366020%3Aoidc-provider%2Foidc.eks.ap-northeast-1.amazonaws.com%2Fid%2F25469AE9DFB4083DEFE5C661A22CB550%22%7D%2C%22Action%22%3A%22sts%3AAssumeRoleWithWebIdentity%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22oidc.eks.ap-northeast-1.amazonaws.com%2Fid%2F25469AE9DFB4083DEFE5C661A22CB550%3Asub%22%3A%22system%3Aserviceaccount%3Atidbcloud-1372813089194211283%3Atidbcloud-1372813089194211283%22%2C%22oidc.eks.ap-northeast-1.amazonaws.com%2Fid%2F25469AE9DFB4083DEFE5C661A22CB550%3Aaud%22%3A%22sts.amazonaws.com%22%7D%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A380838443567%3Arole%2Fshoot--prod--eeffffef-nodes%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A380838443567%3Arole%2Fshoot--prod--2ad8c8ce-nodes%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A380838443567%3Arole%2Fprod-1372813089191811284-eks-ap-northeast-1-749a81fe%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A143458967504%3Arole%2Fprod-1372813089293211288-eks-ap-northeast-1-64930d0a%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A143458967504%3Arole%2Fprod-1372813089206661298-eks-ap-northeast-1-d781be98%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A143458967504%3Arole%2Fprod-1372813089387681287-eks-ap-northeast-1-7492ed54%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A380838443567%3Arole%2Fprod-1372813089206331463-eks-ap-northeast-1-53fd018e%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%2C%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A143458967504%3Arole%2Fprod-1372813089206451543-eks-ap-northeast-1-a1f701f9%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%7D%5D%7D`
	CRDAssumDocment := "eyJWZXJzaW9uIjoiMjAxMi0xMC0xNyIsIlN0YXRlbWVudCI6W3siRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjoiYXJuOmF3czppYW06OjU1NzUzNzM2NjAyMDpvaWRjLXByb3ZpZGVyL29pZGMuZWtzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20vaWQvMjU0NjlBRTlERkI0MDgzREVGRTVDNjYxQTIyQ0I1NTAifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHkiLCJDb25kaXRpb24iOnsiU3RyaW5nRXF1YWxzIjp7Im9pZGMuZWtzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20vaWQvMjU0NjlBRTlERkI0MDgzREVGRTVDNjYxQTIyQ0I1NTA6YXVkIjoic3RzLmFtYXpvbmF3cy5jb20iLCJvaWRjLmVrcy5hcC1ub3J0aGVhc3QtMS5hbWF6b25hd3MuY29tL2lkLzI1NDY5QUU5REZCNDA4M0RFRkU1QzY2MUEyMkNCNTUwOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0aWRiY2xvdWQtMTM3MjgxMzA4OTE5NDIxMTI4Mzp0aWRiY2xvdWQtMTM3MjgxMzA4OTE5NDIxMTI4MyJ9fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvc2hvb3QtLXByb2QtLWVlZmZmZmVmLW5vZGVzIn0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvc2hvb3QtLXByb2QtLTJhZDhjOGNlLW5vZGVzIn0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvcHJvZC0xMzcyODEzMDg5MTkxODExMjg0LWVrcy1hcC1ub3J0aGVhc3QtMS03NDlhODFmZSJ9LCJBY3Rpb24iOiJzdHM6QXNzdW1lUm9sZSIsIkNvbmRpdGlvbiI6e319LHsiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OjE0MzQ1ODk2NzUwNDpyb2xlL3Byb2QtMTM3MjgxMzA4OTI5MzIxMTI4OC1la3MtYXAtbm9ydGhlYXN0LTEtNjQ5MzBkMGEifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9fSx7IkVmZmVjdCI6IkFsbG93IiwiUHJpbmNpcGFsIjp7IkFXUyI6ImFybjphd3M6aWFtOjoxNDM0NTg5Njc1MDQ6cm9sZS9wcm9kLTEzNzI4MTMwODkyMDY2NjEyOTgtZWtzLWFwLW5vcnRoZWFzdC0xLWQ3ODFiZTk4In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MTQzNDU4OTY3NTA0OnJvbGUvcHJvZC0xMzcyODEzMDg5Mzg3NjgxMjg3LWVrcy1hcC1ub3J0aGVhc3QtMS03NDkyZWQ1NCJ9LCJBY3Rpb24iOiJzdHM6QXNzdW1lUm9sZSIsIkNvbmRpdGlvbiI6e319LHsiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OjM4MDgzODQ0MzU2Nzpyb2xlL3Byb2QtMTM3MjgxMzA4OTIwNjMzMTQ2My1la3MtYXAtbm9ydGhlYXN0LTEtNTNmZDAxOGUifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9fSx7IkVmZmVjdCI6IkFsbG93IiwiUHJpbmNpcGFsIjp7IkFXUyI6ImFybjphd3M6aWFtOjoxNDM0NTg5Njc1MDQ6cm9sZS9wcm9kLTEzNzI4MTMwODkyMDY0NTE1NDMtZWtzLWFwLW5vcnRoZWFzdC0xLWExZjcwMWY5In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX1dfQ=="
	CRDAssumDocmentFalse := "eyJWZXJzaW9uIjoiMjAxMi0xMC0xNyIsIlN0YXRlbWVudCI6W3siRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjoiYXJuOmF3czppYW06OjU1NzUzNzM2NjAyMDpvaWRjLXByb3ZpZGVyL29pZGMuZWtzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20vaWQvMjU0NjlBRTlERkI0MDgzREVGRTVDNjYxQTIyQ0I1NTAifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGVXaXRoV2ViSWRlbnRpdHkiLCJDb25kaXRpb24iOnsiU3RyaW5nRXF1YWxzIjp7Im9pZGMuZWtzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20vaWQvMjU0NjlBRTlERkI0MDgzREVGRTVDNjYxQTIyQ0I1NTA6YXVkIjoic3RzLmFtYXpvbmF3cy5jb20iLCJvaWRjLmVrcy5hcC1ub3J0aGVhc3QtMS5hbWF6b25hd3MuY29tL2lkLzI1NDY5QUU5REZCNDA4M0RFRkU1QzY2MUEyMkNCNTUwOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0aWRiY2xvdWQtMTM3MjgxMzA4OTE5NDIxMTI4Mzp0aWRiY2xvdWQtMTM3MjgxMzA4OTE5NDIxMTI4MyJ9fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvc2hvb3QtLXByb2QtLWVlZmZmZmVmLW5vZGVzIn0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvc2hvb3QtLXByb2QtLTJhZDhjOGNlLW5vZGVzIn0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MzgwODM4NDQzNTY3OnJvbGUvcHJvZC0xMzcyODEzMDg5MTkxODExMjg0LWVrcy1hcC1ub3J0aGVhc3QtMS03NDlhODFmZSJ9LCJBY3Rpb24iOiJzdHM6QXNzdW1lUm9sZSIsIkNvbmRpdGlvbiI6e319LHsiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OjE0MzQ1ODk2NzUwNDpyb2xlL3Byb2QtMTM3MjgxMzA4OTI5MzIxMTI4OC1la3MtYXAtbm9ydGhlYXN0LTEtNjQ5MzBkMGEifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9fSx7IkVmZmVjdCI6IkFsbG93IiwiUHJpbmNpcGFsIjp7IkFXUyI6ImFybjphd3M6aWFtOjoxNDM0NTg5Njc1MDQ6cm9sZS9wcm9kLTEzNzI4MTMwODkyMDY2NjEyOTgtZWtzLWFwLW5vcnRoZWFzdC0xLWQ3ODFiZTk4In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX0seyJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJBV1MiOiJhcm46YXdzOmlhbTo6MTQzNDU4OTY3NTA0OnJvbGUvcHJvZC0xMzcyODEzMDg5Mzg3NjgxMjg3LWVrcy1hcC1ub3J0aGVhc3QtMS03NDkyZWQ1NCJ9LCJBY3Rpb24iOiJzdHM6QXNzdW1lUm9sZSIsIkNvbmRpdGlvbiI6e319LHsiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiQVdTIjoiYXJuOmF3czppYW06OjM4MDgzODQ0MzU2Nzpyb2xlL3Byb2QtMTM3MjgxMzA4OTIwNjMzMTQ2My1la3MtYXAtbm9ydGhlYXN0LTEtNTNmZDAxOGUifSwiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9fSx7IkVmZmVjdCI6IkFsbG93IiwiUHJpbmNpcGFsIjp7IkFXUyI6ImFybjphd3M6aWFtOjoxNDM0NTg5Njc2MDQ6cm9sZS9wcm9kLTEzNzI4MTMwODkyMDY0NTE1NDMtZWtzLWFwLW5vcnRoZWFzdC0xLWExZjcwMWY5In0sIkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7fX1dfQ=="
	result, err := r.equalPolicyAWS(context.TODO(), log.FromContext(context.Background()), "o11y-agent-prod-TWoQDFXFGNsMhtCwxmvduY-ap-northeast-1", AWSassumeRolePolicyDocument, CRDAssumDocment, []string{
		"AccessClusterData", "NewAccessDataPolicy",
	}, []observabilitypingcapcomv1.AWSRolePolicy{
		{PolicyName: "AccessClusterData"}, {PolicyName: "NewAccessDataPolicy"},
	})
	g.Expect(err).Should(BeNil())
	g.Expect(result).Should(BeTrue())
	result, err = r.equalPolicyAWS(context.TODO(), log.FromContext(context.Background()), "o11y-agent-prod-TWoQDFXFGNsMhtCwxmvduY-ap-northeast-1", AWSassumeRolePolicyDocument, CRDAssumDocmentFalse, []string{
		"AccessClusterData", "NewAccessDataPolicy",
	}, []observabilitypingcapcomv1.AWSRolePolicy{
		{PolicyName: "AccessClusterData"}, {PolicyName: "NewAccessDataPolicy"},
	})
	g.Expect(err).Should(BeNil())
	g.Expect(result).Should(BeFalse())
}

func Test_AssumeRoleAliCloudPolicyDocumentCompare(t *testing.T) {
	g := NewGomegaWithT(t)
	r := IAM{}
	AliCloudAssumeRolePolicyDocument := `%7B%22Statement%22%3A%5B%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22oidc%3Aaud%22%3A%22sts.aliyuncs.com%22%2C%22oidc%3Asub%22%3A%22system%3Aserviceaccount%3Adev-devtier-dev-devtier%3Adev-devtier-dev-devtier%22%7D%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%5B%22acs%3Aram%3A%3A5092239846523038%3Aoidc-provider%2Fack-rrsa-c21a0a5e390ff46a8b006997fbc85a054%22%5D%7D%7D%2C%7B%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22RAM%22%3A%5B%22acs%3Aram%3A%3A5654539846535456%3Arole%2Fdev-ap-southeast-1-f01-oss-ram%22%5D%7D%7D%5D%2C%22Version%22%3A%221%22%7D`
	CRDAssumDocment := "eyJTdGF0ZW1lbnQiOlt7IkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7IlN0cmluZ0VxdWFscyI6eyJvaWRjOmF1ZCI6InN0cy5hbGl5dW5jcy5jb20iLCJvaWRjOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZXYtZGV2dGllci1kZXYtZGV2dGllcjpkZXYtZGV2dGllci1kZXYtZGV2dGllciJ9fSwiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjpbImFjczpyYW06OjUwOTIyMzk4NDY1MjMwMzg6b2lkYy1wcm92aWRlci9hY2stcnJzYS1jMjFhMGE1ZTM5MGZmNDZhOGIwMDY5OTdmYmM4NWEwNTQiXX19LHsiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9LCJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJSQU0iOlsiYWNzOnJhbTo6NTY1NDUzOTg0NjUzNTQ1Njpyb2xlL2Rldi1hcC1zb3V0aGVhc3QtMS1mMDEtb3NzLXJhbSJdfX1dLCJWZXJzaW9uIjoiMSJ9"
	CRDAssumDocmentFalse := "eyJTdGF0ZW1lbnQiOlt7IkFjdGlvbiI6InN0czpBc3N1bWVSb2xlIiwiQ29uZGl0aW9uIjp7IlN0cmluZ0VxdWFscyI6eyJvaWRjOmF1ZCI6InN0cy5hbGl5dW5jcy5jb20iLCJvaWRjOnN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZXYtZGV2dGllci1kZXYtZGV2dGllcjpkZXYtZGV2dGllci1kZXYtZGV2dGllciJ9fSwiRWZmZWN0IjoiQWxsb3ciLCJQcmluY2lwYWwiOnsiRmVkZXJhdGVkIjpbImFjczpyYW06OjUwOTIyMzk4NDY1MjMwMzg6b2lkYy1wcm92aWRlci9hY2stcnJzYS1jMjFhMGE1ZTM5MGZmNDZhOGIwMDY5OTdmYmM4NWEwNTQiXX19LHsiQWN0aW9uIjoic3RzOkFzc3VtZVJvbGUiLCJDb25kaXRpb24iOnt9LCJFZmZlY3QiOiJBbGxvdyIsIlByaW5jaXBhbCI6eyJSQU0iOlsiYWNzOnJhbTo6NTY1NDUzOTg0NjUzNTQ1ODpyb2xlL2Rldi1hcC1zb3V0aGVhc3QtMS1mMDEtb3NzLXJhbSJdfX1dLCJWZXJzaW9uIjoiMSJ9"
	policyDocument := "eyJWZXJzaW9uIjoiMSIsIlN0YXRlbWVudCI6W119"
	result, err := r.equalPolicyAliCloud(context.TODO(), log.FromContext(context.Background()), "o11y-agent-prod-TWoQDFXFGNsMhtCwxmvduY-ap-northeast-1", AliCloudAssumeRolePolicyDocument, CRDAssumDocment, []string{
		"AccessClusterData", "NewAccessDataPolicy",
	}, map[string]string{
		"AccessClusterData":   `{"Version":"1","Statement":[]}`,
		"NewAccessDataPolicy": `{"Version":"1","Statement":[]}`,
	}, []observabilitypingcapcomv1.AliCloudPolicy{
		{PolicyName: "AccessClusterData", PolicyDocument: policyDocument}, {PolicyName: "NewAccessDataPolicy", PolicyDocument: policyDocument},
	})
	g.Expect(err).Should(BeNil())
	g.Expect(result).Should(BeTrue())
	result, err = r.equalPolicyAliCloud(context.TODO(), log.FromContext(context.Background()), "o11y-agent-prod-TWoQDFXFGNsMhtCwxmvduY-ap-northeast-1", AliCloudAssumeRolePolicyDocument, CRDAssumDocmentFalse, []string{
		"AccessClusterData", "NewAccessDataPolicy",
	}, map[string]string{
		"AccessClusterData":   `{"Version":"1","Statement":[]}`,
		"NewAccessDataPolicy": `{"Version":"1","Statement":[]}`,
	}, []observabilitypingcapcomv1.AliCloudPolicy{
		{PolicyName: "AccessClusterData"}, {PolicyName: "NewAccessDataPolicy"},
	})
	g.Expect(err).Should(BeNil())
	g.Expect(result).Should(BeFalse())
}
