github.com/RevenueMonster/sqlike@v1.0.6/jsonb/marshal_test.go (about)

     1  package jsonb
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/RevenueMonster/sqlike/types"
    11  	"github.com/google/uuid"
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/tidwall/sjson"
    14  	"golang.org/x/text/language"
    15  )
    16  
    17  type longStr string
    18  
    19  type Decimal float64
    20  
    21  func (f Decimal) MarshalJSONB() ([]byte, error) {
    22  	return []byte(fmt.Sprintf(`"%.2f"`, f)), nil
    23  }
    24  
    25  type Boolean bool
    26  
    27  func (b Boolean) MarshalJSON() ([]byte, error) {
    28  	if b {
    29  		return []byte(`"Yes"`), nil
    30  	}
    31  	return []byte(`"No"`), nil
    32  }
    33  
    34  type Text string
    35  
    36  func (txt Text) MarshalText() ([]byte, error) {
    37  	return []byte(txt), nil
    38  }
    39  
    40  type normalStruct struct {
    41  	Str               string
    42  	UUID              uuid.UUID
    43  	Text              Text
    44  	DecimalStr        Decimal
    45  	NullDecimalStr    *Decimal `sqlike:"NullableDecimal"`
    46  	Boolean           Boolean
    47  	LongStr           string
    48  	CustomStrType     longStr
    49  	EmptyByte         []byte
    50  	Byte              []byte
    51  	Bool              bool
    52  	priv              int
    53  	Skip              interface{}
    54  	Int               int
    55  	TinyInt           int8
    56  	SmallInt          int16
    57  	MediumInt         int32
    58  	BigInt            int64
    59  	Uint              uint
    60  	TinyUint          uint8
    61  	SmallUint         uint16
    62  	MediumUint        uint32
    63  	BigUint           uint64
    64  	Float32           float32
    65  	Float64           float64
    66  	UFloat32          float32
    67  	EmptyArray        []string
    68  	EmptyMap          map[string]interface{}
    69  	EmptyStruct       struct{}
    70  	JSONRaw           json.RawMessage
    71  	Timestamp         time.Time
    72  	NullStr           *string
    73  	NullCustomStrType *longStr
    74  	NullInt           *int
    75  	NullTinyInt       *int8
    76  	NullSmallInt      *int16
    77  	NullMediumInt     *int32
    78  	NullBigInt        *int64
    79  	NullFloat32       *float32
    80  	NullFloat64       *float64
    81  	MultiPtr          *****int
    82  	NullKey           *types.Key
    83  }
    84  
    85  var (
    86  	nsPtr  *normalStruct
    87  	ns     normalStruct
    88  	nsInit = new(normalStruct)
    89  )
    90  
    91  func TestMarshal(t *testing.T) {
    92  	var (
    93  		b   []byte
    94  		err error
    95  		i   normalStruct
    96  		i32 = int(888)
    97  		k   = types.NameKey("Name", "@#$%^&*()ashdkjashd", types.NewIDKey("ID", nil))
    98  	)
    99  
   100  	data := `{"Str":"","UUID":"00000000-0000-0000-0000-000000000000","Text":"","DecimalStr":"0.00","NullableDecimal":null,`
   101  	data += `"Boolean":"No","LongStr":"","CustomStrType":"",`
   102  	data += `"EmptyByte":null,"Byte":null,"Bool":false,"Skip":null,`
   103  	data += `"Int":0,"TinyInt":0,"SmallInt":0,"MediumInt":0,"BigInt":0,`
   104  	data += `"Uint":0,"TinyUint":0,"SmallUint":0,"MediumUint":0,"BigUint":0,`
   105  	data += `"Float32":0,"Float64":0,"UFloat32":0,`
   106  	data += `"EmptyArray":null,"EmptyMap":null,"EmptyStruct":{},"JSONRaw":null,"Timestamp":"0001-01-01T00:00:00Z",`
   107  	data += `"NullStr":null,"NullCustomStrType":null,`
   108  	data += `"NullInt":null,"NullTinyInt":null,"NullSmallInt":null,"NullMediumInt":null,"NullBigInt":null,`
   109  	data += `"NullFloat32":null,"NullFloat64":null,`
   110  	data += `"MultiPtr":null,"NullKey":null}`
   111  	dataByte := []byte(data)
   112  
   113  	// Marshal nil
   114  	{
   115  		b, err = Marshal(nsPtr)
   116  		require.NoError(t, err)
   117  		require.Equal(t, []byte(`null`), b)
   118  
   119  		b, err = Marshal(nil)
   120  		require.NoError(t, err)
   121  		require.Equal(t, []byte(`null`), b)
   122  	}
   123  
   124  	// Marshal initialized struct
   125  	{
   126  		b, err = Marshal(nsInit)
   127  		require.NoError(t, err)
   128  		require.Equal(t, dataByte, b)
   129  	}
   130  
   131  	var (
   132  		symbolStr     = `'ajhdjasd12380912$%^&*()_\\"asdasd123910293"""\\\\123210312930-\\`
   133  		jsonEscapeStr = `"'ajhdjasd12380912$%^&*()_\\\\\"asdasd123910293\"\"\"\\\\\\\\123210312930-\\\\"`
   134  		bytes         = []byte(`abcd1234`)
   135  		// result        = []byte(`{"Str":"hello world","DecimalStr":"10.69","NullableDecimal":null,"Boolean":"Yes","LongStr":"` + jsonEscapeStr + `","CustomStrType":"","EmptyByte":"YWJjZDEyMzQ=","Byte":null,"Bool":false,"Skip":null,"Int":0,"TinyInt":0,"SmallInt":0,"MediumInt":0,"BigInt":0,"Uint":0,"TinyUint":0,"SmallUint":0,"MediumUint":0,"BigUint":0,"Float32":0,"Float64":0,"UFloat32":0,"EmptyStruct":{},"JSONRaw":null,"Timestamp":"0001-01-01T00:00:00Z","NullStr":"` + jsonEscapeStr + `","NullCustomStrType":null,"NullInt":888,"NullTinyInt":null,"NullSmallInt":null,"NullMediumInt":null,"NullBigInt":null,"NullFloat32":null,"NullFloat64":null,"MultiPtr":null,"NullKey":"` + k.String() + `"}`)
   136  	)
   137  
   138  	// Marshal struct with pointer value
   139  	{
   140  		i.Text = `"My long text.......""`
   141  		i.Str = "hello world"
   142  		i.priv = 100
   143  		i.LongStr = symbolStr
   144  		i.Boolean = true
   145  		i.DecimalStr = 10.688
   146  		i.EmptyByte = bytes
   147  		i.EmptyArray = make([]string, 0)
   148  		i.EmptyMap = make(map[string]interface{})
   149  		i.NullStr = &symbolStr
   150  		i.NullInt = &i32
   151  		i.NullKey = k
   152  
   153  		dataByte, _ = sjson.SetBytes(dataByte, "Str", "hello world")
   154  		dataByte, _ = sjson.SetBytes(dataByte, "Text", `"My long text.......""`)
   155  		dataByte, _ = sjson.SetRawBytes(dataByte, "LongStr", []byte(jsonEscapeStr))
   156  		dataByte, _ = sjson.SetBytes(dataByte, "Boolean", "Yes")
   157  		dataByte, _ = sjson.SetBytes(dataByte, "DecimalStr", "10.69")
   158  		dataByte, _ = sjson.SetBytes(dataByte, "EmptyByte", base64.StdEncoding.EncodeToString(bytes))
   159  		dataByte, _ = sjson.SetBytes(dataByte, "EmptyArray", make([]string, 0))
   160  		dataByte, _ = sjson.SetBytes(dataByte, "EmptyMap", make(map[string]interface{}))
   161  		dataByte, _ = sjson.SetRawBytes(dataByte, "NullStr", []byte(jsonEscapeStr))
   162  		dataByte, _ = sjson.SetBytes(dataByte, "NullInt", i32)
   163  		dataByte, _ = sjson.SetBytes(dataByte, "NullKey", k.String())
   164  
   165  		b, err = Marshal(i)
   166  		require.NoError(t, err)
   167  		require.Equal(t, dataByte, b)
   168  	}
   169  
   170  	t.Run("Test Map's Key with Int, Uint data type", func(it *testing.T) {
   171  		var (
   172  			intMap map[int]string
   173  			b      []byte
   174  			err    error
   175  		)
   176  
   177  		b, err = Marshal(intMap)
   178  		require.NoError(t, err)
   179  		require.Equal(t, []byte(`null`), b)
   180  
   181  		intMap = make(map[int]string)
   182  		intMap[0] = "hello"
   183  		intMap[100] = "🤖🤖"
   184  		intMap[-1] = "negative"
   185  
   186  		b, err = Marshal(intMap)
   187  		require.NoError(t, err)
   188  		require.Equal(t, []byte(`{"-1":"negative","0":"hello","100":"🤖🤖"}`), b)
   189  
   190  		var outMap map[int]string
   191  		err = Unmarshal(b, &outMap)
   192  		require.NoError(t, err)
   193  		require.Equal(t, "hello", outMap[0])
   194  		require.Equal(t, "🤖🤖", outMap[100])
   195  		require.Equal(t, "negative", outMap[-1])
   196  
   197  		var (
   198  			uint8Map map[uint8]string
   199  		)
   200  
   201  		b, err = Marshal(uint8Map)
   202  		require.NoError(t, err)
   203  		require.Equal(t, []byte(`null`), b)
   204  
   205  		uint8Map = make(map[uint8]string)
   206  		uint8Map[0] = "zero (\"initial value\")"
   207  		uint8Map[100] = "🤖🤖"
   208  		uint8Map[88] = "Long sentences here .............."
   209  		b, err = Marshal(uint8Map)
   210  		require.NoError(t, err)
   211  		require.Equal(t, []byte(`{"0":"zero (\"initial value\")","88":"Long sentences here ..............","100":"🤖🤖"}`), b)
   212  
   213  		var uoutMap map[uint8]string
   214  		err = Unmarshal(b, &uoutMap)
   215  		require.NoError(t, err)
   216  		require.Equal(t, `zero ("initial value")`, uoutMap[0])
   217  		require.Equal(t, "🤖🤖", uoutMap[100])
   218  		require.Equal(t, "Long sentences here ..............", uoutMap[88])
   219  	})
   220  
   221  	t.Run("Test Map's Key with data type implements `MarshalText`", func(it *testing.T) {
   222  		langMap := make(map[language.Tag]string)
   223  		langMap[language.English] = "english"
   224  		langMap[language.Japanese] = "japanese"
   225  		langMap[language.Korean] = "korean"
   226  		langMap[language.Arabic] = "arabic"
   227  		langMap[language.Chinese] = "chinese"
   228  
   229  		b, err := Marshal(langMap)
   230  		require.NoError(t, err)
   231  
   232  		outMap := make(map[language.Tag]string)
   233  		err = Unmarshal(b, &outMap)
   234  		require.NoError(t, err)
   235  		require.Equal(t, langMap[language.English], outMap[language.English])
   236  		require.Equal(t, langMap[language.Japanese], outMap[language.Japanese])
   237  		require.Equal(t, langMap[language.Korean], outMap[language.Korean])
   238  		require.Equal(t, langMap[language.Arabic], outMap[language.Arabic])
   239  		require.Equal(t, langMap[language.Chinese], outMap[language.Chinese])
   240  	})
   241  }
   242  
   243  func BenchmarkJSONMarshal(b *testing.B) {
   244  	var err error
   245  	b.Run("Pointer Struct w/o initialize", func(t *testing.B) {
   246  		for n := 0; n < t.N; n++ {
   247  			_, err = json.Marshal(nsPtr)
   248  			require.NoError(t, err)
   249  		}
   250  	})
   251  
   252  	b.Run("Pointer Struct w initialize", func(t *testing.B) {
   253  		for n := 0; n < t.N; n++ {
   254  			_, err = json.Marshal(nsInit)
   255  			require.NoError(t, err)
   256  		}
   257  	})
   258  
   259  	b.Run("Struct w initialize", func(t *testing.B) {
   260  		for n := 0; n < t.N; n++ {
   261  			_, err = json.Marshal(nsPtr)
   262  			require.NoError(t, err)
   263  		}
   264  	})
   265  }
   266  
   267  func BenchmarkJSONBMarshal(b *testing.B) {
   268  	var err error
   269  	b.Run("Pointer Struct w/o initialize", func(t *testing.B) {
   270  		for n := 0; n < t.N; n++ {
   271  			_, err = Marshal(nsPtr)
   272  			require.NoError(t, err)
   273  		}
   274  	})
   275  
   276  	b.Run("Pointer Struct w initialize", func(t *testing.B) {
   277  		for n := 0; n < t.N; n++ {
   278  			_, err = Marshal(nsInit)
   279  			require.NoError(t, err)
   280  		}
   281  	})
   282  
   283  	b.Run("Struct w initialize", func(t *testing.B) {
   284  		for n := 0; n < t.N; n++ {
   285  			_, err = Marshal(nsPtr)
   286  			require.NoError(t, err)
   287  		}
   288  	})
   289  }