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

     1  // Copyright 2023 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  	"strings"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/require"
    22  	"gopkg.in/src-d/go-errors.v1"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  	"github.com/dolthub/go-mysql-server/sql/types"
    26  )
    27  
    28  func TestReplace(t *testing.T) {
    29  
    30  	_, err := NewJSONReplace()
    31  	require.True(t, errors.Is(err, sql.ErrInvalidArgumentNumber))
    32  
    33  	f1 := buildGetFieldExpressions(t, NewJSONReplace, 3)
    34  	f2 := buildGetFieldExpressions(t, NewJSONReplace, 5)
    35  
    36  	json := `{"a": 1, "b": [2, 3], "c": {"d": "foo"}}`
    37  
    38  	testCases := []struct {
    39  		f        sql.Expression
    40  		row      sql.Row
    41  		expected interface{}
    42  		err      error
    43  	}{
    44  		{f1, sql.Row{json, "$.a", 10.1}, `{"a": 10.1, "b": [2, 3], "c": {"d": "foo"}}`, nil},               // replace existing
    45  		{f1, sql.Row{json, "$.e", "new"}, json, nil},                                                       // replace non-existing does nothing
    46  		{f1, sql.Row{json, "$.c.d", "test"}, `{"a": 1, "b": [2, 3], "c": {"d": "test"}}`, nil},             // replace nested
    47  		{f2, sql.Row{json, "$.a", 10.1, "$.e", "new"}, `{"a": 10.1, "b": [2, 3], "c": {"d": "foo"}}`, nil}, // replace multiple, one change.
    48  		{f1, sql.Row{json, "$.a.e", "test"}, json, nil},                                                    // replace nested non-existent does nothing
    49  		{f1, sql.Row{json, "$.c.e", "test"}, json, nil},                                                    // replace nested in existing struct missing field does nothing
    50  		{f1, sql.Row{json, "$.c[5]", 4.1}, json, nil},                                                      // replace struct with indexing out of range
    51  		{f1, sql.Row{json, "$.b[0]", 4.1}, `{"a": 1, "b": [4.1, 3], "c": {"d": "foo"}}`, nil},              // replace element in array
    52  		{f1, sql.Row{json, "$.b[5]", 4.1}, json, nil},                                                      // replace element in array out of range does nothing
    53  		{f1, sql.Row{json, "$.b.c", 4}, json, nil},                                                         // replace nested in array does nothing
    54  		{f1, sql.Row{json, "$.a[0]", 4.1}, `{"a": 4.1, "b": [2, 3], "c": {"d": "foo"}}`, nil},              // replace scalar when treated as array
    55  		{f1, sql.Row{json, "$[0]", 4.1}, `4.1`, nil},                                                       // replace root element when treated as array
    56  		{f1, sql.Row{json, "$.[0]", 4.1}, nil, ErrInvalidPath},                                             // improper struct indexing
    57  		{f1, sql.Row{json, "foo", "test"}, nil, ErrInvalidPath},                                            // invalid path
    58  		{f1, sql.Row{json, "$.c.*", "test"}, nil, ErrPathWildcard},                                         // path contains * wildcard
    59  		{f1, sql.Row{json, "$.c.**", "test"}, nil, ErrPathWildcard},                                        // path contains ** wildcard
    60  		{f1, sql.Row{json, "$", 10.1}, `10.1`, nil},                                                        // replace root element
    61  		{f1, sql.Row{nil, "$", 42.7}, nil, nil},                                                            // null document returns null
    62  		{f1, sql.Row{json, nil, 10}, nil, nil},                                                             // if any path is null, return null
    63  	}
    64  
    65  	for _, tstC := range testCases {
    66  		var paths []string
    67  		for _, path := range tstC.row[1:] {
    68  			if _, ok := path.(string); ok {
    69  				paths = append(paths, path.(string))
    70  			}
    71  		}
    72  
    73  		t.Run(tstC.f.String()+"."+strings.Join(paths, ","), func(t *testing.T) {
    74  			req := require.New(t)
    75  			result, err := tstC.f.Eval(sql.NewEmptyContext(), tstC.row)
    76  			if tstC.err == nil {
    77  				req.NoError(err)
    78  
    79  				var expect interface{}
    80  				if tstC.expected != nil {
    81  					expect, _, err = types.JSON.Convert(tstC.expected)
    82  					if err != nil {
    83  						panic("Bad test string. Can't convert string to JSONDocument: " + tstC.expected.(string))
    84  					}
    85  				}
    86  
    87  				req.Equal(expect, result)
    88  			} else {
    89  				req.Error(tstC.err, err)
    90  			}
    91  		})
    92  	}
    93  }