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 }