package cluster_svc

import (
	"context"
	"testing"
	"time"

	"github.com/shopspring/decimal"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/clusterproxy"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/rpc"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	"github.com/tidbcloud/serverless-service/internal/domain/facade/mock_facade"
	"github.com/tidbcloud/serverless-service/internal/domain/repo"
	"github.com/tidbcloud/serverless-service/internal/domain/repo/mock_repo"
	"github.com/tidbcloud/serverless-service/internal/domain/service/mock_service"
	"github.com/tidbcloud/serverless-service/internal/errorcode"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"go.uber.org/mock/gomock"
	"google.golang.org/protobuf/types/known/timestamppb"
	"k8s.io/utils/ptr"
)

func Test_ClusterService_ListClusters(t *testing.T) {
	ctx := context.Background()
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	billingSvc := mock_service.NewMockBillingService(ctrl)
	clusterRepo := mock_repo.NewMockClusterRepo(ctrl)
	eventRepo := mock_repo.NewMockClusterTidbPoolRepo(ctrl)
	clusterProxyProvider := mock_facade.NewMockClusterProxyProviderFacade(ctrl)
	accountFacade := mock_facade.NewMockAccountFacade(ctrl)
	mgmtFacade := mock_facade.NewMockMgmtSvcFacade(ctrl)
	svc := NewClusterRpcService(billingSvc, clusterRepo, eventRepo, clusterProxyProvider, accountFacade, mgmtFacade)

	Convey("Test_ListClusters", t, func() {
		Convey("should return err if page token is invalid", func() {
			req := &rpc.ListClustersReq{PageToken: ptr.To("invalid")}
			_, err := svc.ListClusters(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if failed to count clusters", func() {
			req := &rpc.ListClustersReq{}
			e := bizerr.New(bizerr.ErrCodeUnknown, "failed")
			clusterRepo.EXPECT().CountClusters(gomock.Any(), gomock.Any()).Return(int64(0), e)
			_, err := svc.ListClusters(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if failed to list clusters", func() {
			req := &rpc.ListClustersReq{}
			e := bizerr.New(bizerr.ErrCodeUnknown, "failed")
			clusterRepo.EXPECT().CountClusters(gomock.Any(), gomock.Any()).Return(int64(1), nil)
			clusterRepo.EXPECT().ListClustersInPage(gomock.Any(), gomock.Any(), uint32(1), uint32(10)).Return(nil, e)
			_, err := svc.ListClusters(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should succeed w/ paging", func() {
			req := &rpc.ListClustersReq{PageToken: ptr.To("2"), PageSize: ptr.To(uint32(1))}
			clusters := []*entity.Cluster{{ID: 1001, TenantID: 1, ProjectID: 101, KeyspaceID: 233}}
			clusterRepo.EXPECT().CountClusters(gomock.Any(), gomock.Any()).Return(int64(3), nil)
			clusterRepo.EXPECT().
				ListClustersInPage(gomock.Any(), gomock.Any(), uint32(2), uint32(1)).
				Return(clusters, nil)
			resp, err := svc.ListClusters(ctx, req)
			So(err, ShouldBeNil)
			So(resp.Total, ShouldEqual, 3)
			So(resp.NextPageToken, ShouldEqual, "3")
			So(resp.Clusters, ShouldHaveLength, 1)
			So(resp.Clusters[0].Id, ShouldEqual, 1001)
		})

		Convey("should succeed w/ filter", func() {
			req := &rpc.ListClustersReq{Filter: &rpc.Cluster_Filter{
				TenantIds: []uint64{101},
				RuModes:   []rpc.Cluster_RUMode{rpc.Cluster_RUMode_STANDARD},
				IsPaid:    ptr.To(true),
			}}
			filter := &repo.ClusterFilter{
				TenantIds: []uint64{101},
				RuModes:   []constant.ClusterRUMode{constant.ClusterRUModeStandard},
				IsPaid:    ptr.To(true),
			}
			clusters := []*entity.Cluster{{ID: 1001, TenantID: 1, ProjectID: 101, KeyspaceID: 233}}

			clusterRepo.EXPECT().CountClusters(gomock.Any(), filter).Return(int64(1), nil)
			clusterRepo.EXPECT().ListClustersInPage(gomock.Any(), filter, uint32(1), uint32(10)).Return(clusters, nil)

			resp, err := svc.ListClusters(ctx, req)
			So(err, ShouldBeNil)
			So(resp.Total, ShouldEqual, 1)
			So(resp.NextPageToken, ShouldEqual, "")
		})
	})
}

func Test_ClusterService_GetCluster(t *testing.T) {
	ctx := context.Background()
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	billingSvc := mock_service.NewMockBillingService(ctrl)
	clusterRepo := mock_repo.NewMockClusterRepo(ctrl)
	eventRepo := mock_repo.NewMockClusterTidbPoolRepo(ctrl)
	clusterProxyProvider := mock_facade.NewMockClusterProxyProviderFacade(ctrl)
	accountFacade := mock_facade.NewMockAccountFacade(ctrl)
	mgmtFacade := mock_facade.NewMockMgmtSvcFacade(ctrl)
	svc := NewClusterRpcService(billingSvc, clusterRepo, eventRepo, clusterProxyProvider, accountFacade, mgmtFacade)

	cluster := &entity.Cluster{
		ID:         1001,
		TenantID:   1,
		ProjectID:  101,
		RegionName: "us-east-1",
		SpendLimit: decimal.Zero,
		FreeCredit: decimal.NewFromInt(5),
		RuMode:     constant.RuLimitModeStandard,
	}

	Convey("Test_GetCluster", t, func() {
		Convey("should return err if get cluster failed", func() {
			req := &rpc.GetClusterReq{ClusterId: 1001}
			e := bizerr.New(bizerr.ErrCodeUnknown, "failed")
			clusterRepo.EXPECT().GetClusterByID(gomock.Any(), uint64(1001)).Return(nil, e)
			_, err := svc.GetCluster(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if get sku costs failed", func() {
			req := &rpc.GetClusterReq{ClusterId: 1001}
			e := bizerr.New(bizerr.ErrCodeUnknown, "failed")
			clusterRepo.EXPECT().GetClusterByID(gomock.Any(), uint64(1001)).Return(cluster, nil)
			billingSvc.EXPECT().
				CalculateClusterCostThisMonth(gomock.Any(), uint64(1), "1001", constant.ClusterTypeCluster, nil, "", "").
				Return(nil, e)
			_, err := svc.GetCluster(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should succeed", func() {
			req := &rpc.GetClusterReq{ClusterId: 1001}
			spent := &facade.CompleteCostExplorerResult{
				InternalCosts: facade.CostExplorerResult{
					CreditsCosts: facade.Costs{
						KindCategoryUnitsCost: map[string]facade.CategoryItemCosts{
							"TiDB Cloud Starter": {
								CategoryItemUnitsCosts: map[string]facade.CategoryCosts{
									"Request Units": {
										Order: 1,
										Price: facade.Price{
											RawPrice: decimal.NewFromFloat(3),
										},
									},
									"Row-based Storage": {
										Order: 1,
										Price: facade.Price{
											RawPrice: decimal.NewFromFloat(2),
										},
									},
								},
							},
						},
					},
				},
			}
			clusterRepo.EXPECT().GetClusterByID(gomock.Any(), uint64(1001)).Return(cluster, nil)
			billingSvc.EXPECT().
				CalculateClusterCostThisMonth(gomock.Any(), uint64(1), "1001", constant.ClusterTypeCluster, nil, "", "").
				Return(spent, nil)
			billingSvc.EXPECT().
				GetCostExplorerServerlessSkuCost(spent).
				Return(&facade.BillingSubCostExplorerResult{
					RuFreeCost:        decimal.NewFromFloat(3),
					RowStoreFreeCost:  decimal.NewFromFloat(2),
					ColumnarFreeCost:  decimal.NewFromFloat(0),
					RuSpentCost:       decimal.NewFromFloat(0),
					RowStoreSpentCost: decimal.NewFromFloat(0),
					ColumnarSpentCost: decimal.NewFromFloat(0),
				})
			resp, err := svc.GetCluster(ctx, req)
			So(err, ShouldBeNil)
			So(resp.Id, ShouldEqual, 1001)
			So(resp.SpendLimit, ShouldEqual, 0)
			So(resp.FreeCredit, ShouldEqual, 500)
			So(resp.RuMode, ShouldEqual, rpc.Cluster_RUMode_STANDARD)
			So(resp.Spent.RequestUnit, ShouldEqual, 300)
		})
	})
}

func Test_ClusterService_ListSpendLimitEvents(t *testing.T) {
	ctx := context.Background()
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	billingSvc := mock_service.NewMockBillingService(ctrl)
	clusterRepo := mock_repo.NewMockClusterRepo(ctrl)
	eventRepo := mock_repo.NewMockClusterTidbPoolRepo(ctrl)
	clusterProxyProvider := mock_facade.NewMockClusterProxyProviderFacade(ctrl)
	accountFacade := mock_facade.NewMockAccountFacade(ctrl)
	mgmtFacade := mock_facade.NewMockMgmtSvcFacade(ctrl)
	svc := NewClusterRpcService(billingSvc, clusterRepo, eventRepo, clusterProxyProvider, accountFacade, mgmtFacade)

	events := []*entity.ClusterTidbPool{
		{
			CreatedAt:        time.Now(),
			SpendLimitStatus: constant.SpendLimitEventStatusHandy,
		}, {
			CreatedAt:        time.Now(),
			SpendLimitStatus: constant.SpendLimitEventStatusExcessive,
		}, {
			CreatedAt:        time.Now(),
			SpendLimitStatus: constant.SpendLimitEventStatusFit,
		},
	}

	Convey("Test_ListSpendLimitEvents", t, func() {
		Convey("should return err if page token is invalid", func() {
			req := &rpc.ListSpendLimitEventsReq{ClusterId: 1001}
			e := bizerr.New(bizerr.ErrCodeUnknown, "failed")
			eventRepo.EXPECT().
				ListSpendLimitEvents(gomock.Any(), uint64(1001), gomock.Any(), gomock.Any()).
				Return(nil, e)
			_, err := svc.ListSpendLimitEvents(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should succeed", func() {
			req := &rpc.ListSpendLimitEventsReq{ClusterId: 1001}
			eventRepo.EXPECT().
				ListSpendLimitEvents(gomock.Any(), uint64(1001), gomock.Any(), gomock.Any()).
				Return(events, nil)
			resp, err := svc.ListSpendLimitEvents(ctx, req)
			So(err, ShouldBeNil)
			So(resp.Events, ShouldHaveLength, 3)
			So(resp.Events[0].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_SETTING)
			So(resp.Events[1].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_EXCESSIVE)
			So(resp.Events[2].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_FIT)
		})

		Convey("should succeed w/ specified time", func() {
			t := time.Now().UTC()
			req := &rpc.ListSpendLimitEventsReq{
				ClusterId: 1001,
				StartTime: timestamppb.New(t),
				EndTime:   timestamppb.New(t),
			}
			eventRepo.EXPECT().ListSpendLimitEvents(gomock.Any(), uint64(1001), t, t).Return(events, nil)
			resp, err := svc.ListSpendLimitEvents(ctx, req)
			So(err, ShouldBeNil)
			So(resp.Events, ShouldHaveLength, 3)
			So(resp.Events[0].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_SETTING)
			So(resp.Events[1].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_EXCESSIVE)
			So(resp.Events[2].Status, ShouldEqual, rpc.SpendLimitEvent_STATUS_FIT)
		})
	})
}

func Test_ClusterService_ListDatabases(t *testing.T) {
	ctx := context.Background()
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	billingSvc := mock_service.NewMockBillingService(ctrl)
	clusterRepo := mock_repo.NewMockClusterRepo(ctrl)
	eventRepo := mock_repo.NewMockClusterTidbPoolRepo(ctrl)
	clusterProxyProvider := mock_facade.NewMockClusterProxyProviderFacade(ctrl)
	accountFacade := mock_facade.NewMockAccountFacade(ctrl)
	mgmtFacade := mock_facade.NewMockMgmtSvcFacade(ctrl)
	svc := NewClusterRpcService(billingSvc, clusterRepo, eventRepo, clusterProxyProvider, accountFacade, mgmtFacade)
	clusterProxy := mock_facade.NewMockClusterProxyFacade(ctrl)

	Convey("Test_ListDatabases", t, func() {
		Convey("should return err if getting cluster failed", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			e := bizerr.New(errorcode.ErrCodeSvcMgmt, "failed to get cluster")
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(nil, e)
			_, err := svc.ListDatabases(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if meeting invalid auth method", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: 10,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			_, err := svc.ListDatabases(ctx, req)
			So(err, ShouldNotBeNil)
		})

		cluster := &tidbmgmtservicev1.Cluster{
			ClusterId: 4321,
			ProjectId: 1234,
			ClusterInfo: &tidbmgmtservicev1.Cluster_ServerlessCluster{
				ServerlessCluster: &globalserverlessv1.ServerlessServiceCluster{
					Cluster: &regionalserverlessv1.ServerlessRegionalServiceClusterInfo{
						ProxyEndpoint: "https://test.ru",
						Version:       "1",
					},
				},
			},
		}

		Convey("should return err if failed to get cluster proxy", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to get cluster proxy"))
			_, err := svc.ListDatabases(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if failed to query sql", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(clusterProxy, nil)
			clusterProxy.EXPECT().
				QuerySQL(gomock.Any(), "SHOW DATABASES").
				Return(nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to query sql"))
			_, err := svc.ListDatabases(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if result is not json", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(clusterProxy, nil)
			clusterProxy.EXPECT().QuerySQL(gomock.Any(), "SHOW DATABASES").Return([]byte("dsasd'dsa[sda"), nil)
			_, err := svc.ListDatabases(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should success", func() {
			req := &rpc.ListDatabasesReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(clusterProxy, nil)
			clusterProxy.EXPECT().
				QuerySQL(gomock.Any(), "SHOW DATABASES").
				Return([]byte("{\"Database\":[\"SU5GT1JNQVRJT05fU0NIRU1B\",\"UEVSRk9STUFOQ0VfU0NIRU1B\",\"bXlzcWw=\",\"dGVzdA==\"]}"), nil)
			databases, err := svc.ListDatabases(ctx, req)
			So(err, ShouldBeNil)
			So(
				databases.Databases,
				ShouldResemble,
				[]string{"INFORMATION_SCHEMA", "PERFORMANCE_SCHEMA", "mysql", "test"},
			)
		})
	})
}

func Test_ClusterService_CreateDatabase(t *testing.T) {
	ctx := context.Background()
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	billingSvc := mock_service.NewMockBillingService(ctrl)
	clusterRepo := mock_repo.NewMockClusterRepo(ctrl)
	eventRepo := mock_repo.NewMockClusterTidbPoolRepo(ctrl)
	clusterProxyProvider := mock_facade.NewMockClusterProxyProviderFacade(ctrl)
	accountFacade := mock_facade.NewMockAccountFacade(ctrl)
	mgmtFacade := mock_facade.NewMockMgmtSvcFacade(ctrl)
	svc := NewClusterRpcService(billingSvc, clusterRepo, eventRepo, clusterProxyProvider, accountFacade, mgmtFacade)
	clusterProxy := mock_facade.NewMockClusterProxyFacade(ctrl)

	Convey("Test_CreateDatabase", t, func() {
		Convey("should return err if getting cluster failed", func() {
			req := &rpc.CreateDatabaseReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
				Database:  "users",
			}
			e := bizerr.New(errorcode.ErrCodeSvcMgmt, "failed to get cluster")
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(nil, e)
			_, err := svc.CreateDatabase(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if meeting invalid auth method", func() {
			req := &rpc.CreateDatabaseReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: 10,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
				Database:  "users",
			}
			_, err := svc.CreateDatabase(ctx, req)
			So(err, ShouldNotBeNil)
		})

		cluster := &tidbmgmtservicev1.Cluster{
			ClusterId: 4321,
			ProjectId: 1234,
			ClusterInfo: &tidbmgmtservicev1.Cluster_ServerlessCluster{
				ServerlessCluster: &globalserverlessv1.ServerlessServiceCluster{
					Cluster: &regionalserverlessv1.ServerlessRegionalServiceClusterInfo{
						ProxyEndpoint: "https://test.ru",
						Version:       "1",
					},
				},
			},
		}

		Convey("should return err if failed to get cluster proxy", func() {
			req := &rpc.CreateDatabaseReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
				Database:  "users",
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to get cluster proxy"))
			_, err := svc.CreateDatabase(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should return err if failed to execute sql", func() {
			req := &rpc.CreateDatabaseReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
				Database:  "users",
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(clusterProxy, nil)
			clusterProxy.EXPECT().
				ExecuteSQL(gomock.Any(), "CREATE DATABASE `users`").
				Return(bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to query sql"))
			_, err := svc.CreateDatabase(ctx, req)
			So(err, ShouldNotBeNil)
		})

		Convey("should success", func() {
			req := &rpc.CreateDatabaseReq{
				Operator: &rpc.DBUserOperator{
					Username:   "leon",
					AuthMethod: rpc.DBUser_METHOD_TOKEN,
					Credential: "YSFSSA",
				},
				ProjectId: 1234,
				ClusterId: 4321,
				Database:  "users",
			}
			mgmtFacade.EXPECT().
				GetCluster(gomock.Any(), uint64(4321), tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC).
				Return(cluster, nil)
			clusterProxyProvider.EXPECT().GetClusterProxy(gomock.Any(), cluster, &clusterproxy.ClusterOperator{
				Username:   "leon",
				AuthMethod: clusterproxy.AuthMethodToken,
				Credential: "YSFSSA",
			}).Return(clusterProxy, nil)
			clusterProxy.EXPECT().ExecuteSQL(gomock.Any(), "CREATE DATABASE `users`").Return(nil)
			databases, err := svc.CreateDatabase(ctx, req)
			So(err, ShouldBeNil)
			So(databases.BaseResp.ErrCode, ShouldEqual, 0)
		})
	})
}
