package region

import (
	"testing"

	"github.com/samber/lo"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	grpcmetadata "google.golang.org/grpc/metadata"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/account/mock_account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/dedicatedglobal/mock_dedicatedglobal"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

const (
	testStorageTypeDetailDefaultIops = 8000
	testStorageTypeDetailMinIops     = 3000
	testStorageTypeDetailMaxIops     = 16000
)

func TestListNodeSpecs(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	dedicatedMock := mock_dedicatedglobal.NewMockDedicatedGlobal(ctrl)
	accountMock := mock_account.NewMockClient(ctrl)

	server := serverImpl{
		dedicatedCli: dedicatedMock,
		accountCli:   accountMock,
	}

	// Prepare test data
	orgID := uint64(1000)
	orgIDStr := "1000"
	projectId := uint64(2000)
	projectIDStr := "2000"
	region := "aws-us-east-1"
	clusterID := "3000"

	// Test versions
	oldVersion := "v6.5.0"   // Only supports Basic/Plus
	newVersion := "v7.5.5"   // Supports Standard/Performance
	newerVersion := "v8.5.0" // Supports Standard/Performance

	// Create mock SPUs for different component types
	tidbSPU := &globaldedicatedv1.Spu{
		Name:          "8c16g",
		DisplayName:   "8 vCPU, 16 GiB",
		CpuCore:       8,
		MemorySizeGi:  16,
		DefaultCount:  2,
		ComponentType: globaldedicatedv1.ComponentType_TIDB,
		Region:        globalcommonv1.Region_AWS_US_EAST_1,
		CloudProvider: globalcommonv1.CloudProvider_AWS,
	}

	tikvSPU := &globaldedicatedv1.Spu{
		Name:          "8c16g",
		DisplayName:   "8 vCPU, 16 GiB",
		CpuCore:       8,
		MemorySizeGi:  16,
		DefaultCount:  3,
		ComponentType: globaldedicatedv1.ComponentType_TIKV,
		Region:        globalcommonv1.Region_AWS_US_EAST_1,
		CloudProvider: globalcommonv1.CloudProvider_AWS,
		DataStorage: &globaldedicatedv1.Spu_Storage{
			DefaultSizeGi: 300,
			MinSizeGi:     200,
			MaxSizeGi:     500,
		},
		RaftlogStorage: &globaldedicatedv1.Spu_Storage{
			DefaultIops: 3000,
			MinIops:     1000,
			MaxIops:     5000,
		},
		StorageTypeDetails: map[string]*globaldedicatedv1.Spu_StorageTypeDetail{
			globaldedicatedv1.Spu_Plus.String(): {
				DataStorage: &globaldedicatedv1.Spu_Storage{
					DefaultIops: 8000,
					MinIops:     3000,
					MaxIops:     16000,
				},
			},
			globaldedicatedv1.Spu_Performance.String(): {
				RaftlogStorage: &globaldedicatedv1.Spu_Storage{
					DefaultIops: 4000,
					MinIops:     3000,
					MaxIops:     16000,
				},
			},
		},
		SupportedDiskPairs: []*globaldedicatedv1.Spu_DiskPair{
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_GP3,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
			},
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_GP3,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_GP3,
			},
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_GP3,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_IO2,
			},
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_IO2,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
			},
		},
	}

	tiflashSPU := &globaldedicatedv1.Spu{
		Name:          "16c32g",
		DisplayName:   "16 vCPU, 32 GiB",
		CpuCore:       16,
		MemorySizeGi:  32,
		DefaultCount:  1,
		ComponentType: globaldedicatedv1.ComponentType_TIFLASH,
		Region:        globalcommonv1.Region_AWS_US_EAST_1,
		CloudProvider: globalcommonv1.CloudProvider_AWS,
		DataStorage: &globaldedicatedv1.Spu_Storage{
			DefaultSizeGi: 500,
			MinSizeGi:     300,
			MaxSizeGi:     1000,
		},
		RaftlogStorage: &globaldedicatedv1.Spu_Storage{
			DefaultIops: 3000,
			MinIops:     1000,
			MaxIops:     5000,
		},
		StorageTypeDetails: tikvSPU.GetStorageTypeDetails(),
		SupportedDiskPairs: []*globaldedicatedv1.Spu_DiskPair{
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_GP3,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
			},
			{
				DataVolumeType:    globaldedicatedv1.StorageMu_IO2,
				RaftlogVolumeType: globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
			},
		},
	}

	// Prepare storage types for different components and versions
	createTiKVComponent := func(dataVt, raftVt globaldedicatedv1.StorageMu_VolumeType) *globaldedicatedv1.Cluster_ClusterComponent {
		return &globaldedicatedv1.Cluster_ClusterComponent{
			ComponentType: globaldedicatedv1.ComponentType_TIKV,
			Storages: map[string]*globaldedicatedv1.Cluster_ClusterComponent_Storage{
				globaldedicatedv1.Sku_DATA.String(): {
					VolumeType: lo.ToPtr(dataVt),
				},
				globaldedicatedv1.Sku_RAFT_LOG.String(): {
					VolumeType: lo.ToPtr(raftVt),
				},
			},
		}
	}

	createTiFlashComponent := func(dataVt, raftVt globaldedicatedv1.StorageMu_VolumeType) *globaldedicatedv1.Cluster_ClusterComponent {
		return &globaldedicatedv1.Cluster_ClusterComponent{
			ComponentType: globaldedicatedv1.ComponentType_TIFLASH,
			Storages: map[string]*globaldedicatedv1.Cluster_ClusterComponent_Storage{
				globaldedicatedv1.Sku_DATA.String(): {
					VolumeType: lo.ToPtr(dataVt),
				},
				globaldedicatedv1.Sku_RAFT_LOG.String(): {
					VolumeType: lo.ToPtr(raftVt),
				},
			},
		}
	}

	// Basic storage components
	tikvBasicComponent := createTiKVComponent(
		globaldedicatedv1.StorageMu_GP3,
		globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
	)
	tiflashBasicComponent := createTiFlashComponent(
		globaldedicatedv1.StorageMu_GP3,
		globaldedicatedv1.StorageMu_VOLUME_TYPE_UNSPECIFIED,
	)

	// Standard storage components
	tikvStandardComponent := createTiKVComponent(
		globaldedicatedv1.StorageMu_GP3,
		globaldedicatedv1.StorageMu_GP3,
	)

	// Performance storage components
	tikvPerformanceComponent := createTiKVComponent(
		globaldedicatedv1.StorageMu_GP3,
		globaldedicatedv1.StorageMu_IO2,
	)

	// Subtest 1: Success - List all node specs for older TiDB version
	t.Run("Success - List all node specs for older TiDB version", func(t *testing.T) {
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.OrgIDHeaderKey:            orgIDStr,
			metadata.DefaultProjectIDHeaderKey: projectIDStr,
			metadata.ProjectListHeaderKey:      projectIDStr,
			metadata.IsCheckPermissionKey:      "true",
			metadata.UserIDHeaderKey:           "1",
			metadata.UserEmailHeaderKey:        "foo@bar.com",
		}))

		accountMock.EXPECT().GetTenantThirdPartyAccountProviders(gomock.Any(), orgID).Return(nil, nil)
		dedicatedMock.EXPECT().
			ListSpus(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.ListSpusResponse{Spus: []*globaldedicatedv1.Spu{tidbSPU, tikvSPU, tiflashSPU}}, nil)
		dedicatedMock.EXPECT().
			GetCluster(gomock.Any(), &globaldedicatedv1.GetClusterRequest{ClusterId: 3000}).
			Return(&globaldedicatedv1.Cluster{ProjectId: projectId, Version: oldVersion, ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{tikvBasicComponent, tiflashBasicComponent}}, nil)

		resp, err := server.ListNodeSpecs(
			ctx,
			&openapidedicatedv1beta1.ListNodeSpecsRequest{RegionId: region, ProjectId: projectIDStr,
				Condition: &openapidedicatedv1beta1.ListNodeSpecsRequest_ClusterId{
					ClusterId: clusterID,
				},
			},
		)
		require.NoError(t, err)
		require.NotNil(t, resp)
		assert.Equal(t, int32(3), resp.TotalSize)
		assert.Len(t, resp.NodeSpecs, 3)

		// Verify TiKV storage types for old version (should be Basic/Plus only)
		tikvSpec := lo.Filter(resp.NodeSpecs, func(ns *openapidedicatedv1beta1.NodeSpec, _ int) bool {
			return ns.ComponentType == openapidedicatedv1beta1.ComponentType_TIKV
		})[0]
		require.Contains(t, tikvSPU.GetStorageTypeDetails(), globaldedicatedv1.Spu_Performance.String())

		// For older version, and current storage type is basic or plus, all storage types should be available
		assert.Len(t, tikvSpec.StorageTypes, 2)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Basic)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Plus)
		assert.Equal(t, int32(300), tikvSpec.GetDefaultStorageSizeGi())
		assert.Nil(t, tikvSpec.DefaultRaftStoreIops)
		assert.Len(t, tikvSpec.GetStorageTypeDetails(), 1)
		assertNodeSpecStorageTypeDetail(t, tikvSpec, globaldedicatedv1.Spu_Plus.String())
		assert.NotContains(t, tikvSpec.GetStorageTypeDetails(), globaldedicatedv1.Spu_Performance.String())

		tiflashSpec, ok := lo.Find(resp.NodeSpecs, func(ns *openapidedicatedv1beta1.NodeSpec) bool {
			return ns.ComponentType == openapidedicatedv1beta1.ComponentType_TIFLASH
		})
		require.True(t, ok)
		assert.Empty(t, tiflashSpec.GetStorageTypeDetails())
	})

	// Subtest 2: Success - List all node specs for newer TiDB version
	t.Run("Success - List all node specs for newer TiDB version", func(t *testing.T) {
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.OrgIDHeaderKey:            orgIDStr,
			metadata.DefaultProjectIDHeaderKey: projectIDStr,
			metadata.ProjectListHeaderKey:      projectIDStr,
			metadata.IsCheckPermissionKey:      "true",
			metadata.UserIDHeaderKey:           "1",
			metadata.UserEmailHeaderKey:        "foo@bar.com",
		}))

		accountMock.EXPECT().GetTenantThirdPartyAccountProviders(gomock.Any(), orgID).Return(nil, nil)
		dedicatedMock.EXPECT().
			ListSpus(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.ListSpusResponse{Spus: []*globaldedicatedv1.Spu{tidbSPU, tikvSPU, tiflashSPU}}, nil)
		dedicatedMock.EXPECT().
			GetCluster(gomock.Any(), &globaldedicatedv1.GetClusterRequest{ClusterId: 3000}).
			Return(&globaldedicatedv1.Cluster{ProjectId: projectId, Version: newVersion, ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{tikvStandardComponent, tiflashBasicComponent}}, nil)

		resp, err := server.ListNodeSpecs(
			ctx,
			&openapidedicatedv1beta1.ListNodeSpecsRequest{
				RegionId:  region,
				ProjectId: projectIDStr,
				Condition: &openapidedicatedv1beta1.ListNodeSpecsRequest_ClusterId{
					ClusterId: clusterID,
				},
			},
		)
		require.NoError(t, err)
		require.NotNil(t, resp)
		assert.Equal(t, int32(3), resp.TotalSize)
		assert.Len(t, resp.NodeSpecs, 3)

		// Verify TiKV storage types for new version (should be Standard/Performance only)
		tikvSpec := lo.Filter(resp.NodeSpecs, func(ns *openapidedicatedv1beta1.NodeSpec, _ int) bool {
			return ns.ComponentType == openapidedicatedv1beta1.ComponentType_TIKV
		})[0]

		// For newer version with Standard storage, only Standard and Performance should be available
		assert.Len(t, tikvSpec.StorageTypes, 2)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Standard)
		assert.Contains(
			t,
			tikvSpec.StorageTypes,
			openapidedicatedv1beta1.Cluster_StorageNodeSetting_Performance,
		)
		assert.NotContains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Basic)
		assert.NotContains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Plus)
		assert.Len(t, tikvSpec.GetStorageTypeDetails(), 1)
		assertNodeSpecStorageTypeDetail(t, tikvSpec, globaldedicatedv1.Spu_Plus.String())
	})

	// Subtest 3: Success - List all node specs for v8.5.0+ version
	t.Run("Success - List all node specs for v8.5.0+ version", func(t *testing.T) {
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.OrgIDHeaderKey:            orgIDStr,
			metadata.DefaultProjectIDHeaderKey: projectIDStr,
			metadata.ProjectListHeaderKey:      projectIDStr,
			metadata.IsCheckPermissionKey:      "true",
			metadata.UserIDHeaderKey:           "1",
			metadata.UserEmailHeaderKey:        "foo@bar.com",
		}))

		accountMock.EXPECT().GetTenantThirdPartyAccountProviders(gomock.Any(), orgID).Return(nil, nil)
		dedicatedMock.EXPECT().
			ListSpus(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.ListSpusResponse{Spus: []*globaldedicatedv1.Spu{tidbSPU, tikvSPU, tiflashSPU}}, nil)
		dedicatedMock.EXPECT().
			GetCluster(gomock.Any(), &globaldedicatedv1.GetClusterRequest{ClusterId: 3000}).
			Return(&globaldedicatedv1.Cluster{ProjectId: projectId, Version: newerVersion, ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{tikvPerformanceComponent, tiflashBasicComponent}}, nil)

		resp, err := server.ListNodeSpecs(
			ctx,
			&openapidedicatedv1beta1.ListNodeSpecsRequest{
				RegionId:  region,
				ProjectId: projectIDStr,
				Condition: &openapidedicatedv1beta1.ListNodeSpecsRequest_ClusterId{
					ClusterId: clusterID,
				},
			},
		)
		require.NoError(t, err)
		require.NotNil(t, resp)
		assert.Equal(t, int32(3), resp.TotalSize)
		assert.Len(t, resp.NodeSpecs, 3)

		// Verify TiKV storage types for newest version (should be Standard/Performance only)
		tikvSpec := lo.Filter(resp.NodeSpecs, func(ns *openapidedicatedv1beta1.NodeSpec, _ int) bool {
			return ns.ComponentType == openapidedicatedv1beta1.ComponentType_TIKV
		})[0]

		// For newer version with Performance storage, only Standard and Performance should be available
		assert.Len(t, tikvSpec.StorageTypes, 2)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Standard)
		assert.Contains(
			t,
			tikvSpec.StorageTypes,
			openapidedicatedv1beta1.Cluster_StorageNodeSetting_Performance,
		)
		assert.NotContains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Basic)
		assert.NotContains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Plus)
		assert.Len(t, tikvSpec.GetStorageTypeDetails(), 1)
		assertNodeSpecStorageTypeDetail(t, tikvSpec, globaldedicatedv1.Spu_Plus.String())
	})

	// Subtest 4: Success - Default version when no cluster ID provided
	t.Run("Success - Default version when no cluster ID provided", func(t *testing.T) {
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.OrgIDHeaderKey:            orgIDStr,
			metadata.DefaultProjectIDHeaderKey: projectIDStr,
			metadata.ProjectListHeaderKey:      projectIDStr,
			metadata.IsCheckPermissionKey:      "true",
			metadata.UserIDHeaderKey:           "1",
			metadata.UserEmailHeaderKey:        "foo@bar.com",
		}))

		accountMock.EXPECT().GetTenantThirdPartyAccountProviders(gomock.Any(), orgID).Return(nil, nil)
		dedicatedMock.EXPECT().
			ListSpus(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.ListSpusResponse{Spus: []*globaldedicatedv1.Spu{tidbSPU, tikvSPU, tiflashSPU}}, nil)
		dedicatedMock.EXPECT().
			GetDefaultTidbVersion(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.GetDefaultTidbVersionResponse{Version: newerVersion}, nil)

		resp, err := server.ListNodeSpecs(ctx, &openapidedicatedv1beta1.ListNodeSpecsRequest{RegionId: region, ProjectId: projectIDStr})
		require.NoError(t, err)
		require.NotNil(t, resp)
		assert.Equal(t, int32(3), resp.TotalSize)

		// Should use default storage type values
		tikvSpec := lo.Filter(resp.NodeSpecs, func(ns *openapidedicatedv1beta1.NodeSpec, _ int) bool {
			return ns.ComponentType == openapidedicatedv1beta1.ComponentType_TIKV
		})[0]

		// For newer version with default configuration, should have Standard/Performance.
		// If Plus is supported and there is Basic in original list, the Basic should also be included ignored the version constraint.
		assert.Len(t, tikvSpec.StorageTypes, 4)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Standard)
		assert.Contains(
			t,
			tikvSpec.StorageTypes,
			openapidedicatedv1beta1.Cluster_StorageNodeSetting_Performance,
		)
		assert.Contains(t, tikvSpec.StorageTypes, openapidedicatedv1beta1.Cluster_StorageNodeSetting_Plus)
	})

	// Subtest 5: Success - Filter by component type
	t.Run("Success - Filter by component type", func(t *testing.T) {
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.OrgIDHeaderKey:            orgIDStr,
			metadata.DefaultProjectIDHeaderKey: projectIDStr,
			metadata.ProjectListHeaderKey:      projectIDStr,
			metadata.IsCheckPermissionKey:      "true",
			metadata.UserIDHeaderKey:           "1",
			metadata.UserEmailHeaderKey:        "foo@bar.com",
		}))

		accountMock.EXPECT().GetTenantThirdPartyAccountProviders(gomock.Any(), orgID).Return(nil, nil)
		dedicatedMock.EXPECT().
			ListSpus(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.ListSpusResponse{Spus: []*globaldedicatedv1.Spu{tidbSPU, tikvSPU, tiflashSPU}}, nil)
		dedicatedMock.EXPECT().
			GetDefaultTidbVersion(gomock.Any(), gomock.Any()).
			Return(&globaldedicatedv1.GetDefaultTidbVersionResponse{Version: oldVersion}, nil)

		resp, err := server.ListNodeSpecs(
			ctx,
			&openapidedicatedv1beta1.ListNodeSpecsRequest{
				RegionId:      region,
				ProjectId:     projectIDStr,
				ComponentType: openapidedicatedv1beta1.ComponentType_TIKV,
			},
		)
		require.NoError(t, err)
		require.NotNil(t, resp)
		assert.Equal(t, int32(1), resp.TotalSize) // Total is still all SPUs
		assert.Len(t, resp.NodeSpecs, 1)          // But filtered to just TIKV
		assert.Equal(t, openapidedicatedv1beta1.ComponentType_TIKV, resp.NodeSpecs[0].ComponentType)

		// For old version, should have Basic/Plus storage types
		assert.Len(t, resp.NodeSpecs[0].StorageTypes, 2)
		assert.Contains(
			t,
			resp.NodeSpecs[0].StorageTypes,
			openapidedicatedv1beta1.Cluster_StorageNodeSetting_Basic,
		)
		assert.Contains(
			t,
			resp.NodeSpecs[0].StorageTypes,
			openapidedicatedv1beta1.Cluster_StorageNodeSetting_Plus,
		)
	})
}

