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  }