github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/table/session_test.go (about) 1 package table 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 "github.com/ydb-platform/ydb-go-genproto/Ydb_Table_V1" 14 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 15 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations" 16 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Scheme" 17 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table" 18 "google.golang.org/grpc" 19 "google.golang.org/protobuf/proto" 20 "google.golang.org/protobuf/types/known/durationpb" 21 22 "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator" 23 "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" 24 "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config" 25 "github.com/ydb-platform/ydb-go-sdk/v3/internal/tx" 26 "github.com/ydb-platform/ydb-go-sdk/v3/internal/types" 27 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 28 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" 29 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" 30 "github.com/ydb-platform/ydb-go-sdk/v3/table" 31 "github.com/ydb-platform/ydb-go-sdk/v3/table/options" 32 "github.com/ydb-platform/ydb-go-sdk/v3/testutil" 33 ) 34 35 func TestSessionKeepAlive(t *testing.T) { 36 ctx, cancel := xcontext.WithCancel(context.Background()) 37 defer cancel() 38 39 var ( 40 status Ydb_Table.KeepAliveResult_SessionStatus 41 e error 42 ) 43 b := StubBuilder{ 44 T: t, 45 cc: testutil.NewBalancer( 46 testutil.WithInvokeHandlers( 47 testutil.InvokeHandlers{ 48 testutil.TableKeepAlive: func(interface{}) (proto.Message, error) { 49 return &Ydb_Table.KeepAliveResult{SessionStatus: status}, e 50 }, 51 testutil.TableCreateSession: func(interface{}) (proto.Message, error) { 52 return &Ydb_Table.CreateSessionResult{ 53 SessionId: testutil.SessionID(), 54 }, nil 55 }, 56 }, 57 ), 58 ), 59 } 60 s, err := b.createSession(ctx) 61 if err != nil { 62 t.Fatal(err) 63 } 64 e = fmt.Errorf("any error") 65 err = s.KeepAlive(ctx) 66 if err == nil { 67 t.Fatal(err) 68 } 69 70 status, e = Ydb_Table.KeepAliveResult_SESSION_STATUS_READY, nil 71 err = s.KeepAlive(ctx) 72 if err != nil { 73 t.Fatal(err) 74 } 75 if s.Status() != table.SessionReady { 76 t.Fatalf("Result %v differ from, expectd %v", s.Status(), table.SessionReady) 77 } 78 79 status, e = Ydb_Table.KeepAliveResult_SESSION_STATUS_BUSY, nil 80 err = s.KeepAlive(ctx) 81 if err != nil { 82 t.Fatal(err) 83 } 84 if s.Status() != table.SessionBusy { 85 t.Fatalf("Result %v differ from, expectd %v", s.Status(), table.SessionBusy) 86 } 87 } 88 89 func TestSessionDescribeTable(t *testing.T) { 90 ctx, cancel := xcontext.WithCancel(context.Background()) 91 defer cancel() 92 93 var ( 94 result *Ydb_Table.DescribeTableResult 95 e error 96 ) 97 b := StubBuilder{ 98 T: t, 99 cc: testutil.NewBalancer( 100 testutil.WithInvokeHandlers( 101 testutil.InvokeHandlers{ 102 testutil.TableCreateSession: func(interface{}) (proto.Message, error) { 103 return &Ydb_Table.CreateSessionResult{ 104 SessionId: testutil.SessionID(), 105 }, nil 106 }, 107 testutil.TableDescribeTable: func(interface{}) (proto.Message, error) { 108 r := &Ydb_Table.DescribeTableResult{} 109 proto.Merge(r, result) 110 111 return r, e 112 }, 113 }, 114 ), 115 ), 116 } 117 s, err := b.createSession(ctx) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 { 123 e = fmt.Errorf("any error") 124 _, err = s.DescribeTable(ctx, "") 125 if err == nil { 126 t.Fatal(err) 127 } 128 } 129 { 130 e = nil 131 expect := options.Description{ 132 Name: "testName", 133 PrimaryKey: []string{"testKey"}, 134 Columns: []options.Column{ 135 { 136 Name: "testColumn", 137 Type: types.NewVoid(), 138 Family: "testFamily", 139 }, 140 }, 141 KeyRanges: []options.KeyRange{ 142 { 143 From: nil, 144 To: value.Int64Value(100500), 145 }, 146 { 147 From: value.Int64Value(100500), 148 To: nil, 149 }, 150 }, 151 ColumnFamilies: []options.ColumnFamily{ 152 { 153 Name: "testFamily", 154 Data: options.StoragePool{}, 155 Compression: options.ColumnFamilyCompressionLZ4, 156 KeepInMemory: options.FeatureEnabled, 157 }, 158 }, 159 Attributes: map[string]string{}, 160 ReadReplicaSettings: options.ReadReplicasSettings{ 161 Type: options.ReadReplicasAnyAzReadReplicas, 162 Count: 42, 163 }, 164 StorageSettings: options.StorageSettings{ 165 TableCommitLog0: options.StoragePool{Media: "m1"}, 166 TableCommitLog1: options.StoragePool{Media: "m2"}, 167 External: options.StoragePool{Media: "m3"}, 168 StoreExternalBlobs: options.FeatureEnabled, 169 }, 170 Indexes: []options.IndexDescription{}, 171 Changefeeds: make([]options.ChangefeedDescription, 0), 172 } 173 a := allocator.New() 174 defer a.Free() 175 result = &Ydb_Table.DescribeTableResult{ 176 Self: &Ydb_Scheme.Entry{ 177 Name: expect.Name, 178 Owner: "", 179 Type: 0, 180 EffectivePermissions: nil, 181 Permissions: nil, 182 }, 183 Columns: []*Ydb_Table.ColumnMeta{ 184 { 185 Name: expect.Columns[0].Name, 186 Type: types.TypeToYDB(expect.Columns[0].Type, a), 187 Family: "testFamily", 188 }, 189 }, 190 PrimaryKey: expect.PrimaryKey, 191 ShardKeyBounds: []*Ydb.TypedValue{ 192 value.ToYDB(expect.KeyRanges[0].To, a), 193 }, 194 Indexes: nil, 195 TableStats: nil, 196 ColumnFamilies: []*Ydb_Table.ColumnFamily{ 197 { 198 Name: "testFamily", 199 Data: nil, 200 Compression: Ydb_Table.ColumnFamily_COMPRESSION_LZ4, 201 KeepInMemory: Ydb.FeatureFlag_ENABLED, 202 }, 203 }, 204 ReadReplicasSettings: expect.ReadReplicaSettings.ToYDB(), 205 StorageSettings: expect.StorageSettings.ToYDB(), 206 } 207 208 d, err := s.DescribeTable(ctx, "") 209 if err != nil { 210 t.Fatal(err) 211 } 212 if !reflect.DeepEqual(d, expect) { 213 t.Fatalf("Result %+v differ from, expectd %+v", d, expect) 214 } 215 } 216 } 217 218 func TestSessionOperationModeOnExecuteDataQuery(t *testing.T) { 219 fromTo := [...]struct { 220 srcMode operation.Mode 221 dstMode operation.Mode 222 }{ 223 { 224 srcMode: operation.ModeUnknown, 225 dstMode: operation.ModeSync, 226 }, 227 { 228 srcMode: operation.ModeSync, 229 dstMode: operation.ModeSync, 230 }, 231 { 232 srcMode: operation.ModeAsync, 233 dstMode: operation.ModeAsync, 234 }, 235 } 236 for _, test := range []struct { 237 method testutil.MethodCode 238 do func(t *testing.T, ctx context.Context, c *Client) 239 }{ 240 { 241 method: testutil.TableExecuteDataQuery, 242 do: func(t *testing.T, ctx context.Context, c *Client) { 243 s := &session{ 244 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 245 config: config.New(), 246 } 247 _, _, err := s.Execute(ctx, table.TxControl(), "", table.NewQueryParameters()) 248 require.NoError(t, err) 249 }, 250 }, 251 { 252 method: testutil.TableExplainDataQuery, 253 do: func(t *testing.T, ctx context.Context, c *Client) { 254 s := &session{ 255 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 256 config: config.New(), 257 } 258 _, err := s.Explain(ctx, "") 259 require.NoError(t, err) 260 }, 261 }, 262 { 263 method: testutil.TablePrepareDataQuery, 264 do: func(t *testing.T, ctx context.Context, c *Client) { 265 s := &session{ 266 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 267 config: config.New(), 268 } 269 _, err := s.Prepare(ctx, "") 270 require.NoError(t, err) 271 }, 272 }, 273 { 274 method: testutil.TableCreateSession, 275 do: func(t *testing.T, ctx context.Context, c *Client) { 276 _ = simpleSession(t) 277 }, 278 }, 279 { 280 method: testutil.TableDeleteSession, 281 do: func(t *testing.T, ctx context.Context, c *Client) { 282 s := &session{ 283 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 284 config: config.New(), 285 } 286 require.NoError(t, s.Close(ctx)) 287 }, 288 }, 289 { 290 method: testutil.TableBeginTransaction, 291 do: func(t *testing.T, ctx context.Context, c *Client) { 292 s := &session{ 293 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 294 config: config.New(), 295 } 296 _, err := s.BeginTransaction(ctx, table.TxSettings()) 297 require.NoError(t, err) 298 }, 299 }, 300 { 301 method: testutil.TableCommitTransaction, 302 do: func(t *testing.T, ctx context.Context, c *Client) { 303 tx := &transaction{ 304 Identifier: tx.ID(""), 305 s: &session{ 306 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 307 config: config.New(), 308 }, 309 } 310 _, err := tx.CommitTx(ctx) 311 require.NoError(t, err) 312 }, 313 }, 314 { 315 method: testutil.TableRollbackTransaction, 316 do: func(t *testing.T, ctx context.Context, c *Client) { 317 tx := &transaction{ 318 Identifier: tx.ID(""), 319 s: &session{ 320 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 321 config: config.New(), 322 }, 323 } 324 err := tx.Rollback(ctx) 325 require.NoError(t, err) 326 }, 327 }, 328 { 329 method: testutil.TableKeepAlive, 330 do: func(t *testing.T, ctx context.Context, c *Client) { 331 s := &session{ 332 tableService: Ydb_Table_V1.NewTableServiceClient(c.cc), 333 config: config.New(), 334 } 335 require.NoError(t, s.KeepAlive(ctx)) 336 }, 337 }, 338 } { 339 t.Run( 340 test.method.String(), 341 func(t *testing.T) { 342 for _, srcDst := range fromTo { 343 t.Run(srcDst.srcMode.String()+"->"+srcDst.dstMode.String(), func(t *testing.T) { 344 client := New(context.Background(), testutil.NewBalancer( 345 testutil.WithInvokeHandlers( 346 testutil.InvokeHandlers{ 347 testutil.TableExecuteDataQuery: func(interface{}) (proto.Message, error) { 348 return &Ydb_Table.ExecuteQueryResult{ 349 TxMeta: &Ydb_Table.TransactionMeta{ 350 Id: "", 351 }, 352 }, nil 353 }, 354 testutil.TableBeginTransaction: func(interface{}) (proto.Message, error) { 355 return &Ydb_Table.BeginTransactionResult{ 356 TxMeta: &Ydb_Table.TransactionMeta{ 357 Id: "", 358 }, 359 }, nil 360 }, 361 testutil.TableExplainDataQuery: func(request interface{}) (result proto.Message, err error) { 362 return &Ydb_Table.ExplainQueryResult{}, nil 363 }, 364 testutil.TablePrepareDataQuery: func(request interface{}) (result proto.Message, err error) { 365 return &Ydb_Table.PrepareQueryResult{}, nil 366 }, 367 testutil.TableCreateSession: func(interface{}) (proto.Message, error) { 368 return &Ydb_Table.CreateSessionResult{ 369 SessionId: testutil.SessionID(), 370 }, nil 371 }, 372 testutil.TableDeleteSession: func(request interface{}) (result proto.Message, err error) { 373 return &Ydb_Table.DeleteSessionResponse{}, nil 374 }, 375 testutil.TableCommitTransaction: func(request interface{}) (result proto.Message, err error) { 376 return &Ydb_Table.CommitTransactionResult{}, nil 377 }, 378 testutil.TableRollbackTransaction: func(request interface{}) (result proto.Message, err error) { 379 return &Ydb_Table.RollbackTransactionResponse{}, nil 380 }, 381 testutil.TableKeepAlive: func(request interface{}) (result proto.Message, err error) { 382 return &Ydb_Table.KeepAliveResult{}, nil 383 }, 384 }, 385 ), 386 ), config.New()) 387 ctx, cancel := xcontext.WithTimeout( 388 context.Background(), 389 time.Second, 390 ) 391 defer cancel() 392 test.do(t, ctx, client) 393 }) 394 } 395 }, 396 ) 397 } 398 } 399 400 func TestCreateTableRegression(t *testing.T) { 401 client := New(context.Background(), testutil.NewBalancer( 402 testutil.WithInvokeHandlers( 403 testutil.InvokeHandlers{ 404 testutil.TableCreateSession: func(request interface{}) (proto.Message, error) { 405 return &Ydb_Table.CreateSessionResult{ 406 SessionId: "", 407 }, nil 408 }, 409 testutil.TableCreateTable: func(act interface{}) (proto.Message, error) { 410 exp := &Ydb_Table.CreateTableRequest{ 411 SessionId: "", 412 Path: "episodes", 413 Columns: []*Ydb_Table.ColumnMeta{ 414 { 415 Name: "series_id", 416 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 417 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 418 TypeId: Ydb.Type_UINT64, 419 }}}, 420 }}, 421 }, 422 { 423 Name: "season_id", 424 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 425 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 426 TypeId: Ydb.Type_UINT64, 427 }}}, 428 }}, 429 }, 430 { 431 Name: "episode_id", 432 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 433 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 434 TypeId: Ydb.Type_UINT64, 435 }}}, 436 }}, 437 }, 438 { 439 Name: "title", 440 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 441 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 442 TypeId: Ydb.Type_UTF8, 443 }}}, 444 }}, 445 }, 446 { 447 Name: "air_date", 448 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 449 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 450 TypeId: Ydb.Type_UINT64, 451 }}}, 452 }}, 453 }, 454 }, 455 PrimaryKey: []string{ 456 "series_id", 457 "season_id", 458 "episode_id", 459 }, 460 OperationParams: &Ydb_Operations.OperationParams{ 461 OperationMode: Ydb_Operations.OperationParams_SYNC, 462 }, 463 Attributes: map[string]string{ 464 "attr": "attr_value", 465 }, 466 } 467 if !proto.Equal(exp, act.(proto.Message)) { 468 //nolint:revive 469 return nil, fmt.Errorf("proto's not equal: \n\nact: %v\n\nexp: %s\n\n", act, exp) 470 } 471 472 return &Ydb_Table.CreateTableResponse{}, nil 473 }, 474 }, 475 ), 476 ), config.New()) 477 478 ctx, cancel := xcontext.WithTimeout( 479 context.Background(), 480 time.Second, 481 ) 482 defer cancel() 483 484 err := client.Do(ctx, func(ctx context.Context, s table.Session) error { 485 return s.CreateTable(ctx, "episodes", 486 options.WithColumn("series_id", types.NewOptional(types.Uint64)), 487 options.WithColumn("season_id", types.NewOptional(types.Uint64)), 488 options.WithColumn("episode_id", types.NewOptional(types.Uint64)), 489 options.WithColumn("title", types.NewOptional(types.Text)), 490 options.WithColumn("air_date", types.NewOptional(types.Uint64)), 491 options.WithPrimaryKeyColumn("series_id", "season_id", "episode_id"), 492 options.WithAttribute("attr", "attr_value"), 493 ) 494 }) 495 496 require.NoError(t, err, "") 497 } 498 499 func TestDescribeTableRegression(t *testing.T) { 500 client := New(context.Background(), testutil.NewBalancer( 501 testutil.WithInvokeHandlers( 502 testutil.InvokeHandlers{ 503 testutil.TableCreateSession: func(request interface{}) (proto.Message, error) { 504 return &Ydb_Table.CreateSessionResult{ 505 SessionId: "", 506 }, nil 507 }, 508 testutil.TableDescribeTable: func(act interface{}) (proto.Message, error) { 509 return &Ydb_Table.DescribeTableResult{ 510 Self: &Ydb_Scheme.Entry{ 511 Name: "episodes", 512 }, 513 Columns: []*Ydb_Table.ColumnMeta{ 514 { 515 Name: "series_id", 516 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 517 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 518 TypeId: Ydb.Type_UINT64, 519 }}}, 520 }}, 521 }, 522 { 523 Name: "season_id", 524 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 525 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 526 TypeId: Ydb.Type_UINT64, 527 }}}, 528 }}, 529 }, 530 { 531 Name: "episode_id", 532 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 533 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 534 TypeId: Ydb.Type_UINT64, 535 }}}, 536 }}, 537 }, 538 { 539 Name: "title", 540 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 541 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 542 TypeId: Ydb.Type_UTF8, 543 }}}, 544 }}, 545 }, 546 { 547 Name: "air_date", 548 Type: &Ydb.Type{Type: &Ydb.Type_OptionalType{ 549 OptionalType: &Ydb.OptionalType{Item: &Ydb.Type{Type: &Ydb.Type_TypeId{ 550 TypeId: Ydb.Type_UINT64, 551 }}}, 552 }}, 553 }, 554 }, 555 PrimaryKey: []string{ 556 "series_id", 557 "season_id", 558 "episode_id", 559 }, 560 Attributes: map[string]string{ 561 "attr": "attr_value", 562 }, 563 }, nil 564 }, 565 }, 566 ), 567 ), config.New()) 568 569 ctx, cancel := xcontext.WithTimeout( 570 context.Background(), 571 time.Second, 572 ) 573 defer cancel() 574 575 var act options.Description 576 577 err := client.Do(ctx, func(ctx context.Context, s table.Session) (err error) { 578 act, err = s.DescribeTable(ctx, "episodes") 579 580 return err 581 }) 582 583 require.NoError(t, err, "") 584 585 exp := options.Description{ 586 Name: "episodes", 587 Columns: []options.Column{ 588 { 589 Name: "series_id", 590 Type: types.NewOptional(types.Uint64), 591 }, 592 { 593 Name: "season_id", 594 Type: types.NewOptional(types.Uint64), 595 }, 596 { 597 Name: "episode_id", 598 Type: types.NewOptional(types.Uint64), 599 }, 600 { 601 Name: "title", 602 Type: types.NewOptional(types.Text), 603 }, 604 { 605 Name: "air_date", 606 Type: types.NewOptional(types.Uint64), 607 }, 608 }, 609 KeyRanges: []options.KeyRange{ 610 {}, 611 }, 612 PrimaryKey: []string{ 613 "series_id", 614 "season_id", 615 "episode_id", 616 }, 617 ColumnFamilies: []options.ColumnFamily{}, 618 Attributes: map[string]string{ 619 "attr": "attr_value", 620 }, 621 Indexes: []options.IndexDescription{}, 622 Changefeeds: []options.ChangefeedDescription{}, 623 } 624 625 assert.Equal(t, exp, act) 626 } 627 628 var errUnexpectedRequest = errors.New("unexpected request") 629 630 type copyTablesMock struct { 631 *Ydb_Table.CopyTablesRequest 632 } 633 634 func (mock *copyTablesMock) CopyTables( 635 _ context.Context, in *Ydb_Table.CopyTablesRequest, opts ...grpc.CallOption, 636 ) (*Ydb_Table.CopyTablesResponse, error) { 637 if in.String() == mock.String() { 638 return &Ydb_Table.CopyTablesResponse{}, nil 639 } 640 641 return nil, fmt.Errorf("%w: %s, exp: %s", errUnexpectedRequest, in, mock.String()) 642 } 643 644 func TestCopyTables(t *testing.T) { 645 ctx := xtest.Context(t) 646 for _, tt := range []struct { 647 sessionID string 648 operationTimeout time.Duration 649 operationCancelAfter time.Duration 650 service *copyTablesMock 651 opts []options.CopyTablesOption 652 err error 653 }{ 654 { 655 sessionID: "1", 656 operationTimeout: time.Second, 657 operationCancelAfter: time.Second, 658 service: ©TablesMock{ 659 CopyTablesRequest: &Ydb_Table.CopyTablesRequest{ 660 SessionId: "1", 661 Tables: []*Ydb_Table.CopyTableItem{ 662 { 663 SourcePath: "from", 664 DestinationPath: "to", 665 OmitIndexes: true, 666 }, 667 }, 668 OperationParams: &Ydb_Operations.OperationParams{ 669 OperationMode: Ydb_Operations.OperationParams_SYNC, 670 OperationTimeout: durationpb.New(time.Second), 671 CancelAfter: durationpb.New(time.Second), 672 }, 673 }, 674 }, 675 opts: []options.CopyTablesOption{ 676 options.CopyTablesItem("from", "to", true), 677 }, 678 err: nil, 679 }, 680 { 681 sessionID: "2", 682 operationTimeout: 2 * time.Second, 683 operationCancelAfter: 2 * time.Second, 684 service: ©TablesMock{ 685 CopyTablesRequest: &Ydb_Table.CopyTablesRequest{ 686 SessionId: "2", 687 Tables: []*Ydb_Table.CopyTableItem{ 688 { 689 SourcePath: "from1", 690 DestinationPath: "to1", 691 OmitIndexes: true, 692 }, 693 { 694 SourcePath: "from2", 695 DestinationPath: "to2", 696 OmitIndexes: false, 697 }, 698 { 699 SourcePath: "from3", 700 DestinationPath: "to3", 701 OmitIndexes: true, 702 }, 703 }, 704 OperationParams: &Ydb_Operations.OperationParams{ 705 OperationMode: Ydb_Operations.OperationParams_SYNC, 706 OperationTimeout: durationpb.New(2 * time.Second), 707 CancelAfter: durationpb.New(2 * time.Second), 708 }, 709 }, 710 }, 711 opts: []options.CopyTablesOption{ 712 options.CopyTablesItem("from1", "to1", true), 713 options.CopyTablesItem("from2", "to2", false), 714 options.CopyTablesItem("from3", "to3", true), 715 }, 716 err: nil, 717 }, 718 { 719 sessionID: "3", 720 operationTimeout: time.Second, 721 operationCancelAfter: time.Second, 722 service: ©TablesMock{ 723 CopyTablesRequest: &Ydb_Table.CopyTablesRequest{ 724 SessionId: "1", 725 Tables: []*Ydb_Table.CopyTableItem{ 726 { 727 SourcePath: "from", 728 DestinationPath: "to", 729 OmitIndexes: true, 730 }, 731 }, 732 OperationParams: &Ydb_Operations.OperationParams{ 733 OperationMode: Ydb_Operations.OperationParams_SYNC, 734 OperationTimeout: durationpb.New(time.Second), 735 CancelAfter: durationpb.New(time.Second), 736 }, 737 }, 738 }, 739 opts: []options.CopyTablesOption{ 740 options.CopyTablesItem("from1", "to1", true), 741 }, 742 err: errUnexpectedRequest, 743 }, 744 { 745 sessionID: "4", 746 operationTimeout: time.Second, 747 operationCancelAfter: time.Second, 748 service: ©TablesMock{}, 749 opts: nil, 750 err: errParamsRequired, 751 }, 752 } { 753 t.Run("", func(t *testing.T) { 754 err := copyTables(ctx, tt.sessionID, tt.operationTimeout, tt.operationCancelAfter, tt.service, tt.opts...) 755 if tt.err != nil { 756 require.ErrorIs(t, err, tt.err) 757 } else { 758 require.NoError(t, err) 759 } 760 }) 761 } 762 } 763 764 type renameTablesMock struct { 765 *Ydb_Table.RenameTablesRequest 766 } 767 768 func (mock *renameTablesMock) RenameTables( 769 _ context.Context, in *Ydb_Table.RenameTablesRequest, opts ...grpc.CallOption, 770 ) (*Ydb_Table.RenameTablesResponse, error) { 771 if in.String() == mock.String() { 772 return &Ydb_Table.RenameTablesResponse{}, nil 773 } 774 775 return nil, fmt.Errorf("%w: %s, exp: %s", errUnexpectedRequest, in, mock.String()) 776 } 777 778 func TestRenameTables(t *testing.T) { 779 ctx := xtest.Context(t) 780 for _, tt := range []struct { 781 sessionID string 782 operationTimeout time.Duration 783 operationCancelAfter time.Duration 784 service *renameTablesMock 785 opts []options.RenameTablesOption 786 err error 787 }{ 788 { 789 sessionID: "1", 790 operationTimeout: time.Second, 791 operationCancelAfter: time.Second, 792 service: &renameTablesMock{ 793 RenameTablesRequest: &Ydb_Table.RenameTablesRequest{ 794 SessionId: "1", 795 Tables: []*Ydb_Table.RenameTableItem{ 796 { 797 SourcePath: "from", 798 DestinationPath: "to", 799 ReplaceDestination: true, 800 }, 801 }, 802 OperationParams: &Ydb_Operations.OperationParams{ 803 OperationMode: Ydb_Operations.OperationParams_SYNC, 804 OperationTimeout: durationpb.New(time.Second), 805 CancelAfter: durationpb.New(time.Second), 806 }, 807 }, 808 }, 809 opts: []options.RenameTablesOption{ 810 options.RenameTablesItem("from", "to", true), 811 }, 812 err: nil, 813 }, 814 { 815 sessionID: "2", 816 operationTimeout: 2 * time.Second, 817 operationCancelAfter: 2 * time.Second, 818 service: &renameTablesMock{ 819 RenameTablesRequest: &Ydb_Table.RenameTablesRequest{ 820 SessionId: "2", 821 Tables: []*Ydb_Table.RenameTableItem{ 822 { 823 SourcePath: "from1", 824 DestinationPath: "to1", 825 ReplaceDestination: true, 826 }, 827 { 828 SourcePath: "from2", 829 DestinationPath: "to2", 830 ReplaceDestination: false, 831 }, 832 { 833 SourcePath: "from3", 834 DestinationPath: "to3", 835 ReplaceDestination: true, 836 }, 837 }, 838 OperationParams: &Ydb_Operations.OperationParams{ 839 OperationMode: Ydb_Operations.OperationParams_SYNC, 840 OperationTimeout: durationpb.New(2 * time.Second), 841 CancelAfter: durationpb.New(2 * time.Second), 842 }, 843 }, 844 }, 845 opts: []options.RenameTablesOption{ 846 options.RenameTablesItem("from1", "to1", true), 847 options.RenameTablesItem("from2", "to2", false), 848 options.RenameTablesItem("from3", "to3", true), 849 }, 850 err: nil, 851 }, 852 { 853 sessionID: "3", 854 operationTimeout: time.Second, 855 operationCancelAfter: time.Second, 856 service: &renameTablesMock{ 857 RenameTablesRequest: &Ydb_Table.RenameTablesRequest{ 858 SessionId: "1", 859 Tables: []*Ydb_Table.RenameTableItem{ 860 { 861 SourcePath: "from", 862 DestinationPath: "to", 863 ReplaceDestination: true, 864 }, 865 }, 866 OperationParams: &Ydb_Operations.OperationParams{ 867 OperationMode: Ydb_Operations.OperationParams_SYNC, 868 OperationTimeout: durationpb.New(time.Second), 869 CancelAfter: durationpb.New(time.Second), 870 }, 871 }, 872 }, 873 opts: []options.RenameTablesOption{ 874 options.RenameTablesItem("from1", "to1", true), 875 }, 876 err: errUnexpectedRequest, 877 }, 878 { 879 sessionID: "4", 880 operationTimeout: time.Second, 881 operationCancelAfter: time.Second, 882 service: &renameTablesMock{}, 883 opts: nil, 884 err: errParamsRequired, 885 }, 886 } { 887 t.Run("", func(t *testing.T) { 888 err := renameTables(ctx, tt.sessionID, tt.operationTimeout, tt.operationCancelAfter, tt.service, tt.opts...) 889 if tt.err != nil { 890 require.ErrorIs(t, err, tt.err) 891 } else { 892 require.NoError(t, err) 893 } 894 }) 895 } 896 }