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 }