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 }