github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/processors/query/operator-enrichment-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/mock"
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/voedger/voedger/pkg/appdef"
    14  	"github.com/voedger/voedger/pkg/istructs"
    15  	"github.com/voedger/voedger/pkg/pipeline"
    16  	"github.com/voedger/voedger/pkg/state"
    17  	coreutils "github.com/voedger/voedger/pkg/utils"
    18  )
    19  
    20  func TestEnrichmentOperator_DoSync(t *testing.T) {
    21  
    22  	t.Run("Should set reference fields", func(t *testing.T) {
    23  		require := require.New(t)
    24  
    25  		appDef := func() appdef.IAppDef {
    26  			adb := appdef.New()
    27  
    28  			addObject := func(n appdef.QName) {
    29  				adb.AddObject(n).
    30  					AddField("id_lower_case_name", appdef.DataKind_RecordID, false)
    31  			}
    32  
    33  			addObject(appdef.NewQName("_", "root"))
    34  			addObject(appdef.NewQName("f", "first_children_1"))
    35  			addObject(appdef.NewQName("f", "deep_children_1"))
    36  			addObject(appdef.NewQName("f", "very_deep_children_1"))
    37  			addObject(appdef.NewQName("s", "first_children_2"))
    38  			addObject(appdef.NewQName("s", "deep_children_1"))
    39  			addObject(appdef.NewQName("s", "very_deep_children_1"))
    40  
    41  			adb.AddObject(qNameXLowerCase).
    42  				AddField("name", appdef.DataKind_string, false)
    43  
    44  			app, err := adb.Build()
    45  			require.NoError(err)
    46  
    47  			return app
    48  		}()
    49  
    50  		commonFields := []IRefField{refField{field: "id_lower_case_name", ref: "name", key: "id_lower_case_name/name"}}
    51  
    52  		elements := []IElement{
    53  			element{
    54  				path: path{rootDocument},
    55  				refs: commonFields,
    56  			},
    57  			element{
    58  				path: path{"first_children_1"},
    59  				refs: commonFields,
    60  			},
    61  			element{
    62  				path: path{"first_children_1", "deep_children_1"},
    63  				refs: commonFields,
    64  			},
    65  			element{
    66  				path: path{"first_children_1", "deep_children_1", "very_deep_children_1"},
    67  				refs: commonFields,
    68  			},
    69  			element{
    70  				path: path{"first_children_2"},
    71  				refs: commonFields,
    72  			},
    73  			element{
    74  				path: path{"first_children_2", "deep_children_1"},
    75  				refs: commonFields,
    76  			},
    77  			element{
    78  				path: path{"first_children_2", "deep_children_1", "very_deep_children_1"},
    79  				refs: commonFields,
    80  			},
    81  		}
    82  		row := func(idLowerCaseName int) IOutputRow {
    83  			return &outputRow{
    84  				keyToIdx: map[string]int{
    85  					"id_lower_case_name/name": 0,
    86  				},
    87  				values: []interface{}{
    88  					istructs.RecordID(idLowerCaseName),
    89  				},
    90  			}
    91  		}
    92  		record := func(name string) istructs.IStateValue {
    93  			r := &mockRecord{}
    94  			r.
    95  				On("AsString", "name").Return(name).
    96  				On("QName").Return(qNameXLowerCase)
    97  			sv := &mockStateValue{}
    98  			sv.On("AsRecord", "").Return(r)
    99  			return sv
   100  		}
   101  		work := func() pipeline.IWorkpiece {
   102  			o := &coreutils.TestObject{
   103  				Name:    appdef.NewQName("", "root"),
   104  				Id:      istructs.RecordID(1),
   105  				Parent_: istructs.NullRecordID,
   106  				Data: map[string]interface{}{
   107  					"id_lower_case_name": istructs.RecordID(2001),
   108  					"name":               "ROOT",
   109  				},
   110  				Containers_: map[string][]*coreutils.TestObject{
   111  					"first_children_1": {
   112  						{
   113  							Name:    appdef.NewQName("f", "first_children_1"),
   114  							Id:      istructs.RecordID(101),
   115  							Parent_: istructs.RecordID(1),
   116  							Data: map[string]interface{}{
   117  								"id_lower_case_name": istructs.RecordID(200101),
   118  								"name":               "FIRST_CHILDREN_1_101",
   119  							},
   120  							Containers_: map[string][]*coreutils.TestObject{
   121  								"deep_children_1": {
   122  									{
   123  										Name:    appdef.NewQName("f", "deep_children_1"),
   124  										Id:      istructs.RecordID(201),
   125  										Parent_: istructs.RecordID(101),
   126  										Data: map[string]interface{}{
   127  											"id_lower_case_name": istructs.RecordID(200201),
   128  											"name":               "DEEP_CHILDREN_1_201",
   129  										},
   130  										Containers_: map[string][]*coreutils.TestObject{
   131  											"very_deep_children_1": {
   132  												{
   133  													Name:    appdef.NewQName("f", "very_deep_children_1"),
   134  													Id:      istructs.RecordID(301),
   135  													Parent_: istructs.RecordID(201),
   136  													Data: map[string]interface{}{
   137  														"id_lower_case_name": istructs.RecordID(200301),
   138  														"name":               "VERY_DEEP_CHILDREN_1_301",
   139  													},
   140  												},
   141  											},
   142  										},
   143  									},
   144  								},
   145  							},
   146  						},
   147  						{
   148  							Name:    appdef.NewQName("f", "first_children_1"),
   149  							Id:      istructs.RecordID(102),
   150  							Parent_: istructs.RecordID(1),
   151  							Data: map[string]interface{}{
   152  								"id_lower_case_name": istructs.RecordID(200102),
   153  								"name":               "FIRST_CHILDREN_1_102",
   154  							},
   155  						},
   156  					},
   157  					"first_children_2": {
   158  						{
   159  							Name:    appdef.NewQName("s", "first_children_2"),
   160  							Id:      istructs.RecordID(401),
   161  							Parent_: istructs.RecordID(1),
   162  							Data: map[string]interface{}{
   163  								"id_lower_case_name": istructs.RecordID(200401),
   164  								"name":               "FIRST_CHILDREN_2_401",
   165  							},
   166  							Containers_: map[string][]*coreutils.TestObject{
   167  								"deep_children_1": {
   168  									{
   169  										Name:    appdef.NewQName("s", "deep_children_1"),
   170  										Id:      istructs.RecordID(501),
   171  										Parent_: istructs.RecordID(401),
   172  										Data: map[string]interface{}{
   173  											"id_lower_case_name": istructs.RecordID(200501),
   174  											"name":               "DEEP_CHILDREN_1_501",
   175  										},
   176  										Containers_: map[string][]*coreutils.TestObject{
   177  											"very_deep_children_1": {
   178  												{
   179  													Name:    appdef.NewQName("s", "very_deep_children_1"),
   180  													Id:      istructs.RecordID(601),
   181  													Parent_: istructs.RecordID(501),
   182  													Data: map[string]interface{}{
   183  														"id_lower_case_name": istructs.RecordID(200601),
   184  														"name":               "VERY_DEEP_CHILDREN_1_601",
   185  													},
   186  												},
   187  												{
   188  													Name:    appdef.NewQName("s", "very_deep_children_1"),
   189  													Id:      istructs.RecordID(602),
   190  													Parent_: istructs.RecordID(501),
   191  													Data: map[string]interface{}{
   192  														"id_lower_case_name": istructs.RecordID(200602),
   193  														"name":               "VERY_DEEP_CHILDREN_1_602",
   194  													},
   195  												},
   196  											},
   197  										},
   198  									},
   199  								},
   200  							},
   201  						},
   202  					},
   203  				},
   204  			}
   205  			return rowsWorkpiece{
   206  				object: o,
   207  				outputRow: &outputRow{
   208  					keyToIdx: map[string]int{
   209  						rootDocument:                       0,
   210  						"first_children_1":                 1,
   211  						"first_children_1/deep_children_1": 2,
   212  						"first_children_1/deep_children_1/very_deep_children_1": 3,
   213  						"first_children_2":                                      4,
   214  						"first_children_2/deep_children_1":                      5,
   215  						"first_children_2/deep_children_1/very_deep_children_1": 6,
   216  					},
   217  					values: []interface{}{
   218  						[]IOutputRow{row(2001)},
   219  						[]IOutputRow{row(200101), row(200102)},
   220  						[]IOutputRow{row(200201)},
   221  						[]IOutputRow{row(200301)},
   222  						[]IOutputRow{row(200401)},
   223  						[]IOutputRow{row(200501)},
   224  						[]IOutputRow{row(200601), row(200602)},
   225  					},
   226  				},
   227  				enrichedRootFieldsKinds: make(map[string]appdef.DataKind),
   228  			}
   229  		}
   230  		skb := &mockStateKeyBuilder{}
   231  		skb.On("PutRecordID", mock.Anything, mock.Anything)
   232  		s := &mockState{}
   233  		s.
   234  			On("KeyBuilder", state.Record, appdef.NullQName).Return(skb).
   235  			On("MustExist", mock.Anything).Return(record("root")).Once().
   236  			On("MustExist", mock.Anything).Return(record("first_children_1_101")).Once().
   237  			On("MustExist", mock.Anything).Return(record("first_children_1_102")).Once().
   238  			On("MustExist", mock.Anything).Return(record("deep_children_1_201")).Once().
   239  			On("MustExist", mock.Anything).Return(record("very_deep_children_1_301")).Once().
   240  			On("MustExist", mock.Anything).Return(record("first_children_2_401")).Once().
   241  			On("MustExist", mock.Anything).Return(record("deep_children_1_501")).Once().
   242  			On("MustExist", mock.Anything).Return(record("very_deep_children_1_601")).Once().
   243  			On("MustExist", mock.Anything).Return(record("very_deep_children_1_602")).Once()
   244  		op := &EnrichmentOperator{
   245  			state:      s,
   246  			elements:   elements,
   247  			fieldsDefs: newFieldsDefs(appDef),
   248  			metrics:    &testMetrics{},
   249  		}
   250  
   251  		outWork, err := op.DoAsync(context.Background(), work())
   252  
   253  		require.NoError(err)
   254  		require.Len(outWork.(IWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow), 1)
   255  		require.Equal("root", outWork.(IWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Value("id_lower_case_name/name"))
   256  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow), 2)
   257  		require.Equal("first_children_1_101", outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   258  		require.Equal("first_children_1_102", outWork.(IWorkpiece).OutputRow().Value("first_children_1").([]IOutputRow)[1].Value("id_lower_case_name/name"))
   259  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1").([]IOutputRow), 1)
   260  		require.Equal("deep_children_1_201", outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   261  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1/very_deep_children_1").([]IOutputRow), 1)
   262  		require.Equal("very_deep_children_1_301", outWork.(IWorkpiece).OutputRow().Value("first_children_1/deep_children_1/very_deep_children_1").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   263  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2").([]IOutputRow), 1)
   264  		require.Equal("first_children_2_401", outWork.(IWorkpiece).OutputRow().Value("first_children_2").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   265  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1").([]IOutputRow), 1)
   266  		require.Equal("deep_children_1_501", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   267  		require.Len(outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow), 2)
   268  		require.Equal("very_deep_children_1_601", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow)[0].Value("id_lower_case_name/name"))
   269  		require.Equal("very_deep_children_1_602", outWork.(IWorkpiece).OutputRow().Value("first_children_2/deep_children_1/very_deep_children_1").([]IOutputRow)[1].Value("id_lower_case_name/name"))
   270  	})
   271  	t.Run("Should handle ctx error during row fill with ref fields", func(t *testing.T) {
   272  		require := require.New(t)
   273  		ctx, cancel := context.WithCancel(context.Background())
   274  		cancel()
   275  		work := rowsWorkpiece{
   276  			outputRow: &outputRow{
   277  				keyToIdx: map[string]int{rootDocument: 0},
   278  				values: []interface{}{
   279  					[]IOutputRow{&outputRow{}},
   280  				},
   281  			},
   282  		}
   283  		op := EnrichmentOperator{
   284  			elements: []IElement{element{path: path{""}, refs: []IRefField{refField{"", "", ""}}}},
   285  			metrics:  &testMetrics{},
   286  		}
   287  
   288  		outWork, err := op.DoAsync(ctx, work)
   289  
   290  		require.Equal("context canceled", err.Error())
   291  		require.Equal(work, outWork)
   292  	})
   293  }