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