func TestSpuStorageTypeDetails2NodeSpec(t *testing.T) {
	details := map[string]*globaldedicatedv1.Spu_StorageTypeDetail{
		globaldedicatedv1.Spu_Plus.String(): {
			DataStorage: &globaldedicatedv1.Spu_Storage{
				DefaultIops: 8000,
				MinIops:     3000,
				MaxIops:     16000,
			},
			RaftlogStorage: &globaldedicatedv1.Spu_Storage{
				DefaultIops: 4000,
				MinIops:     2000,
				MaxIops:     6000,
			},
		},
		globaldedicatedv1.Spu_Performance.String(): {
			DataStorage: &globaldedicatedv1.Spu_Storage{
				DefaultIops: 9000,
				MinIops:     5000,
				MaxIops:     18000,
			},
		},
	}

	t.Run("aws plus should expose data storage detail only", func(t *testing.T) {
		specDetails := spuStorageTypeDetails2NodeSpec(
			globaldedicatedv1.ComponentType_TIKV,
			globalcommonv1.CloudProvider_AWS,
			details,
		)
		require.Len(t, specDetails, 1)
		assert.NotContains(t, specDetails, globaldedicatedv1.Spu_Performance.String())
		assertNodeSpecStorageTypeDetail(
			t,
			&openapidedicatedv1beta1.NodeSpec{StorageTypeDetails: specDetails},
			globaldedicatedv1.Spu_Plus.String(),
		)
	})

	t.Run("non aws should not expose storage type details", func(t *testing.T) {
		assert.Nil(t, spuStorageTypeDetails2NodeSpec(
			globaldedicatedv1.ComponentType_TIKV,
			globalcommonv1.CloudProvider_AZURE,
			details,
		))
	})

	t.Run("non tikv should not expose storage type details", func(t *testing.T) {
		assert.Nil(t, spuStorageTypeDetails2NodeSpec(
			globaldedicatedv1.ComponentType_TIFLASH,
			globalcommonv1.CloudProvider_AWS,
			details,
		))
	})
}

func assertNodeSpecStorageTypeDetail(
	t *testing.T,
	spec *openapidedicatedv1beta1.NodeSpec,
	storageType string,
) {
	t.Helper()

	detail, ok := spec.GetStorageTypeDetails()[storageType]
	require.True(t, ok)
	require.NotNil(t, detail)

	require.NotNil(t, detail.GetDataStorage())
	assert.Equal(t, int32(testStorageTypeDetailDefaultIops), detail.GetDataStorage().GetDefaultIops())
	assert.Equal(t, int32(testStorageTypeDetailMinIops), detail.GetDataStorage().GetMinIops())
	assert.Equal(t, int32(testStorageTypeDetailMaxIops), detail.GetDataStorage().GetMaxIops())
	assert.Nil(t, detail.GetRaftlogStorage())
}
