github.com/matrixorigin/matrixone@v1.2.0/pkg/container/bytejson/bytejson_test.go (about)

     1  // Copyright 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bytejson
    16  
    17  import (
    18  	"strconv"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestLiteral(t *testing.T) {
    25  	j := []string{"true", "false", "null"}
    26  	for _, x := range j {
    27  		bj, err := ParseFromString(x)
    28  		require.Nil(t, err)
    29  		require.Equal(t, x, bj.String())
    30  	}
    31  }
    32  
    33  func TestNumber(t *testing.T) {
    34  	// generate max int64
    35  	j := []string{
    36  		"9223372036854775807",
    37  		"-9223372036854775808",
    38  		"1",
    39  		"-1",
    40  	}
    41  	for _, x := range j {
    42  		bj, err := ParseFromString(x)
    43  		require.Nil(t, err)
    44  		// transform string to int64
    45  		now, err := strconv.ParseInt(x, 10, 64)
    46  		require.Nil(t, err)
    47  		require.Equal(t, now, bj.GetInt64())
    48  	}
    49  
    50  	// generate max uint64
    51  	j = []string{
    52  		"18446744073709551615",
    53  		"0",
    54  		"1",
    55  	}
    56  	for _, x := range j {
    57  		bj, err := ParseFromString(x)
    58  		require.Nil(t, err)
    59  		// transform string to uint64
    60  		now, err := strconv.ParseUint(x, 10, 64)
    61  		require.Nil(t, err)
    62  		require.Equal(t, now, bj.GetUint64())
    63  	}
    64  
    65  	//generate max float64
    66  	j = []string{
    67  		"1.7976931348623157e+308",
    68  		"-1.7976931348623157e+308",
    69  		"4.940656458412465441765687928682213723651e-324",
    70  		"0.112131431",
    71  		"1.13353411",
    72  	}
    73  	for _, x := range j {
    74  		bj, err := ParseFromString(x)
    75  		require.Nil(t, err)
    76  		// transform string to float64
    77  		now, err := strconv.ParseFloat(x, 64)
    78  		require.Nil(t, err)
    79  		require.Equal(t, now, bj.GetFloat64())
    80  	}
    81  }
    82  
    83  func TestObject(t *testing.T) {
    84  	j := []string{
    85  		`{"a":1}`,
    86  		`{"a": 1, "b": 2, "c": true, "d": false, "e": null, "f": "string", "g": [1, 2, 3], "h": {"a": 1, "b": 2}, "i": 1.1, "j": 1.1e+10, "k": 1.1e-10}`,
    87  		`{"a":{}}`,
    88  		`{"a":{"b":{"c":{"d":[null,false,true,123,"abc",[1,2,3],{"a":1,"b":2,"c":3,"d":4,"e":5},123.456]}}}}`,
    89  	}
    90  	for _, x := range j {
    91  		bj, err := ParseFromString(x)
    92  		require.Nil(t, err)
    93  		require.JSONEq(t, x, bj.String())
    94  	}
    95  }
    96  func TestArray(t *testing.T) {
    97  	j := []string{
    98  		`[`,
    99  		`[{]`,
   100  		`[{}]`,
   101  		`["1"]`,
   102  		`{"k1": "value", "k2": [10, 20]}`,
   103  		`[null,false,true,123,"abc",[1,2,3],{"a":1,"b":2,"c":3,"d":4,"e":5},123.456,1.1e+10,1.1e-10]`,
   104  	}
   105  	for i, x := range j {
   106  		bj, err := ParseFromString(x)
   107  		if i > 1 {
   108  			require.Nil(t, err)
   109  			require.JSONEq(t, x, bj.String())
   110  		} else {
   111  			require.NotNil(t, err)
   112  		}
   113  	}
   114  }
   115  
   116  func TestQuery(t *testing.T) {
   117  	kases := []struct {
   118  		jsonStr string
   119  		pathStr string
   120  		outStr  string
   121  	}{
   122  		{
   123  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   124  			pathStr: "$.a",
   125  			outStr:  "\"1\"",
   126  		},
   127  		{
   128  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   129  			pathStr: "$.b",
   130  			outStr:  "\"2\"",
   131  		},
   132  		{
   133  			jsonStr: `[1,2,3]`,
   134  			pathStr: "$[0]",
   135  			outStr:  "1",
   136  		},
   137  		{
   138  			jsonStr: `[1,2,3]`,
   139  			pathStr: "$[2]",
   140  			outStr:  "3",
   141  		},
   142  		{
   143  			jsonStr: `[1,2,3]`,
   144  			pathStr: "$[*]",
   145  			outStr:  "[1,2,3]",
   146  		},
   147  		{
   148  			jsonStr: `{"a":[1,2,3,{"b":4}]}`,
   149  			pathStr: "$.a[3].b",
   150  			outStr:  "4",
   151  		},
   152  		{
   153  			jsonStr: `{"a":[1,2,3,{"b":4}]}`,
   154  			pathStr: "$.a[3].c",
   155  			outStr:  "null",
   156  		},
   157  		{
   158  			jsonStr: `{"a":[1,2,3,{"b":4}],"c":5}`,
   159  			pathStr: "$.*",
   160  			outStr:  `[[1,2,3,{"b":4}],5]`,
   161  		},
   162  		{
   163  			jsonStr: `{"a":[1,2,3,{"a":4}]}`,
   164  			pathStr: "$**.a",
   165  			outStr:  `[[1,2,3,{"a":4}],4]`,
   166  		},
   167  		{
   168  			jsonStr: `{"a":1}`,
   169  			pathStr: "$[0]",
   170  			outStr:  `{"a":1}`,
   171  		},
   172  		{
   173  			jsonStr: `{"a":1}`,
   174  			pathStr: "$[0].a",
   175  			outStr:  `1`,
   176  		},
   177  		{
   178  			jsonStr: `{"a":1}`,
   179  			pathStr: "$[1]",
   180  			outStr:  `null`,
   181  		},
   182  	}
   183  	for _, kase := range kases {
   184  		bj, err := ParseFromString(kase.jsonStr)
   185  		require.Nil(t, err)
   186  		path, err := ParseJsonPath(kase.pathStr)
   187  		require.Nil(t, err)
   188  		out := bj.Query([]*Path{&path})
   189  		require.JSONEq(t, kase.outStr, out.String())
   190  	}
   191  }
   192  func TestUnnest(t *testing.T) {
   193  	kases := []struct {
   194  		jsonStr   string
   195  		pathStr   string
   196  		mode      string
   197  		recursive bool
   198  		outer     bool
   199  		outStr    []string
   200  		valid     bool
   201  	}{
   202  		{
   203  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   204  			mode:    "other",
   205  			valid:   false,
   206  		},
   207  		{
   208  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   209  			mode:    "both",
   210  			pathStr: "$",
   211  			outStr: []string{
   212  				`key: a, path: $.a, value: "1", this: {"a": "1", "b": "2", "c": "3"}`,
   213  				`key: b, path: $.b, value: "2", this: {"a": "1", "b": "2", "c": "3"}`,
   214  				`key: c, path: $.c, value: "3", this: {"a": "1", "b": "2", "c": "3"}`,
   215  			},
   216  			valid: true,
   217  		},
   218  		{
   219  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   220  			pathStr: "$.a",
   221  			mode:    "both",
   222  			valid:   true,
   223  		},
   224  		{
   225  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   226  			mode:    "object",
   227  			outStr: []string{
   228  				`key: a, path: $.a, value: "1", this: {"a": "1", "b": "2", "c": "3"}`,
   229  				`key: b, path: $.b, value: "2", this: {"a": "1", "b": "2", "c": "3"}`,
   230  				`key: c, path: $.c, value: "3", this: {"a": "1", "b": "2", "c": "3"}`,
   231  			},
   232  			valid: true,
   233  		},
   234  		{
   235  			jsonStr: `{"a": "1", "b": "2", "c": "3"}`,
   236  			mode:    "array",
   237  			valid:   true,
   238  		},
   239  		{
   240  			jsonStr: `[1,2,3]`,
   241  			mode:    "array",
   242  			outStr: []string{
   243  				`path: $[0], index: 0, value: 1, this: [1, 2, 3]`,
   244  				`path: $[1], index: 1, value: 2, this: [1, 2, 3]`,
   245  				`path: $[2], index: 2, value: 3, this: [1, 2, 3]`,
   246  			},
   247  			valid: true,
   248  		},
   249  		{
   250  			jsonStr: `[1,2,3]`,
   251  			mode:    "object",
   252  			valid:   true,
   253  		},
   254  		{
   255  			jsonStr: `[1,2,3]`,
   256  			mode:    "both",
   257  			outStr: []string{
   258  				`path: $[0], index: 0, value: 1, this: [1, 2, 3]`,
   259  				`path: $[1], index: 1, value: 2, this: [1, 2, 3]`,
   260  				`path: $[2], index: 2, value: 3, this: [1, 2, 3]`,
   261  			},
   262  			valid: true,
   263  		},
   264  		{
   265  			jsonStr: `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   266  			mode:    "both",
   267  			outStr: []string{
   268  				`key: a, path: $.a, value: [1, 2, 3], this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   269  				`key: b, path: $.b, value: {"c": 4, "d": [5, 6, 7]}, this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   270  			},
   271  			valid: true,
   272  		},
   273  		{
   274  			jsonStr: `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   275  			mode:    "object",
   276  			outStr: []string{
   277  				`key: a, path: $.a, value: [1, 2, 3], this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   278  				`key: b, path: $.b, value: {"c": 4, "d": [5, 6, 7]}, this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   279  			},
   280  			valid: true,
   281  		},
   282  		{
   283  			jsonStr: `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   284  			mode:    "array",
   285  			outer:   true,
   286  			outStr: []string{
   287  				`path: $, this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   288  			},
   289  			valid: true,
   290  		},
   291  		{
   292  			jsonStr:   `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   293  			mode:      "both",
   294  			recursive: true,
   295  			outStr: []string{
   296  				`key: a, path: $.a, value: [1, 2, 3], this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   297  				`path: $.a[0], index: 0, value: 1, this: [1, 2, 3]`,
   298  				`path: $.a[1], index: 1, value: 2, this: [1, 2, 3]`,
   299  				`path: $.a[2], index: 2, value: 3, this: [1, 2, 3]`,
   300  				`key: b, path: $.b, value: {"c": 4, "d": [5, 6, 7]}, this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   301  				`key: c, path: $.b.c, value: 4, this: {"c": 4, "d": [5, 6, 7]}`,
   302  				`key: d, path: $.b.d, value: [5, 6, 7], this: {"c": 4, "d": [5, 6, 7]}`,
   303  				`path: $.b.d[0], index: 0, value: 5, this: [5, 6, 7]`,
   304  				`path: $.b.d[1], index: 1, value: 6, this: [5, 6, 7]`,
   305  				`path: $.b.d[2], index: 2, value: 7, this: [5, 6, 7]`,
   306  			},
   307  			valid: true,
   308  		},
   309  		{
   310  			jsonStr:   `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   311  			mode:      "object",
   312  			recursive: true,
   313  			outStr: []string{
   314  				`key: a, path: $.a, value: [1, 2, 3], this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   315  				`key: b, path: $.b, value: {"c": 4, "d": [5, 6, 7]}, this: {"a": [1, 2, 3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   316  				`key: c, path: $.b.c, value: 4, this: {"c": 4, "d": [5, 6, 7]}`,
   317  				`key: d, path: $.b.d, value: [5, 6, 7], this: {"c": 4, "d": [5, 6, 7]}`,
   318  			},
   319  			valid: true,
   320  		},
   321  		{
   322  			jsonStr:   `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   323  			mode:      "array",
   324  			recursive: true,
   325  			pathStr:   "$.a",
   326  			outStr: []string{
   327  				`path: $.a[0], index: 0, value: 1, this: [1, 2, 3]`,
   328  				`path: $.a[1], index: 1, value: 2, this: [1, 2, 3]`,
   329  				`path: $.a[2], index: 2, value: 3, this: [1, 2, 3]`,
   330  			},
   331  			valid: true,
   332  		},
   333  		{
   334  			jsonStr: `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   335  			mode:    "array",
   336  			pathStr: "$.b",
   337  			valid:   true,
   338  			outer:   true,
   339  			outStr: []string{
   340  				`path: $.b, this: {"c": 4, "d": [5, 6, 7]}`,
   341  			},
   342  		},
   343  		{
   344  			jsonStr: `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   345  			mode:    "array",
   346  			pathStr: "$.b.d",
   347  			outStr: []string{
   348  				`path: $.b.d[0], index: 0, value: 5, this: [5, 6, 7]`,
   349  				`path: $.b.d[1], index: 1, value: 6, this: [5, 6, 7]`,
   350  				`path: $.b.d[2], index: 2, value: 7, this: [5, 6, 7]`,
   351  			},
   352  			valid: true,
   353  		},
   354  		{
   355  			jsonStr:   `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   356  			mode:      "object",
   357  			pathStr:   "$.b",
   358  			recursive: true,
   359  			outStr: []string{
   360  				`key: c, path: $.b.c, value: 4, this: {"c": 4, "d": [5, 6, 7]}`,
   361  				`key: d, path: $.b.d, value: [5, 6, 7], this: {"c": 4, "d": [5, 6, 7]}`,
   362  			},
   363  			valid: true,
   364  		},
   365  		{
   366  			jsonStr:   `{"a": [1,2,3], "b": {"c": 4, "d": [5, 6, 7]}}`,
   367  			mode:      "both",
   368  			pathStr:   "$.*",
   369  			recursive: true,
   370  			outStr: []string{
   371  				`path: $.a[0], index: 0, value: 1, this: [1, 2, 3]`,
   372  				`path: $.a[1], index: 1, value: 2, this: [1, 2, 3]`,
   373  				`path: $.a[2], index: 2, value: 3, this: [1, 2, 3]`,
   374  				`key: c, path: $.b.c, value: 4, this: {"c": 4, "d": [5, 6, 7]}`,
   375  				`key: d, path: $.b.d, value: [5, 6, 7], this: {"c": 4, "d": [5, 6, 7]}`,
   376  				`path: $.b.d[0], index: 0, value: 5, this: [5, 6, 7]`,
   377  				`path: $.b.d[1], index: 1, value: 6, this: [5, 6, 7]`,
   378  				`path: $.b.d[2], index: 2, value: 7, this: [5, 6, 7]`,
   379  			},
   380  			valid: true,
   381  		},
   382  		{
   383  			jsonStr: `{"a": [1,2,3], "b": {"a": {"b": 1}, "c": 4, "d": [5, 6, 7]}}`,
   384  			mode:    "object",
   385  			pathStr: "$.a**.a",
   386  			outStr: []string{
   387  				`key: b, path: $.a[0].a.b, value: 1, this: {"b": 1}`,
   388  			},
   389  			valid: true,
   390  		},
   391  		{
   392  			jsonStr: `{"a": [1,2,3,{"b":4}], "b": {"a": {"b": 1}, "c": 4, "d": [5, 6, 7]}}`,
   393  			mode:    "both",
   394  			pathStr: "$**.a",
   395  			outStr: []string{
   396  				`path: $.a[0], index: 0, value: 1, this: [1, 2, 3, {"b": 4}]`,
   397  				`path: $.a[1], index: 1, value: 2, this: [1, 2, 3, {"b": 4}]`,
   398  				`path: $.a[2], index: 2, value: 3, this: [1, 2, 3, {"b": 4}]`,
   399  				`path: $.a[3], index: 3, value: {"b": 4}, this: [1, 2, 3, {"b": 4}]`,
   400  				`key: b, path: $.b.a.b, value: 1, this: {"b": 1}`,
   401  			},
   402  			valid: true,
   403  		},
   404  		{
   405  			jsonStr:   `{"a": [1,2,3,{"b":4}], "b": {"a": {"b": 1}, "c": 4, "d": [5, 6, 7]}}`,
   406  			mode:      "both",
   407  			pathStr:   "$**.a",
   408  			recursive: true,
   409  			outStr: []string{
   410  				`path: $.a[0], index: 0, value: 1, this: [1, 2, 3, {"b": 4}]`,
   411  				`path: $.a[1], index: 1, value: 2, this: [1, 2, 3, {"b": 4}]`,
   412  				`path: $.a[2], index: 2, value: 3, this: [1, 2, 3, {"b": 4}]`,
   413  				`path: $.a[3], index: 3, value: {"b": 4}, this: [1, 2, 3, {"b": 4}]`,
   414  				`key: b, path: $.a[3].b, value: 4, this: {"b": 4}`,
   415  				`key: b, path: $.b.a.b, value: 1, this: {"b": 1}`,
   416  			},
   417  			valid: true,
   418  		},
   419  	}
   420  	filterMap := map[string]struct{}{
   421  		"index": {},
   422  		"this":  {},
   423  		"value": {},
   424  		"path":  {},
   425  		"key":   {},
   426  	}
   427  	for _, kase := range kases {
   428  		bj, err := ParseFromString(kase.jsonStr)
   429  		require.Nil(t, err)
   430  		var path Path
   431  		if len(kase.pathStr) > 0 {
   432  			path, err = ParseJsonPath(kase.pathStr)
   433  			require.Nil(t, err)
   434  		}
   435  		out, err := bj.Unnest(&path, kase.outer, kase.recursive, kase.mode, filterMap)
   436  		if !kase.valid {
   437  			require.NotNil(t, err)
   438  			continue
   439  		}
   440  		require.Nil(t, err)
   441  		for i, o := range out {
   442  			require.Equal(t, kase.outStr[i], o.String())
   443  		}
   444  	}
   445  
   446  }
   447  
   448  func TestByteJson_Unquote(t *testing.T) {
   449  	kases := []struct {
   450  		jsonStr string
   451  		outStr  string
   452  		valid   bool
   453  	}{
   454  		{
   455  			jsonStr: `"a"`,
   456  			outStr:  "a",
   457  			valid:   true,
   458  		},
   459  		{
   460  			jsonStr: `"a\"b"`,
   461  			outStr:  `a"b`,
   462  			valid:   true,
   463  		},
   464  		{
   465  			jsonStr: `"a\b"`,
   466  			outStr:  "a\b",
   467  			valid:   true,
   468  		},
   469  		{
   470  			jsonStr: `"a\r"`,
   471  			outStr:  "a\r",
   472  			valid:   true,
   473  		},
   474  		{
   475  			jsonStr: `"a\t"`,
   476  			outStr:  `a	`,
   477  			valid:   true,
   478  		},
   479  		{
   480  			jsonStr: `"a\n"`,
   481  			outStr: `a
   482  `,
   483  			valid: true,
   484  		},
   485  		{
   486  			jsonStr: `"\u554a\u554a\u5361\u5361"`,
   487  			outStr:  `啊啊卡卡`,
   488  			valid:   true,
   489  		},
   490  		{
   491  			jsonStr: `"\u4f60\u597d\uff0c\u006d\u006f"`,
   492  			outStr:  `你好,mo`,
   493  			valid:   true,
   494  		},
   495  		{
   496  			jsonStr: `"\u4f60\u597d\uff0cmo"`,
   497  			outStr:  `你好,mo`,
   498  			valid:   true,
   499  		},
   500  		{
   501  			jsonStr: `"\u4f60\u597d\ufc"`,
   502  			valid:   false,
   503  		},
   504  	}
   505  	for _, kase := range kases {
   506  		bj, err := ParseFromString(kase.jsonStr)
   507  		if !kase.valid {
   508  			require.NotNil(t, err)
   509  			continue
   510  		}
   511  		require.Nil(t, err)
   512  		out, err := bj.Unquote()
   513  		require.Nil(t, err)
   514  		require.Equal(t, kase.outStr, out)
   515  	}
   516  }