github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_contains_test.go (about)

     1  // Copyright 2021 Dolthub, Inc.
     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 json
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/pkg/errors"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/dolthub/go-mysql-server/sql"
    24  	"github.com/dolthub/go-mysql-server/sql/expression"
    25  	"github.com/dolthub/go-mysql-server/sql/types"
    26  )
    27  
    28  func TestJSONContains(t *testing.T) {
    29  	// Quickly assert that an error is thrown with < 2 and > 3 arguments
    30  	_, err := NewJSONContains(
    31  		expression.NewGetField(0, types.JSON, "arg1", false),
    32  	)
    33  	require.Error(t, err)
    34  
    35  	_, err = NewJSONContains(
    36  		expression.NewGetField(0, types.JSON, "arg1", false),
    37  		expression.NewGetField(1, types.JSON, "arg2", false),
    38  		expression.NewGetField(2, types.LongText, "arg3", false),
    39  		expression.NewGetField(3, types.LongText, "arg4", false),
    40  	)
    41  	require.Error(t, err)
    42  
    43  	f, err := NewJSONContains(
    44  		expression.NewGetField(0, types.JSON, "arg1", false),
    45  		expression.NewGetField(1, types.JSON, "arg2", false),
    46  		expression.NewGetField(2, types.LongText, "arg3", false),
    47  	)
    48  	require.NoError(t, err)
    49  
    50  	f2, err := NewJSONContains(
    51  		expression.NewGetField(0, types.JSON, "arg1", false),
    52  		expression.NewGetField(1, types.JSON, "arg2", false),
    53  	)
    54  	require.NoError(t, err)
    55  
    56  	json, _, err := types.JSON.Convert(`{` +
    57  		`"a": [1, 2, 3, 4], ` +
    58  		`"b": {"c": "foo", "d": true}, ` +
    59  		`"e": [[1, 2], [3, 4]] ` +
    60  		`}`)
    61  	require.NoError(t, err)
    62  
    63  	badMap, _, err := types.JSON.Convert(`{"x": [[1, 2], [3, 4]]}`)
    64  	require.NoError(t, err)
    65  
    66  	goodMap, _, err := types.JSON.Convert(`{"e": [[1, 2], [3, 4]]}`)
    67  	require.NoError(t, err)
    68  
    69  	testCases := []struct {
    70  		f        sql.Expression
    71  		row      sql.Row
    72  		expected interface{}
    73  		err      error
    74  	}{
    75  		// JSON Array Tests
    76  		{f2, sql.Row{`[1, [1, 2, 3], 10]`, `[1, 10]`}, true, nil},
    77  		{f2, sql.Row{`[1, [1, 2, 3, 10]]`, `[1, 10]`}, true, nil},
    78  		{f2, sql.Row{`[1, [1, 2, 3], [10]]`, `[1, [10]]`}, true, nil},
    79  		{f2, sql.Row{`[1, [1, 2, 3], [10]]`, `1`}, true, nil},
    80  		{f2, sql.Row{`[1, [1, 2, 3], [10], {"e": 1, "f": 2}]`, `{"e": 1}`}, true, nil},
    81  		{f2, sql.Row{`[1, [1, 2, 3], [10], {"e": [6, 7], "f": 2}]`, `[6, 7]`}, false, nil},
    82  
    83  		// JSON Object Tests
    84  		{f2, sql.Row{`{"b": {"a": [1, 2, 3]}}`, `{"a": [1]}`}, false, nil},
    85  		{f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": [1]}`}, true, nil},
    86  		{f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": []}`}, true, nil},
    87  		{f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": {}}`}, false, nil},
    88  		{f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `{"a": [2, 4]}`}, true, nil},
    89  		{f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `[2]`}, false, nil},
    90  		{f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `2`}, false, nil},
    91  		{f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `"foo"`}, false, nil},
    92  		{f2, sql.Row{"{\"a\": {\"foo\": [1, 2, 3]}}", "{\"a\": {\"foo\": [1]}}"}, true, nil},
    93  		{f2, sql.Row{"{\"a\": {\"foo\": [1, 2, 3]}}", "{\"foo\": [1]}"}, false, nil},
    94  
    95  		// Path Tests
    96  		{f, sql.Row{json, json, "FOO"}, nil, errors.New("Invalid JSON path expression. Path must start with '$', but received: 'FOO'")},
    97  		{f, sql.Row{1, nil, "$.a"}, nil, errors.New("Invalid argument to 1")},
    98  		{f, sql.Row{json, 2, "$.e[0][*]"}, nil, errors.New("Invalid argument to 2")},
    99  		{f, sql.Row{nil, json, "$.b.c"}, nil, nil},
   100  		{f, sql.Row{json, nil, "$.b.c"}, nil, nil},
   101  		{f, sql.Row{json, json, "$.foo"}, nil, nil},
   102  		{f, sql.Row{json, `"foo"`, "$.b.c"}, true, nil},
   103  		{f, sql.Row{json, `1`, "$.e[0][0]"}, true, nil},
   104  		{f, sql.Row{json, `1`, "$.e[0][*]"}, true, nil},
   105  		{f, sql.Row{json, `1`, "$.e[0][0]"}, true, nil},
   106  		{f, sql.Row{json, `[1, 2]`, "$.e[0][*]"}, true, nil},
   107  		{f, sql.Row{json, `[1, 2]`, "$.e[0]"}, true, nil},
   108  		{f, sql.Row{json, json, "$"}, true, nil},       // reflexivity
   109  		{f, sql.Row{json, goodMap, "$.e"}, false, nil}, // The path statement selects an array, which does not contain goodMap
   110  		{f, sql.Row{json, badMap, "$"}, false, nil},    // false due to key name difference
   111  		{f, sql.Row{json, goodMap, "$"}, true, nil},
   112  
   113  		// Miscellaneous Tests
   114  		{f2, sql.Row{json, `[1, 2]`}, false, nil}, // When testing containment against a map, scalars and arrays always return false
   115  		{f2, sql.Row{"[1,2,3,4]", `[1, 2]`}, true, nil},
   116  		{f2, sql.Row{"[1,2,3,4]", `1`}, true, nil},
   117  		{f2, sql.Row{`["apple", "orange", "banana"]`, `"orange"`}, true, nil},
   118  		{f2, sql.Row{`"hello"`, `"hello"`}, true, nil},
   119  		{f2, sql.Row{"{}", "{}"}, true, nil},
   120  		{f2, sql.Row{"hello", "hello"}, nil, sql.ErrInvalidJSONText.New("hello")},
   121  		{f2, sql.Row{"[1,2", "[1]"}, nil, sql.ErrInvalidJSONText.New("[1,2")},
   122  		{f2, sql.Row{"[1,2]", "[1"}, nil, sql.ErrInvalidJSONText.New("[1")},
   123  	}
   124  
   125  	for _, tt := range testCases {
   126  		t.Run(tt.f.String(), func(t *testing.T) {
   127  			require := require.New(t)
   128  			result, err := tt.f.Eval(sql.NewEmptyContext(), tt.row)
   129  			if tt.err == nil {
   130  				require.NoError(err)
   131  			} else {
   132  				require.Equal(err.Error(), tt.err.Error())
   133  			}
   134  
   135  			require.Equal(tt.expected, result)
   136  		})
   137  	}
   138  }