github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/coordination/client_test.go (about)

     1  package coordination
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/require"
     9  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    10  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
    11  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations"
    12  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Scheme"
    13  	"go.uber.org/mock/gomock"
    14  	grpcCodes "google.golang.org/grpc/codes"
    15  	grpcStatus "google.golang.org/grpc/status"
    16  	"google.golang.org/protobuf/types/known/anypb"
    17  	"google.golang.org/protobuf/types/known/durationpb"
    18  
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/config"
    20  	"github.com/ydb-platform/ydb-go-sdk/v3/coordination"
    21  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    23  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    24  	"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
    25  )
    26  
    27  func TestCreateNode(t *testing.T) {
    28  	t.Run("HappyWay", func(t *testing.T) {
    29  		ctx := xtest.Context(t)
    30  		ctrl := gomock.NewController(t)
    31  		client := NewMockCoordinationServiceClient(ctrl)
    32  		client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.CreateNodeResponse{
    33  			Operation: &Ydb_Operations.Operation{
    34  				Ready:  true,
    35  				Status: Ydb.StatusIds_SUCCESS,
    36  			},
    37  		}, nil)
    38  		err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
    39  		require.NoError(t, err)
    40  	})
    41  	t.Run("TransportError", func(t *testing.T) {
    42  		ctx := xtest.Context(t)
    43  		ctrl := gomock.NewController(t)
    44  		client := NewMockCoordinationServiceClient(ctrl)
    45  		client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(nil,
    46  			xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
    47  		)
    48  		err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
    49  		require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
    50  		require.True(t, xerrors.IsRetryObjectValid(err))
    51  	})
    52  	t.Run("OperationError", func(t *testing.T) {
    53  		ctx := xtest.Context(t)
    54  		ctrl := gomock.NewController(t)
    55  		client := NewMockCoordinationServiceClient(ctrl)
    56  		client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(nil,
    57  			xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
    58  		)
    59  		err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
    60  		require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
    61  		require.True(t, xerrors.IsRetryObjectValid(err))
    62  	})
    63  }
    64  
    65  func TestCreateNodeRequest(t *testing.T) {
    66  	for _, tt := range []struct {
    67  		name            string
    68  		path            string
    69  		config          coordination.NodeConfig
    70  		operationParams *Ydb_Operations.OperationParams
    71  		request         *Ydb_Coordination.CreateNodeRequest
    72  	}{
    73  		{
    74  			name: xtest.CurrentFileLine(),
    75  			path: "/abc",
    76  			config: coordination.NodeConfig{
    77  				Path: "/cde",
    78  			},
    79  			operationParams: operation.Params(context.Background(), time.Second, time.Second, operation.ModeSync),
    80  			request: &Ydb_Coordination.CreateNodeRequest{
    81  				Path: "/abc",
    82  				Config: &Ydb_Coordination.Config{
    83  					Path: "/cde",
    84  				},
    85  				OperationParams: &Ydb_Operations.OperationParams{
    86  					OperationMode:    Ydb_Operations.OperationParams_SYNC,
    87  					OperationTimeout: durationpb.New(time.Second),
    88  					CancelAfter:      durationpb.New(time.Second),
    89  				},
    90  			},
    91  		},
    92  	} {
    93  		t.Run(tt.name, func(t *testing.T) {
    94  			request := createNodeRequest(tt.path, tt.config, tt.operationParams)
    95  			require.EqualValues(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
    96  		})
    97  	}
    98  }
    99  
   100  func TestDescribeNodeRequest(t *testing.T) {
   101  	for _, tt := range []struct {
   102  		name            string
   103  		path            string
   104  		operationParams *Ydb_Operations.OperationParams
   105  		request         *Ydb_Coordination.DescribeNodeRequest
   106  	}{
   107  		{
   108  			name: xtest.CurrentFileLine(),
   109  			path: "/a/b/c",
   110  			operationParams: &Ydb_Operations.OperationParams{
   111  				OperationMode: Ydb_Operations.OperationParams_SYNC,
   112  			},
   113  			request: &Ydb_Coordination.DescribeNodeRequest{
   114  				Path: "/a/b/c",
   115  				OperationParams: &Ydb_Operations.OperationParams{
   116  					OperationMode: Ydb_Operations.OperationParams_SYNC,
   117  				},
   118  			},
   119  		},
   120  	} {
   121  		t.Run(tt.name, func(t *testing.T) {
   122  			request := describeNodeRequest(tt.path, tt.operationParams)
   123  			require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
   124  		})
   125  	}
   126  }
   127  
   128  func TestOperationParams(t *testing.T) {
   129  	for _, tt := range []struct {
   130  		name   string
   131  		ctx    context.Context //nolint:containedctx
   132  		config interface {
   133  			OperationTimeout() time.Duration
   134  			OperationCancelAfter() time.Duration
   135  		}
   136  		mode            operation.Mode
   137  		operationParams *Ydb_Operations.OperationParams
   138  	}{
   139  		{
   140  			name:   xtest.CurrentFileLine(),
   141  			ctx:    context.Background(),
   142  			config: config.New(config.WithOperationCancelAfter(time.Second), config.WithOperationTimeout(time.Second)),
   143  			mode:   operation.ModeSync,
   144  			operationParams: &Ydb_Operations.OperationParams{
   145  				OperationMode:    Ydb_Operations.OperationParams_SYNC,
   146  				OperationTimeout: durationpb.New(time.Second),
   147  				CancelAfter:      durationpb.New(time.Second),
   148  			},
   149  		},
   150  		{
   151  			name:   xtest.CurrentFileLine(),
   152  			ctx:    operation.WithCancelAfter(operation.WithTimeout(context.Background(), time.Second), time.Second),
   153  			config: config.New(),
   154  			mode:   operation.ModeSync,
   155  			operationParams: &Ydb_Operations.OperationParams{
   156  				OperationMode:    Ydb_Operations.OperationParams_SYNC,
   157  				OperationTimeout: durationpb.New(time.Second),
   158  				CancelAfter:      durationpb.New(time.Second),
   159  			},
   160  		},
   161  	} {
   162  		t.Run(tt.name, func(t *testing.T) {
   163  			params := operationParams(tt.ctx, tt.config, tt.mode)
   164  			require.Equal(t, xtest.ToJSON(tt.operationParams), xtest.ToJSON(params))
   165  		})
   166  	}
   167  }
   168  
   169  func TestDescribeNode(t *testing.T) {
   170  	t.Run("HappyWay", func(t *testing.T) {
   171  		ctx := xtest.Context(t)
   172  		ctrl := gomock.NewController(t)
   173  		client := NewMockCoordinationServiceClient(ctrl)
   174  		client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.DescribeNodeResponse{
   175  			Operation: &Ydb_Operations.Operation{
   176  				Ready:  true,
   177  				Status: Ydb.StatusIds_SUCCESS,
   178  				Result: func() *anypb.Any {
   179  					result, err := anypb.New(&Ydb_Coordination.DescribeNodeResult{
   180  						Self: &Ydb_Scheme.Entry{
   181  							Name:  "/a/b/c",
   182  							Owner: "root",
   183  							Type:  Ydb_Scheme.Entry_COORDINATION_NODE,
   184  						},
   185  						Config: &Ydb_Coordination.Config{
   186  							Path:                     "/a/b/c",
   187  							SelfCheckPeriodMillis:    100,
   188  							SessionGracePeriodMillis: 1000,
   189  							ReadConsistencyMode:      Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_STRICT,
   190  							AttachConsistencyMode:    Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_STRICT,
   191  							RateLimiterCountersMode:  Ydb_Coordination.RateLimiterCountersMode_RATE_LIMITER_COUNTERS_MODE_AGGREGATED,
   192  						},
   193  					})
   194  					require.NoError(t, err)
   195  
   196  					return result
   197  				}(),
   198  			},
   199  		}, nil)
   200  		nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
   201  			Path:            "/a/b/c",
   202  			OperationParams: nil,
   203  		})
   204  		require.NoError(t, err)
   205  		require.Equal(t, xtest.ToJSON(&scheme.Entry{
   206  			Name:  "/a/b/c",
   207  			Owner: "root",
   208  			Type:  scheme.EntryCoordinationNode,
   209  		}), xtest.ToJSON(nodeScheme))
   210  		require.Equal(t, xtest.ToJSON(coordination.NodeConfig{
   211  			Path:                     "/a/b/c",
   212  			SelfCheckPeriodMillis:    100,
   213  			SessionGracePeriodMillis: 1000,
   214  			ReadConsistencyMode:      coordination.ConsistencyModeStrict,
   215  			AttachConsistencyMode:    coordination.ConsistencyModeStrict,
   216  			RatelimiterCountersMode:  coordination.RatelimiterCountersModeAggregated,
   217  		}), xtest.ToJSON(nodeConfig))
   218  	})
   219  	t.Run("TransportError", func(t *testing.T) {
   220  		ctx := xtest.Context(t)
   221  		ctrl := gomock.NewController(t)
   222  		client := NewMockCoordinationServiceClient(ctrl)
   223  		client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(nil,
   224  			xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, "")),
   225  		)
   226  		nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
   227  			Path:            "/a/b/c",
   228  			OperationParams: nil,
   229  		})
   230  		require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
   231  		require.Nil(t, nodeScheme)
   232  		require.Nil(t, nodeConfig)
   233  	})
   234  	t.Run("OperationError", func(t *testing.T) {
   235  		ctx := xtest.Context(t)
   236  		ctrl := gomock.NewController(t)
   237  		client := NewMockCoordinationServiceClient(ctrl)
   238  		client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(nil,
   239  			xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
   240  		)
   241  		nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
   242  			Path:            "/a/b/c",
   243  			OperationParams: nil,
   244  		})
   245  		require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
   246  		require.Nil(t, nodeScheme)
   247  		require.Nil(t, nodeConfig)
   248  	})
   249  }
   250  
   251  func TestAlterNodeRequest(t *testing.T) {
   252  	for _, tt := range []struct {
   253  		name            string
   254  		path            string
   255  		config          coordination.NodeConfig
   256  		operationParams *Ydb_Operations.OperationParams
   257  		request         *Ydb_Coordination.AlterNodeRequest
   258  	}{
   259  		{
   260  			name: xtest.CurrentFileLine(),
   261  			path: "/a/b/c",
   262  			config: coordination.NodeConfig{
   263  				Path: "/a/b/c",
   264  			},
   265  			operationParams: &Ydb_Operations.OperationParams{
   266  				OperationMode: Ydb_Operations.OperationParams_SYNC,
   267  			},
   268  			request: &Ydb_Coordination.AlterNodeRequest{
   269  				Path: "/a/b/c",
   270  				Config: &Ydb_Coordination.Config{
   271  					Path: "/a/b/c",
   272  				},
   273  				OperationParams: &Ydb_Operations.OperationParams{
   274  					OperationMode: Ydb_Operations.OperationParams_SYNC,
   275  				},
   276  			},
   277  		},
   278  	} {
   279  		t.Run(tt.name, func(t *testing.T) {
   280  			request := alterNodeRequest(tt.path, tt.config, tt.operationParams)
   281  			require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
   282  		})
   283  	}
   284  }
   285  
   286  func TestAlterNode(t *testing.T) {
   287  	t.Run("HappyWay", func(t *testing.T) {
   288  		ctx := xtest.Context(t)
   289  		ctrl := gomock.NewController(t)
   290  		client := NewMockCoordinationServiceClient(ctrl)
   291  		client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.AlterNodeResponse{
   292  			Operation: &Ydb_Operations.Operation{
   293  				Ready:  true,
   294  				Status: Ydb.StatusIds_SUCCESS,
   295  			},
   296  		}, nil)
   297  		err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
   298  		require.NoError(t, err)
   299  	})
   300  	t.Run("TransportError", func(t *testing.T) {
   301  		ctx := xtest.Context(t)
   302  		ctrl := gomock.NewController(t)
   303  		client := NewMockCoordinationServiceClient(ctrl)
   304  		client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(nil,
   305  			xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
   306  		)
   307  		err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
   308  		require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
   309  		require.True(t, xerrors.IsRetryObjectValid(err))
   310  	})
   311  	t.Run("OperationError", func(t *testing.T) {
   312  		ctx := xtest.Context(t)
   313  		ctrl := gomock.NewController(t)
   314  		client := NewMockCoordinationServiceClient(ctrl)
   315  		client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(nil,
   316  			xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
   317  		)
   318  		err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
   319  		require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
   320  		require.True(t, xerrors.IsRetryObjectValid(err))
   321  	})
   322  }
   323  
   324  func TestDropNodeRequest(t *testing.T) {
   325  	for _, tt := range []struct {
   326  		name            string
   327  		path            string
   328  		operationParams *Ydb_Operations.OperationParams
   329  		request         *Ydb_Coordination.DropNodeRequest
   330  	}{
   331  		{
   332  			name: xtest.CurrentFileLine(),
   333  			path: "/a/b/c",
   334  			operationParams: &Ydb_Operations.OperationParams{
   335  				OperationMode: Ydb_Operations.OperationParams_SYNC,
   336  			},
   337  			request: &Ydb_Coordination.DropNodeRequest{
   338  				Path: "/a/b/c",
   339  				OperationParams: &Ydb_Operations.OperationParams{
   340  					OperationMode: Ydb_Operations.OperationParams_SYNC,
   341  				},
   342  			},
   343  		},
   344  	} {
   345  		t.Run(tt.name, func(t *testing.T) {
   346  			request := dropNodeRequest(tt.path, tt.operationParams)
   347  			require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
   348  		})
   349  	}
   350  }
   351  
   352  func TestDropNode(t *testing.T) {
   353  	t.Run("HappyWay", func(t *testing.T) {
   354  		ctx := xtest.Context(t)
   355  		ctrl := gomock.NewController(t)
   356  		client := NewMockCoordinationServiceClient(ctrl)
   357  		client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.DropNodeResponse{
   358  			Operation: &Ydb_Operations.Operation{
   359  				Ready:  true,
   360  				Status: Ydb.StatusIds_SUCCESS,
   361  			},
   362  		}, nil)
   363  		err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
   364  		require.NoError(t, err)
   365  	})
   366  	t.Run("TransportError", func(t *testing.T) {
   367  		ctx := xtest.Context(t)
   368  		ctrl := gomock.NewController(t)
   369  		client := NewMockCoordinationServiceClient(ctrl)
   370  		client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(nil,
   371  			xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
   372  		)
   373  		err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
   374  		require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
   375  		require.True(t, xerrors.IsRetryObjectValid(err))
   376  	})
   377  	t.Run("OperationError", func(t *testing.T) {
   378  		ctx := xtest.Context(t)
   379  		ctrl := gomock.NewController(t)
   380  		client := NewMockCoordinationServiceClient(ctrl)
   381  		client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(nil,
   382  			xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
   383  		)
   384  		err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
   385  		require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
   386  		require.True(t, xerrors.IsRetryObjectValid(err))
   387  	})
   388  }