github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/utils/objectreader_test.go (about) 1 /* 2 * Copyright (c) 2020-present unTill Pro, Ltd. 3 */ 4 5 package coreutils 6 7 import ( 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/voedger/voedger/pkg/appdef" 13 "github.com/voedger/voedger/pkg/istructs" 14 ) 15 16 var ( 17 testWS = appdef.NewQName("test", "test_ws") 18 testQName = appdef.NewQName("test", "QName") 19 testQNameSimple = appdef.NewQName("test", "QNameSimple") 20 testQNameView = appdef.NewQName("test", "view") 21 testFieldDefs = map[string]appdef.DataKind{ 22 "int32": appdef.DataKind_int32, 23 "int64": appdef.DataKind_int64, 24 "float32": appdef.DataKind_float32, 25 "float64": appdef.DataKind_float64, 26 "string": appdef.DataKind_string, 27 "bool": appdef.DataKind_bool, 28 "bytes": appdef.DataKind_bytes, 29 "recordID": appdef.DataKind_RecordID, 30 } 31 32 testAppDef = func(t *testing.T) appdef.IAppDef { 33 adb := appdef.New() 34 35 obj := adb.AddObject(testQName) 36 addFieldDefs(obj, testFieldDefs) 37 38 simpleObj := adb.AddObject(testQNameSimple) 39 simpleObj.AddField("int32", appdef.DataKind_int32, false) 40 41 view := adb.AddView(testQNameView) 42 view.Key().PartKey().AddField("pk", appdef.DataKind_int64) 43 view.Key().ClustCols().AddField("cc", appdef.DataKind_string) 44 iValueFields := map[string]appdef.DataKind{} 45 for n, k := range testFieldDefs { 46 iValueFields[n] = k 47 } 48 iValueFields["record"] = appdef.DataKind_Record 49 for n, k := range iValueFields { 50 view.Value().AddField(n, k, false) 51 } 52 53 ws := adb.AddWorkspace(testWS) 54 ws.AddType(testQName) 55 ws.AddType(testQNameSimple) 56 ws.AddType(appdef.NewQName("test", "view")) 57 58 app, err := adb.Build() 59 require.NoError(t, err) 60 61 return app 62 } 63 64 testData = map[string]interface{}{ 65 "int32": int32(1), 66 "int64": int64(2), 67 "float32": float32(3), 68 "float64": float64(4), 69 "string": "str", 70 "bool": true, 71 "bytes": []byte{5, 6}, 72 "recordID": istructs.RecordID(7), 73 appdef.SystemField_QName: testQName, 74 } 75 testDataSimple = map[string]interface{}{ 76 appdef.SystemField_QName: testQNameSimple, 77 "int32": int32(42), 78 } 79 testBasic = func(expectedQName appdef.QName, m map[string]interface{}, require *require.Assertions) { 80 require.Equal(int32(1), m["int32"]) 81 require.Equal(int64(2), m["int64"]) 82 require.Equal(float32(3), m["float32"]) 83 require.Equal(float64(4), m["float64"]) 84 require.Equal("str", m["string"]) 85 v, ok := m["bool"].(bool) 86 require.True(ok) 87 require.True(v) 88 require.Equal([]byte{5, 6}, m["bytes"]) 89 require.Equal(istructs.RecordID(7), m["recordID"]) 90 actualQName, err := appdef.ParseQName(m[appdef.SystemField_QName].(string)) 91 require.NoError(err) 92 require.Equal(expectedQName, actualQName) 93 } 94 ) 95 96 func addFieldDefs(fields appdef.IFieldsBuilder, fd map[string]appdef.DataKind) { 97 for n, k := range fd { 98 if !appdef.IsSysField(n) { 99 fields.AddField(n, k, false) 100 } 101 } 102 } 103 104 func TestToMap_Basic(t *testing.T) { 105 require := require.New(t) 106 obj := &TestObject{ 107 Name: testQName, 108 Id: 42, 109 Data: testData, 110 Containers_: map[string][]*TestObject{ 111 "container": { 112 { 113 Name: testQNameSimple, 114 Data: testDataSimple, 115 }, 116 }, 117 }, 118 } 119 120 appDef := testAppDef(t) 121 122 t.Run("ObjectToMap", func(t *testing.T) { 123 m := ObjectToMap(obj, appDef) 124 testBasic(testQName, m, require) 125 containerObjects := m["container"].([]map[string]interface{}) 126 require.Len(containerObjects, 1) 127 containerObj := containerObjects[0] 128 require.Equal(int32(42), containerObj["int32"]) 129 require.Equal(testQNameSimple.String(), containerObj[appdef.SystemField_QName]) 130 }) 131 132 t.Run("FieldsToMap", func(t *testing.T) { 133 m := FieldsToMap(obj, appDef) 134 testBasic(testQName, m, require) 135 }) 136 137 t.Run("null QName", func(t *testing.T) { 138 obj = &TestObject{ 139 Name: appdef.NullQName, 140 Id: 42, 141 Data: map[string]interface{}{}, 142 } 143 m := ObjectToMap(obj, appDef) 144 require.Empty(m) 145 m = FieldsToMap(obj, appDef) 146 require.Empty(m) 147 }) 148 } 149 150 func TestToMap_Filter(t *testing.T) { 151 require := require.New(t) 152 obj := &TestObject{ 153 Name: testQName, 154 Id: 42, 155 Data: testData, 156 } 157 158 count := 0 159 filter := Filter(func(name string, kind appdef.DataKind) bool { 160 if name == "bool" { 161 require.Equal(appdef.DataKind_bool, kind) 162 count++ 163 return true 164 } 165 if name == "string" { 166 require.Equal(appdef.DataKind_string, kind) 167 count++ 168 return true 169 } 170 return false 171 }) 172 173 appDef := testAppDef(t) 174 175 t.Run("ObjectToMap", func(t *testing.T) { 176 m := ObjectToMap(obj, appDef, filter) 177 require.Equal(2, count) 178 require.Len(m, 2) 179 v, ok := m["bool"].(bool) 180 require.True(ok) 181 require.True(v) 182 require.Equal("str", m["string"]) 183 }) 184 185 t.Run("FieldsToMap", func(t *testing.T) { 186 m := FieldsToMap(obj, appDef, filter) 187 require.Equal(4, count) 188 require.Len(m, 2) 189 v, ok := m["bool"].(bool) 190 require.True(ok) 191 require.True(v) 192 require.Equal("str", m["string"]) 193 }) 194 } 195 196 func TestMToMap_NonNilsOnly_Filter(t *testing.T) { 197 require := require.New(t) 198 testDataPartial := map[string]interface{}{ 199 "int32": int32(1), 200 "string": "str", 201 "float32": float32(2), 202 appdef.SystemField_QName: testQName, 203 } 204 obj := &TestObject{ 205 Name: testQName, 206 Id: 42, 207 Data: testDataPartial, 208 } 209 expected := map[string]interface{}{ 210 "int32": int32(1), 211 "string": "str", 212 appdef.SystemField_QName: testQName.String(), 213 } 214 215 appDef := testAppDef(t) 216 217 t.Run("ObjectToMap", func(t *testing.T) { 218 m := ObjectToMap(obj, appDef, WithNonNilsOnly(), Filter(func(name string, kind appdef.DataKind) bool { 219 return name != "float32" 220 })) 221 require.Equal(expected, m) 222 }) 223 224 t.Run("FieldsToMap", func(t *testing.T) { 225 m := FieldsToMap(obj, appDef, WithNonNilsOnly(), Filter(func(name string, kind appdef.DataKind) bool { 226 return name != "float32" 227 })) 228 require.Equal(expected, m) 229 }) 230 231 t.Run("ObjectToMap + filter", func(t *testing.T) { 232 filter := Filter(func(name string, kind appdef.DataKind) bool { 233 return name == "string" 234 }) 235 expected := map[string]interface{}{ 236 "string": "str", 237 } 238 m := ObjectToMap(obj, appDef, WithNonNilsOnly(), filter) 239 require.Equal(expected, m) 240 }) 241 } 242 243 func TestReadValue(t *testing.T) { 244 require := require.New(t) 245 246 appDef := testAppDef(t) 247 248 iValueValues := map[string]interface{}{} 249 for k, v := range testData { 250 iValueValues[k] = v 251 } 252 iValueValues[appdef.SystemField_QName] = testQNameView 253 iValueValues["record"] = &TestObject{ 254 Data: testDataSimple, 255 } 256 iValue := &TestValue{ 257 TestObject: &TestObject{ 258 Name: testQNameView, 259 Id: 42, 260 Data: iValueValues, 261 }, 262 } 263 264 t.Run("FieldsToMap", func(t *testing.T) { 265 m := FieldsToMap(iValue, appDef) 266 testBasic(testQNameView, m, require) 267 require.Equal( 268 map[string]interface{}{"int32": int32(42), appdef.SystemField_QName: "test.QNameSimple", appdef.SystemField_Container: ""}, 269 m["record"], 270 ) 271 }) 272 273 t.Run("FieldsToMap non-nils only", func(t *testing.T) { 274 m := FieldsToMap(iValue, appDef, WithNonNilsOnly()) 275 testBasic(testQNameView, m, require) 276 require.Equal( 277 map[string]interface{}{"int32": int32(42), appdef.SystemField_QName: "test.QNameSimple"}, 278 m["record"], 279 ) 280 }) 281 282 t.Run("panic if an object contains DataKind_Record field but is not IValue", func(t *testing.T) { 283 obj := &TestObject{ 284 Name: testQName, 285 Data: iValueValues, 286 } 287 require.Panics(func() { FieldsToMap(obj, appDef) }) 288 require.Panics(func() { FieldsToMap(obj, appDef, WithNonNilsOnly()) }) 289 }) 290 } 291 292 func TestObjectReaderErrors(t *testing.T) { 293 require := require.New(t) 294 require.Panics(func() { ReadByKind("", appdef.DataKind_FakeLast, nil) }) 295 } 296 297 func TestJSONMapToCUDBody(t *testing.T) { 298 t.Run("basic usage", func(t *testing.T) { 299 data := []map[string]interface{}{ 300 { 301 "fld1": "val1", 302 }, 303 { 304 "fld2": "val2", 305 }, 306 } 307 cudBody := JSONMapToCUDBody(data) 308 require.JSONEq(t, `{"cuds":[{"fields":{"fld1":"val1"}},{"fields":{"fld2":"val2"}}]}`, cudBody) 309 }) 310 t.Run("failed to marshel -> panic", func(t *testing.T) { 311 data := []map[string]interface{}{ 312 { 313 "fld1": func() {}, 314 }, 315 } 316 require.Panics(t, func() { JSONMapToCUDBody(data) }) 317 }) 318 }