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: &copyTablesMock{
   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: &copyTablesMock{
   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: &copyTablesMock{
   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:              &copyTablesMock{},
   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  }