github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/collections/merge_test.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     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  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package collections
    15  
    16  import (
    17  	"bytes"
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/gohugoio/hugo/common/maps"
    22  	"github.com/gohugoio/hugo/config"
    23  	"github.com/gohugoio/hugo/deps"
    24  	"github.com/gohugoio/hugo/langs"
    25  	"github.com/gohugoio/hugo/parser"
    26  	"github.com/gohugoio/hugo/parser/metadecoders"
    27  
    28  	qt "github.com/frankban/quicktest"
    29  )
    30  
    31  func TestMerge(t *testing.T) {
    32  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
    33  
    34  	simpleMap := map[string]any{"a": 1, "b": 2}
    35  
    36  	for i, test := range []struct {
    37  		name   string
    38  		params []any
    39  		expect any
    40  		isErr  bool
    41  	}{
    42  		{
    43  			"basic",
    44  			[]any{
    45  				map[string]any{"a": 42, "c": 3},
    46  				map[string]any{"a": 1, "b": 2},
    47  			},
    48  			map[string]any{"a": 1, "b": 2, "c": 3},
    49  			false,
    50  		},
    51  		{
    52  			"multi",
    53  			[]any{
    54  				map[string]any{"a": 42, "c": 3, "e": 11},
    55  				map[string]any{"a": 1, "b": 2},
    56  				map[string]any{"a": 9, "c": 4, "d": 7},
    57  			},
    58  			map[string]any{"a": 9, "b": 2, "c": 4, "d": 7, "e": 11},
    59  			false,
    60  		},
    61  		{
    62  			"basic case insensitive",
    63  			[]any{
    64  				map[string]any{"A": 42, "c": 3},
    65  				map[string]any{"a": 1, "b": 2},
    66  			},
    67  			map[string]any{"a": 1, "b": 2, "c": 3},
    68  			false,
    69  		},
    70  		{
    71  			"nested",
    72  			[]any{
    73  				map[string]any{"a": 42, "c": 3, "b": map[string]any{"d": 55, "e": 66, "f": 3}},
    74  				map[string]any{"a": 1, "b": map[string]any{"d": 1, "e": 2}},
    75  			},
    76  			map[string]any{"a": 1, "b": map[string]any{"d": 1, "e": 2, "f": 3}, "c": 3},
    77  			false,
    78  		},
    79  		{
    80  			// https://github.com/gohugoio/hugo/issues/6633
    81  			"params dst",
    82  			[]any{
    83  				map[string]any{"a": 42, "c": 3},
    84  				maps.Params{"a": 1, "b": 2},
    85  			},
    86  			maps.Params{"a": int(1), "b": int(2), "c": int(3)},
    87  			false,
    88  		},
    89  		{
    90  			"params dst, upper case src",
    91  			[]any{
    92  				map[string]any{"a": 42, "C": 3},
    93  				maps.Params{"a": 1, "b": 2},
    94  			},
    95  			maps.Params{"a": int(1), "b": int(2), "c": int(3)},
    96  			false,
    97  		},
    98  		{
    99  			"params src",
   100  			[]any{
   101  				maps.Params{"a": 42, "c": 3},
   102  				map[string]any{"a": 1, "c": 2},
   103  			},
   104  			map[string]any{"a": int(1), "c": int(2)},
   105  			false,
   106  		},
   107  		{
   108  			"params src, upper case dst",
   109  			[]any{
   110  				maps.Params{"a": 42, "c": 3},
   111  				map[string]any{"a": 1, "C": 2},
   112  			},
   113  			map[string]any{"a": int(1), "C": int(2)},
   114  			false,
   115  		},
   116  		{
   117  			"nested, params dst",
   118  			[]any{
   119  				map[string]any{"a": 42, "c": 3, "b": map[string]any{"d": 55, "e": 66, "f": 3}},
   120  				maps.Params{"a": 1, "b": maps.Params{"d": 1, "e": 2}},
   121  			},
   122  			maps.Params{"a": 1, "b": maps.Params{"d": 1, "e": 2, "f": 3}, "c": 3},
   123  			false,
   124  		},
   125  		{
   126  			// https://github.com/gohugoio/hugo/issues/7899
   127  			"matching keys with non-map src value",
   128  			[]any{
   129  				map[string]any{"k": "v"},
   130  				map[string]any{"k": map[string]any{"k2": "v2"}},
   131  			},
   132  			map[string]any{"k": map[string]any{"k2": "v2"}},
   133  			false,
   134  		},
   135  		{"src nil", []any{nil, simpleMap}, simpleMap, false},
   136  		// Error cases.
   137  		{"dst not a map", []any{nil, "not a map"}, nil, true},
   138  		{"src not a map", []any{"not a map", simpleMap}, nil, true},
   139  		{"different map types", []any{map[int]any{32: "a"}, simpleMap}, nil, true},
   140  		{"all nil", []any{nil, nil}, nil, true},
   141  	} {
   142  
   143  		test := test
   144  		i := i
   145  
   146  		t.Run(test.name, func(t *testing.T) {
   147  			t.Parallel()
   148  			errMsg := qt.Commentf("[%d] %v", i, test)
   149  
   150  			c := qt.New(t)
   151  
   152  			result, err := ns.Merge(test.params...)
   153  
   154  			if test.isErr {
   155  				c.Assert(err, qt.Not(qt.IsNil), errMsg)
   156  				return
   157  			}
   158  
   159  			c.Assert(err, qt.IsNil)
   160  			c.Assert(result, qt.DeepEquals, test.expect, errMsg)
   161  		})
   162  	}
   163  }
   164  
   165  func TestMergeDataFormats(t *testing.T) {
   166  	c := qt.New(t)
   167  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   168  
   169  	toml1 := `
   170  V1 = "v1_1"
   171  
   172  [V2s]
   173  V21 = "v21_1"
   174  
   175  `
   176  
   177  	toml2 := `
   178  V1 = "v1_2"
   179  V2 = "v2_2"
   180  
   181  [V2s]
   182  V21 = "v21_2"
   183  V22 = "v22_2"
   184  
   185  `
   186  
   187  	meta1, err := metadecoders.Default.UnmarshalToMap([]byte(toml1), metadecoders.TOML)
   188  	c.Assert(err, qt.IsNil)
   189  	meta2, err := metadecoders.Default.UnmarshalToMap([]byte(toml2), metadecoders.TOML)
   190  	c.Assert(err, qt.IsNil)
   191  
   192  	for _, format := range []metadecoders.Format{metadecoders.JSON, metadecoders.YAML, metadecoders.TOML} {
   193  
   194  		var dataStr1, dataStr2 bytes.Buffer
   195  		err = parser.InterfaceToConfig(meta1, format, &dataStr1)
   196  		c.Assert(err, qt.IsNil)
   197  		err = parser.InterfaceToConfig(meta2, format, &dataStr2)
   198  		c.Assert(err, qt.IsNil)
   199  
   200  		dst, err := metadecoders.Default.UnmarshalToMap(dataStr1.Bytes(), format)
   201  		c.Assert(err, qt.IsNil)
   202  		src, err := metadecoders.Default.UnmarshalToMap(dataStr2.Bytes(), format)
   203  		c.Assert(err, qt.IsNil)
   204  
   205  		merged, err := ns.Merge(src, dst)
   206  		c.Assert(err, qt.IsNil)
   207  
   208  		c.Assert(
   209  			merged,
   210  			qt.DeepEquals,
   211  			map[string]any{
   212  				"V1": "v1_1", "V2": "v2_2",
   213  				"V2s": map[string]any{"V21": "v21_1", "V22": "v22_2"},
   214  			})
   215  	}
   216  }
   217  
   218  func TestCaseInsensitiveMapLookup(t *testing.T) {
   219  	c := qt.New(t)
   220  
   221  	m1 := reflect.ValueOf(map[string]any{
   222  		"a": 1,
   223  		"B": 2,
   224  	})
   225  
   226  	m2 := reflect.ValueOf(map[int]any{
   227  		1: 1,
   228  		2: 2,
   229  	})
   230  
   231  	var found bool
   232  
   233  	a, found := caseInsensitiveLookup(m1, reflect.ValueOf("A"))
   234  	c.Assert(found, qt.Equals, true)
   235  	c.Assert(a.Interface(), qt.Equals, 1)
   236  
   237  	b, found := caseInsensitiveLookup(m1, reflect.ValueOf("b"))
   238  	c.Assert(found, qt.Equals, true)
   239  	c.Assert(b.Interface(), qt.Equals, 2)
   240  
   241  	two, found := caseInsensitiveLookup(m2, reflect.ValueOf(2))
   242  	c.Assert(found, qt.Equals, true)
   243  	c.Assert(two.Interface(), qt.Equals, 2)
   244  }