github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/processors/query/operator-result-fields-impl_test.go (about)

     1  /*
     2   * Copyright (c) 2021-present unTill Pro, Ltd.
     3   */
     4  
     5  package queryprocessor
     6  
     7  import (
     8  	"context"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/voedger/voedger/pkg/appdef"
    13  	"github.com/voedger/voedger/pkg/istructs"
    14  	"github.com/voedger/voedger/pkg/pipeline"
    15  	coreutils "github.com/voedger/voedger/pkg/utils"
    16  )
    17  
    18  func TestResultFieldsOperator_DoSync(t *testing.T) {
    19  	t.Run("Should set result fields", func(t *testing.T) {
    20  		require := require.New(t)
    21  
    22  		var (
    23  			appDef  appdef.IAppDef
    24  			rootObj appdef.IObject
    25  		)
    26  
    27  		t.Run("Should set result fields", func(t *testing.T) {
    28  			adb := appdef.New()
    29  
    30  			addObject := func(n appdef.QName) {
    31  				o := adb.AddObject(n)
    32  				o.AddField("name", appdef.DataKind_string, false)
    33  			}
    34  			addObject(appdef.NewQName("_", "root"))
    35  			addObject(appdef.NewQName("f", "first_children_1"))
    36  			addObject(appdef.NewQName("f", "deep_children_1"))
    37  			addObject(appdef.NewQName("f", "very_deep_children_1"))
    38  			addObject(appdef.NewQName("s", "first_children_2"))
    39  			addObject(appdef.NewQName("s", "deep_children_1"))
    40  			addObject(appdef.NewQName("s", "very_deep_children_1"))
    41  
    42  			objName := appdef.NewQName("test", "root")
    43  			addObject(objName)
    44  
    45  			app, err := adb.Build()
    46  			require.NoError(err)
    47  
    48  			appDef = app
    49  			rootObj = app.Object(objName)
    50  		})
    51  
    52  		commonFields := []IResultField{resultField{field: "name"}}
    53  
    54  		elements := []IElement{
    55  			element{
    56  				path:   path{rootDocument},
    57  				fields: commonFields,
    58  			},
    59  			element{
    60  				path:   path{"first_children_1"},
    61  				fields: commonFields,
    62  			},
    63  			element{
    64  				path:   path{"first_children_1", "deep_children_1"},
    65  				fields: commonFields,
    66  			},
    67  			element{
    68  				path:   path{"first_children_1", "deep_children_1", "very_deep_children_1"},
    69  				fields: commonFields,
    70  			},
    71  			element{
    72  				path:   path{"first_children_2"},
    73  				fields: commonFields,
    74  			},
    75  			element{
    76  				path:   path{"first_children_2", "deep_children_1"},
    77  				fields: commonFields,
    78  			},
    79  			element{
    80  				path:   path{"first_children_2", "deep_children_1", "very_deep_children_1"},
    81  				fields: commonFields,
    82  			},
    83  		}
    84  
    85  		work := func() pipeline.IWorkpiece {
    86  			o := &coreutils.TestObject{
    87  				Name:    appdef.NewQName("_", "root"),
    88  				Id:      istructs.RecordID(1),
    89  				Parent_: istructs.NullRecordID,
    90  				Data: map[string]interface{}{
    91  					"name": "ROOT",
    92  				},
    93  				Containers_: map[string][]*coreutils.TestObject{
    94  					"first_children_1": {
    95  						{
    96  							Name:    appdef.NewQName("f", "first_children_1"),
    97  							Id:      istructs.RecordID(101),
    98  							Parent_: istructs.RecordID(1),
    99  							Data: map[string]interface{}{
   100  								"name": "FIRST_CHILDREN_1_101",
   101  							},
   102  							Containers_: map[string][]*coreutils.TestObject{
   103  								"deep_children_1": {
   104  									{
   105  										Name:    appdef.NewQName("f", "deep_children_1"),
   106  										Id:      istructs.RecordID(201),
   107  										Parent_: istructs.RecordID(101),
   108  										Data: map[string]interface{}{
   109  											"name": "DEEP_CHILDREN_1_201",
   110  										},
   111  										Containers_: map[string][]*coreutils.TestObject{
   112  											"very_deep_children_1": {
   113  												{
   114  													Name:    appdef.NewQName("f", "very_deep_children_1"),
   115  													Id:      istructs.RecordID(301),
   116  													Parent_: istructs.RecordID(201),
   117  													Data: map[string]interface{}{
   118  														"name": "VERY_DEEP_CHILDREN_1_301",
   119  													},
   120  												},
   121  											},
   122  										},
   123  									},
   124  								},
   125  							},
   126  						},
   127  						{
   128  							Name:    appdef.NewQName("f", "first_children_1"),
   129  							Id:      istructs.RecordID(102),
   130  							Parent_: istructs.RecordID(1),
   131  							Data: map[string]interface{}{
   132  								"name": "FIRST_CHILDREN_1_102",
   133  							},
   134  						},
   135  					},
   136  					"first_children_2": {
   137  						{
   138  							Name:    appdef.NewQName("s", "first_children_2"),
   139  							Id:      istructs.RecordID(401),
   140  							Parent_: istructs.RecordID(1),
   141  							Data: map[string]interface{}{
   142  								"name": "FIRST_CHILDREN_2_401",
   143  							},
   144  							Containers_: map[string][]*coreutils.TestObject{
   145  								"deep_children_1": {
   146  									{
   147  										Name:    appdef.NewQName("s", "deep_children_1"),
   148  										Id:      istructs.RecordID(501),
   149  										Parent_: istructs.RecordID(401),
   150  										Data: map[string]interface{}{
   151  											"name": "DEEP_CHILDREN_1_501",
   152  										},
   153  										Containers_: map[string][]*coreutils.TestObject{
   154  											"very_deep_children_1": {
   155  												{
   156  													Name:    appdef.NewQName("s", "very_deep_children_1"),
   157  													Id:      istructs.RecordID(601),
   158  													Parent_: istructs.RecordID(501),
   159  													Data: map[string]interface{}{
   160  														"name": "VERY_DEEP_CHILDREN_1_601",
   161  													},
   162  												},
   163  												{
   164  													Name:    appdef.NewQName("s", "very_deep_children_1"),
   165  													Id:      istructs.RecordID(602),
   166  													Parent_: istructs.RecordID(501),
   167  													Data: map[string]interface{}{
   168  														"name": "VERY_DEEP_CHILDREN_1_602",
   169  													},
   170  												},
   171  											},
   172  										},
   173  									},
   174  								},
   175  							},
   176  						},
   177  					},
   178  				},
   179  			}
   180  			return rowsWorkpiece{
   181  				object: o,
   182  				outputRow: &outputRow{
   183  					keyToIdx: map[string]int{
   184  						rootDocument:                       0,
   185  						"first_children_1":                 1,
   186  						"first_children_1/deep_children_1": 2,
   187  						"first_children_1/deep_children_1/very_deep_children_1": 3,
   188  						"first_children_2":                                      4,
   189  						"first_children_2/deep_children_1":                      5,
   190  						"first_children_2/deep_children_1/very_deep_children_1": 6,
   191  					},
   192  					values: make([]interface{}, 7),
   193  				},
   194  			}
   195  		}
   196  
   197  		operator := &ResultFieldsOperator{
   198  			elements:   elements,
   199  			rootFields: newFieldsKinds(rootObj),
   200  			fieldsDefs: newFieldsDefs(appDef),
   201  			metrics:    &testMetrics{},
   202  		}
   203  
   204  		outWork, err := operator.DoAsync(context.Background(), work())
   205  
   206  		require.NoError(err)
   207  		require.Len(outWork.(IWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow), 1)
   208  		require.Equal("ROOT", outWork.(IWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Value("name"))
   209  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow), 2)
   210  		require.Equal("FIRST_CHILDREN_1_101", outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow)[0].Value("name"))
   211  		require.Equal("FIRST_CHILDREN_1_102", outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow)[1].Value("name"))
   212  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1").([]IOutputRow), 1)
   213  		require.Equal("DEEP_CHILDREN_1_201", outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1").([]IOutputRow)[0].Value("name"))
   214  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1/very_deep_children_1").([]IOutputRow), 1)
   215  		require.Equal("VERY_DEEP_CHILDREN_1_301", outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1/very_deep_children_1").([]IOutputRow)[0].Value("name"))
   216  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2").([]IOutputRow), 1)
   217  		require.Equal("FIRST_CHILDREN_2_401", outWork.(IWorkpiece).OutputRow().Value("first_children_2").([]IOutputRow)[0].Value("name"))
   218  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1").([]IOutputRow), 1)
   219  		require.Equal("DEEP_CHILDREN_1_501", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1").([]IOutputRow)[0].Value("name"))
   220  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow), 2)
   221  		require.Equal("VERY_DEEP_CHILDREN_1_601", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow)[0].Value("name"))
   222  		require.Equal("VERY_DEEP_CHILDREN_1_602", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow)[1].Value("name"))
   223  	})
   224  	t.Run("Should handle ctx error during row fill with result fields", func(t *testing.T) {
   225  		require := require.New(t)
   226  		ctx, cancel := context.WithCancel(context.Background())
   227  		cancel()
   228  		operator := ResultFieldsOperator{
   229  			elements: []IElement{element{path: path{""}, fields: []IResultField{resultField{""}}}},
   230  			metrics:  &testMetrics{},
   231  		}
   232  		work := rowsWorkpiece{
   233  			outputRow: &outputRow{
   234  				keyToIdx: map[string]int{"": 0},
   235  				values:   []interface{}{nil},
   236  			},
   237  		}
   238  
   239  		outWork, err := operator.DoAsync(ctx, work)
   240  
   241  		require.Equal("context canceled", err.Error())
   242  		require.NotNil(outWork)
   243  	})
   244  	t.Run("Should handle read field value error during row fill with result fields", func(t *testing.T) {
   245  		require := require.New(t)
   246  		work := rowsWorkpiece{
   247  			outputRow: &outputRow{
   248  				keyToIdx: map[string]int{"": 0},
   249  				values:   []interface{}{nil},
   250  			},
   251  		}
   252  		operator := ResultFieldsOperator{
   253  			rootFields: map[string]appdef.DataKind{"": appdef.DataKind_FakeLast},
   254  			elements:   []IElement{element{path: path{""}, fields: []IResultField{resultField{""}}}},
   255  			metrics:    &testMetrics{},
   256  		}
   257  
   258  		require.Panics(func() { operator.DoAsync(context.Background(), work) })
   259  	})
   260  	t.Run("Should handle ctx error during row fill with ref fields", func(t *testing.T) {
   261  		require := require.New(t)
   262  		ctx, cancel := context.WithCancel(context.Background())
   263  		cancel()
   264  		work := rowsWorkpiece{
   265  			outputRow: &outputRow{
   266  				keyToIdx: map[string]int{"": 0},
   267  				values:   []interface{}{nil},
   268  			},
   269  		}
   270  		operator := ResultFieldsOperator{
   271  			elements: []IElement{element{path: path{""}, refs: []IRefField{refField{"", "", ""}}}},
   272  			metrics:  &testMetrics{},
   273  		}
   274  
   275  		outWork, err := operator.DoAsync(ctx, work)
   276  
   277  		require.Equal("context canceled", err.Error())
   278  		require.NotNil(outWork)
   279  	})
   280  	t.Run("Should handle ctx error during row fill with result fields from elements", func(t *testing.T) {
   281  		require := require.New(t)
   282  		ctx, cancel := context.WithCancel(context.Background())
   283  		cancel()
   284  		work := rowsWorkpiece{
   285  			object: &coreutils.TestObject{Containers_: map[string][]*coreutils.TestObject{
   286  				"container": {&coreutils.TestObject{Data: map[string]interface{}{"": ""}}},
   287  			}},
   288  			outputRow: &outputRow{
   289  				keyToIdx: map[string]int{"": 0},
   290  				values:   []interface{}{nil},
   291  			},
   292  		}
   293  		operator := ResultFieldsOperator{
   294  			fieldsDefs: &fieldsDefs{fields: map[appdef.QName]FieldsKinds{appdef.NullQName: nil}},
   295  			elements:   []IElement{element{path: path{"container"}, fields: []IResultField{resultField{""}}}},
   296  			metrics:    &testMetrics{},
   297  		}
   298  
   299  		outWork, err := operator.DoAsync(ctx, work)
   300  
   301  		require.Equal("context canceled", err.Error())
   302  		require.NotNil(outWork)
   303  	})
   304  }