github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_contains_path_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 TestJSONContainsPath(t *testing.T) {
    29  	// Verify arg count 3 or more.
    30  	_, err := NewJSONContainsPath()
    31  	require.Error(t, err)
    32  
    33  	_, err = NewJSONContainsPath(
    34  		expression.NewGetField(0, types.JSON, "arg1", false),
    35  	)
    36  	require.Error(t, err)
    37  
    38  	_, err = NewJSONContainsPath(
    39  		expression.NewGetField(0, types.JSON, "arg1", false),
    40  		expression.NewGetField(1, types.LongText, "arg2", false),
    41  	)
    42  	require.Error(t, err)
    43  
    44  	// setup call expressions for calling with 1, 2, and 3 paths.
    45  	onePath, err := NewJSONContainsPath(
    46  		expression.NewGetField(0, types.JSON, "arg1", false),
    47  		expression.NewGetField(1, types.LongText, "arg2", false),
    48  		expression.NewGetField(2, types.LongText, "arg3", false),
    49  	)
    50  	require.NoError(t, err)
    51  
    52  	twoPath, err := NewJSONContainsPath(
    53  		expression.NewGetField(0, types.JSON, "arg1", false),
    54  		expression.NewGetField(1, types.LongText, "arg2", false),
    55  		expression.NewGetField(2, types.LongText, "arg3", false),
    56  		expression.NewGetField(3, types.LongText, "arg4", false),
    57  	)
    58  	require.NoError(t, err)
    59  
    60  	threePath, err := NewJSONContainsPath(
    61  		expression.NewGetField(0, types.JSON, "arg1", false),
    62  		expression.NewGetField(1, types.LongText, "arg2", false),
    63  		expression.NewGetField(2, types.LongText, "arg3", false),
    64  		expression.NewGetField(3, types.LongText, "arg4", false),
    65  		expression.NewGetField(4, types.LongText, "arg5", false),
    66  	)
    67  	require.NoError(t, err)
    68  
    69  	testCases := []struct {
    70  		fCall    sql.Expression
    71  		input    sql.Row
    72  		expected interface{}
    73  		err      error
    74  	}{
    75  		{onePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `oNe`, `$.a`}, true, nil},
    76  		{onePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `one`, `$.e`}, false, nil},
    77  		{onePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `all`, `$.e`}, false, nil},
    78  		{onePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `All`, `$.c.d`}, true, nil},
    79  
    80  		{twoPath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `one`, `$.a`, `$.e`}, true, nil},
    81  		{twoPath, sql.Row{`{"a": 1, "b": 2, "c": {"d": 4}}`, `ALL`, `$.a`, `$.e`}, false, nil},
    82  
    83  		{twoPath, sql.Row{`{"a": 1, "b": 2, "c": {"d": {"e" : 42}}}`, `all`, `$.a`, `$.c.d.e`}, true, nil},
    84  		{threePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": {"e" : 42}}}`, `all`, `$.a`, `$.c.d.e`, `$.x`}, false, nil},
    85  		{threePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": {"e" : 42}}}`, `one`, `$.a`, `$.c.d.e`, `$.x`}, true, nil},
    86  
    87  		// NULL inputs. Any NULL should result in NULL output.
    88  		{onePath, sql.Row{nil, `one`, `$.a`}, nil, nil},
    89  		{onePath, sql.Row{`{"a": 1}`, nil, `$.a`}, nil, nil},
    90  		{twoPath, sql.Row{`{"a": 1}`, `one`, `$.a`, nil}, true, nil}, // Match MySQL behavior, not docs.
    91  		{twoPath, sql.Row{`{"a": 1}`, `one`, nil, `$.a`}, nil, nil},
    92  		{twoPath, sql.Row{`{"a": 1}`, "all", `$.x`, nil}, false, nil}, // Match MySQL behavior, not docs.
    93  		{twoPath, sql.Row{`{"a": 1}`, `all`, `$.a`, nil}, nil, nil},
    94  
    95  		// Error cases
    96  		{onePath, sql.Row{`{"a": 1}`, `None`, `$.a`}, nil, errors.New("The oneOrAll argument to json_contains_path may take these values: 'one' or 'all'")},
    97  		{onePath, sql.Row{`{"a": 1`, `One`, `$.a`}, nil, errors.New(`Invalid JSON text: {"a": 1`)},
    98  		{threePath, sql.Row{`{"a": 1, "b": 2, "c": {"d": {"e" : 42}}}`, `one`, 42, `$.c.d.e`, `$.x`}, nil, errors.New(`Invalid JSON path expression. Path must start with '$', but received: '42'`)},
    99  	}
   100  
   101  	for _, testcase := range testCases {
   102  		t.Run(testcase.fCall.String(), func(t *testing.T) {
   103  			require := require.New(t)
   104  			result, err := testcase.fCall.Eval(sql.NewEmptyContext(), testcase.input)
   105  			if testcase.err == nil {
   106  				require.NoError(err)
   107  			} else {
   108  				require.Equal(err.Error(), testcase.err.Error())
   109  			}
   110  
   111  			require.Equal(testcase.expected, result)
   112  		})
   113  	}
   114  }