github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/collections/where_test.go (about)

     1  // Copyright 2017 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  	"fmt"
    18  	"html/template"
    19  	"reflect"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/gohugoio/hugo/common/maps"
    25  
    26  	"github.com/gohugoio/hugo/deps"
    27  )
    28  
    29  func TestWhere(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	ns := New(&deps.Deps{})
    33  
    34  	type Mid struct {
    35  		Tst TstX
    36  	}
    37  
    38  	d1 := time.Now()
    39  	d2 := d1.Add(1 * time.Hour)
    40  	d3 := d2.Add(1 * time.Hour)
    41  	d4 := d3.Add(1 * time.Hour)
    42  	d5 := d4.Add(1 * time.Hour)
    43  	d6 := d5.Add(1 * time.Hour)
    44  
    45  	type testt struct {
    46  		seq    interface{}
    47  		key    interface{}
    48  		op     string
    49  		match  interface{}
    50  		expect interface{}
    51  	}
    52  
    53  	createTestVariants := func(test testt) []testt {
    54  		testVariants := []testt{test}
    55  		if islice := ToTstXIs(test.seq); islice != nil {
    56  			variant := test
    57  			variant.seq = islice
    58  			expect := ToTstXIs(test.expect)
    59  			if expect != nil {
    60  				variant.expect = expect
    61  			}
    62  			testVariants = append(testVariants, variant)
    63  		}
    64  
    65  		return testVariants
    66  	}
    67  
    68  	for i, test := range []testt{
    69  		{
    70  			seq: []map[int]string{
    71  				{1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
    72  			},
    73  			key: 2, match: "m",
    74  			expect: []map[int]string{
    75  				{1: "a", 2: "m"},
    76  			},
    77  		},
    78  		{
    79  			seq: []map[string]int{
    80  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
    81  			},
    82  			key: "b", match: 4,
    83  			expect: []map[string]int{
    84  				{"a": 3, "b": 4},
    85  			},
    86  		},
    87  		{
    88  			seq: []map[string]float64{
    89  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
    90  			},
    91  			key: "b", match: 4.0,
    92  			expect: []map[string]float64{{"a": 3, "b": 4}},
    93  		},
    94  		{
    95  			seq: []map[string]float64{
    96  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
    97  			},
    98  			key: "b", match: 4.0, op: "!=",
    99  			expect: []map[string]float64{{"a": 1, "b": 2}, {"a": 5, "x": 4}},
   100  		},
   101  		{
   102  			seq: []map[string]float64{
   103  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
   104  			},
   105  			key: "b", match: 4.0, op: "<",
   106  			expect: []map[string]float64{{"a": 1, "b": 2}},
   107  		},
   108  		{
   109  			seq: []map[string]float64{
   110  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
   111  			},
   112  			key: "b", match: 4, op: "<",
   113  			expect: []map[string]float64{{"a": 1, "b": 2}},
   114  		},
   115  		{
   116  			seq: []map[string]int{
   117  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
   118  			},
   119  			key: "b", match: 4.0, op: "<",
   120  			expect: []map[string]int{{"a": 1, "b": 2}},
   121  		},
   122  		{
   123  			seq: []map[string]int{
   124  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
   125  			},
   126  			key: "b", match: 4.2, op: "<",
   127  			expect: []map[string]int{{"a": 1, "b": 2}, {"a": 3, "b": 4}},
   128  		},
   129  		{
   130  			seq: []map[string]float64{
   131  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
   132  			},
   133  			key: "b", match: 4.0, op: "<=",
   134  			expect: []map[string]float64{{"a": 1, "b": 2}, {"a": 3, "b": 4}},
   135  		},
   136  		{
   137  			seq: []map[string]float64{
   138  				{"a": 1, "b": 2}, {"a": 3, "b": 3}, {"a": 5, "x": 4},
   139  			},
   140  			key: "b", match: 2.0, op: ">",
   141  			expect: []map[string]float64{{"a": 3, "b": 3}},
   142  		},
   143  		{
   144  			seq: []map[string]float64{
   145  				{"a": 1, "b": 2}, {"a": 3, "b": 3}, {"a": 5, "x": 4},
   146  			},
   147  			key: "b", match: 2.0, op: ">=",
   148  			expect: []map[string]float64{{"a": 1, "b": 2}, {"a": 3, "b": 3}},
   149  		},
   150  		// Issue #8353
   151  		// String type mismatch.
   152  		{
   153  			seq: []map[string]interface{}{
   154  				{"a": "1", "b": "2"}, {"a": "3", "b": template.HTML("4")}, {"a": "5", "x": "4"},
   155  			},
   156  			key: "b", match: "4",
   157  			expect: []map[string]interface{}{
   158  				{"a": "3", "b": template.HTML("4")},
   159  			},
   160  		},
   161  		{
   162  			seq: []TstX{
   163  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   164  			},
   165  			key: "B", match: "f",
   166  			expect: []TstX{
   167  				{A: "e", B: "f"},
   168  			},
   169  		},
   170  		{
   171  			seq: []*map[int]string{
   172  				{1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
   173  			},
   174  			key: 2, match: "m",
   175  			expect: []*map[int]string{
   176  				{1: "a", 2: "m"},
   177  			},
   178  		},
   179  		// Case insensitive maps.Params
   180  		// Slice of structs
   181  		{
   182  			seq: []TstParams{{params: maps.Params{"i": 0, "color": "indigo"}}, {params: maps.Params{"i": 1, "color": "blue"}}, {params: maps.Params{"i": 2, "color": "green"}}, {params: maps.Params{"i": 3, "color": "blue"}}},
   183  			key: ".Params.COLOR", match: "blue",
   184  			expect: []TstParams{{params: maps.Params{"i": 1, "color": "blue"}}, {params: maps.Params{"i": 3, "color": "blue"}}},
   185  		},
   186  		{
   187  			seq: []TstParams{{params: maps.Params{"nested": map[string]interface{}{"color": "indigo"}}}, {params: maps.Params{"nested": map[string]interface{}{"color": "blue"}}}},
   188  			key: ".Params.NEsTED.COLOR", match: "blue",
   189  			expect: []TstParams{{params: maps.Params{"nested": map[string]interface{}{"color": "blue"}}}},
   190  		},
   191  		{
   192  			seq: []TstParams{{params: maps.Params{"i": 0, "color": "indigo"}}, {params: maps.Params{"i": 1, "color": "blue"}}, {params: maps.Params{"i": 2, "color": "green"}}, {params: maps.Params{"i": 3, "color": "blue"}}},
   193  			key: ".Params", match: "blue",
   194  			expect: []TstParams{},
   195  		},
   196  		// Slice of maps
   197  		{
   198  			seq: []maps.Params{
   199  				{"a": "a1", "b": "b1"}, {"a": "a2", "b": "b2"},
   200  			},
   201  			key: "B", match: "b2",
   202  			expect: []maps.Params{
   203  				{"a": "a2", "b": "b2"},
   204  			},
   205  		},
   206  		{
   207  			seq: []maps.Params{
   208  				{
   209  					"a": map[string]interface{}{
   210  						"b": "b1",
   211  					},
   212  				},
   213  				{
   214  					"a": map[string]interface{}{
   215  						"b": "b2",
   216  					},
   217  				},
   218  			},
   219  			key: "A.B", match: "b2",
   220  			expect: []maps.Params{
   221  				{
   222  					"a": map[string]interface{}{
   223  						"b": "b2",
   224  					},
   225  				},
   226  			},
   227  		},
   228  		{
   229  			seq: []*TstX{
   230  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   231  			},
   232  			key: "B", match: "f",
   233  			expect: []*TstX{
   234  				{A: "e", B: "f"},
   235  			},
   236  		},
   237  		{
   238  			seq: []*TstX{
   239  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
   240  			},
   241  			key: "TstRp", match: "rc",
   242  			expect: []*TstX{
   243  				{A: "c", B: "d"},
   244  			},
   245  		},
   246  		{
   247  			seq: []TstX{
   248  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
   249  			},
   250  			key: "TstRv", match: "rc",
   251  			expect: []TstX{
   252  				{A: "e", B: "c"},
   253  			},
   254  		},
   255  		{
   256  			seq: []map[string]TstX{
   257  				{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
   258  			},
   259  			key: "foo.B", match: "d",
   260  			expect: []map[string]TstX{
   261  				{"foo": TstX{A: "c", B: "d"}},
   262  			},
   263  		},
   264  		{
   265  			seq: []map[string]TstX{
   266  				{"baz": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
   267  			},
   268  			key: "foo.B", match: "d",
   269  			expect: []map[string]TstX{
   270  				{"foo": TstX{A: "c", B: "d"}},
   271  			},
   272  		},
   273  		{
   274  			seq: []map[string]TstX{
   275  				{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
   276  			},
   277  			key: ".foo.B", match: "d",
   278  			expect: []map[string]TstX{
   279  				{"foo": TstX{A: "c", B: "d"}},
   280  			},
   281  		},
   282  		{
   283  			seq: []map[string]TstX{
   284  				{"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
   285  			},
   286  			key: "foo.TstRv", match: "rd",
   287  			expect: []map[string]TstX{
   288  				{"foo": TstX{A: "c", B: "d"}},
   289  			},
   290  		},
   291  		{
   292  			seq: []map[string]*TstX{
   293  				{"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}},
   294  			},
   295  			key: "foo.TstRp", match: "rc",
   296  			expect: []map[string]*TstX{
   297  				{"foo": &TstX{A: "c", B: "d"}},
   298  			},
   299  		},
   300  		{
   301  			seq: []TstXIHolder{
   302  				{&TstX{A: "a", B: "b"}}, {&TstX{A: "c", B: "d"}}, {&TstX{A: "e", B: "f"}},
   303  			},
   304  			key: "XI.TstRp", match: "rc",
   305  			expect: []TstXIHolder{
   306  				{&TstX{A: "c", B: "d"}},
   307  			},
   308  		},
   309  		{
   310  			seq: []TstXIHolder{
   311  				{&TstX{A: "a", B: "b"}}, {&TstX{A: "c", B: "d"}}, {&TstX{A: "e", B: "f"}},
   312  			},
   313  			key: "XI.A", match: "e",
   314  			expect: []TstXIHolder{
   315  				{&TstX{A: "e", B: "f"}},
   316  			},
   317  		},
   318  		{
   319  			seq: []map[string]Mid{
   320  				{"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
   321  			},
   322  			key: "foo.Tst.B", match: "d",
   323  			expect: []map[string]Mid{
   324  				{"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
   325  			},
   326  		},
   327  		{
   328  			seq: []map[string]Mid{
   329  				{"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
   330  			},
   331  			key: "foo.Tst.TstRv", match: "rd",
   332  			expect: []map[string]Mid{
   333  				{"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
   334  			},
   335  		},
   336  		{
   337  			seq: []map[string]*Mid{
   338  				{"foo": &Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": &Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": &Mid{Tst: TstX{A: "e", B: "f"}}},
   339  			},
   340  			key: "foo.Tst.TstRp", match: "rc",
   341  			expect: []map[string]*Mid{
   342  				{"foo": &Mid{Tst: TstX{A: "c", B: "d"}}},
   343  			},
   344  		},
   345  		{
   346  			seq: []map[string]int{
   347  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   348  			},
   349  			key: "b", op: ">", match: 3,
   350  			expect: []map[string]int{
   351  				{"a": 3, "b": 4}, {"a": 5, "b": 6},
   352  			},
   353  		},
   354  		{
   355  			seq: []map[string]float64{
   356  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   357  			},
   358  			key: "b", op: ">", match: 3.0,
   359  			expect: []map[string]float64{
   360  				{"a": 3, "b": 4}, {"a": 5, "b": 6},
   361  			},
   362  		},
   363  		{
   364  			seq: []TstX{
   365  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   366  			},
   367  			key: "B", op: "!=", match: "f",
   368  			expect: []TstX{
   369  				{A: "a", B: "b"}, {A: "c", B: "d"},
   370  			},
   371  		},
   372  		{
   373  			seq: []map[string]int{
   374  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   375  			},
   376  			key: "b", op: "in", match: []int{3, 4, 5},
   377  			expect: []map[string]int{
   378  				{"a": 3, "b": 4},
   379  			},
   380  		},
   381  		{
   382  			seq: []map[string]float64{
   383  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   384  			},
   385  			key: "b", op: "in", match: []float64{3, 4, 5},
   386  			expect: []map[string]float64{
   387  				{"a": 3, "b": 4},
   388  			},
   389  		},
   390  		{
   391  			seq: []map[string][]string{
   392  				{"a": []string{"A", "B", "C"}, "b": []string{"D", "E", "F"}}, {"a": []string{"G", "H", "I"}, "b": []string{"J", "K", "L"}}, {"a": []string{"M", "N", "O"}, "b": []string{"P", "Q", "R"}},
   393  			},
   394  			key: "b", op: "intersect", match: []string{"D", "P", "Q"},
   395  			expect: []map[string][]string{
   396  				{"a": []string{"A", "B", "C"}, "b": []string{"D", "E", "F"}}, {"a": []string{"M", "N", "O"}, "b": []string{"P", "Q", "R"}},
   397  			},
   398  		},
   399  		{
   400  			seq: []map[string][]int{
   401  				{"a": []int{1, 2, 3}, "b": []int{4, 5, 6}}, {"a": []int{7, 8, 9}, "b": []int{10, 11, 12}}, {"a": []int{13, 14, 15}, "b": []int{16, 17, 18}},
   402  			},
   403  			key: "b", op: "intersect", match: []int{4, 10, 12},
   404  			expect: []map[string][]int{
   405  				{"a": []int{1, 2, 3}, "b": []int{4, 5, 6}}, {"a": []int{7, 8, 9}, "b": []int{10, 11, 12}},
   406  			},
   407  		},
   408  		{
   409  			seq: []map[string][]int8{
   410  				{"a": []int8{1, 2, 3}, "b": []int8{4, 5, 6}}, {"a": []int8{7, 8, 9}, "b": []int8{10, 11, 12}}, {"a": []int8{13, 14, 15}, "b": []int8{16, 17, 18}},
   411  			},
   412  			key: "b", op: "intersect", match: []int8{4, 10, 12},
   413  			expect: []map[string][]int8{
   414  				{"a": []int8{1, 2, 3}, "b": []int8{4, 5, 6}}, {"a": []int8{7, 8, 9}, "b": []int8{10, 11, 12}},
   415  			},
   416  		},
   417  		{
   418  			seq: []map[string][]int16{
   419  				{"a": []int16{1, 2, 3}, "b": []int16{4, 5, 6}}, {"a": []int16{7, 8, 9}, "b": []int16{10, 11, 12}}, {"a": []int16{13, 14, 15}, "b": []int16{16, 17, 18}},
   420  			},
   421  			key: "b", op: "intersect", match: []int16{4, 10, 12},
   422  			expect: []map[string][]int16{
   423  				{"a": []int16{1, 2, 3}, "b": []int16{4, 5, 6}}, {"a": []int16{7, 8, 9}, "b": []int16{10, 11, 12}},
   424  			},
   425  		},
   426  		{
   427  			seq: []map[string][]int32{
   428  				{"a": []int32{1, 2, 3}, "b": []int32{4, 5, 6}}, {"a": []int32{7, 8, 9}, "b": []int32{10, 11, 12}}, {"a": []int32{13, 14, 15}, "b": []int32{16, 17, 18}},
   429  			},
   430  			key: "b", op: "intersect", match: []int32{4, 10, 12},
   431  			expect: []map[string][]int32{
   432  				{"a": []int32{1, 2, 3}, "b": []int32{4, 5, 6}}, {"a": []int32{7, 8, 9}, "b": []int32{10, 11, 12}},
   433  			},
   434  		},
   435  		{
   436  			seq: []map[string][]int64{
   437  				{"a": []int64{1, 2, 3}, "b": []int64{4, 5, 6}}, {"a": []int64{7, 8, 9}, "b": []int64{10, 11, 12}}, {"a": []int64{13, 14, 15}, "b": []int64{16, 17, 18}},
   438  			},
   439  			key: "b", op: "intersect", match: []int64{4, 10, 12},
   440  			expect: []map[string][]int64{
   441  				{"a": []int64{1, 2, 3}, "b": []int64{4, 5, 6}}, {"a": []int64{7, 8, 9}, "b": []int64{10, 11, 12}},
   442  			},
   443  		},
   444  		{
   445  			seq: []map[string][]float32{
   446  				{"a": []float32{1.0, 2.0, 3.0}, "b": []float32{4.0, 5.0, 6.0}}, {"a": []float32{7.0, 8.0, 9.0}, "b": []float32{10.0, 11.0, 12.0}}, {"a": []float32{13.0, 14.0, 15.0}, "b": []float32{16.0, 17.0, 18.0}},
   447  			},
   448  			key: "b", op: "intersect", match: []float32{4, 10, 12},
   449  			expect: []map[string][]float32{
   450  				{"a": []float32{1.0, 2.0, 3.0}, "b": []float32{4.0, 5.0, 6.0}}, {"a": []float32{7.0, 8.0, 9.0}, "b": []float32{10.0, 11.0, 12.0}},
   451  			},
   452  		},
   453  		{
   454  			seq: []map[string][]float64{
   455  				{"a": []float64{1.0, 2.0, 3.0}, "b": []float64{4.0, 5.0, 6.0}}, {"a": []float64{7.0, 8.0, 9.0}, "b": []float64{10.0, 11.0, 12.0}}, {"a": []float64{13.0, 14.0, 15.0}, "b": []float64{16.0, 17.0, 18.0}},
   456  			},
   457  			key: "b", op: "intersect", match: []float64{4, 10, 12},
   458  			expect: []map[string][]float64{
   459  				{"a": []float64{1.0, 2.0, 3.0}, "b": []float64{4.0, 5.0, 6.0}}, {"a": []float64{7.0, 8.0, 9.0}, "b": []float64{10.0, 11.0, 12.0}},
   460  			},
   461  		},
   462  		{
   463  			seq: []map[string]int{
   464  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   465  			},
   466  			key: "b", op: "in", match: ns.Slice(3, 4, 5),
   467  			expect: []map[string]int{
   468  				{"a": 3, "b": 4},
   469  			},
   470  		},
   471  		{
   472  			seq: []map[string]float64{
   473  				{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
   474  			},
   475  			key: "b", op: "in", match: ns.Slice(3.0, 4.0, 5.0),
   476  			expect: []map[string]float64{
   477  				{"a": 3, "b": 4},
   478  			},
   479  		},
   480  		{
   481  			seq: []map[string]time.Time{
   482  				{"a": d1, "b": d2}, {"a": d3, "b": d4}, {"a": d5, "b": d6},
   483  			},
   484  			key: "b", op: "in", match: ns.Slice(d3, d4, d5),
   485  			expect: []map[string]time.Time{
   486  				{"a": d3, "b": d4},
   487  			},
   488  		},
   489  		{
   490  			seq: []TstX{
   491  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   492  			},
   493  			key: "B", op: "not in", match: []string{"c", "d", "e"},
   494  			expect: []TstX{
   495  				{A: "a", B: "b"}, {A: "e", B: "f"},
   496  			},
   497  		},
   498  		{
   499  			seq: []TstX{
   500  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   501  			},
   502  			key: "B", op: "not in", match: ns.Slice("c", t, "d", "e"),
   503  			expect: []TstX{
   504  				{A: "a", B: "b"}, {A: "e", B: "f"},
   505  			},
   506  		},
   507  		{
   508  			seq: []map[string]int{
   509  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   510  			},
   511  			key: "b", op: "", match: nil,
   512  			expect: []map[string]int{
   513  				{"a": 3},
   514  			},
   515  		},
   516  		{
   517  			seq: []map[string]int{
   518  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   519  			},
   520  			key: "b", op: "!=", match: nil,
   521  			expect: []map[string]int{
   522  				{"a": 1, "b": 2}, {"a": 5, "b": 6},
   523  			},
   524  		},
   525  		{
   526  			seq: []map[string]int{
   527  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   528  			},
   529  			key: "b", op: ">", match: nil,
   530  			expect: []map[string]int{},
   531  		},
   532  		{
   533  			seq: []map[string]float64{
   534  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   535  			},
   536  			key: "b", op: "", match: nil,
   537  			expect: []map[string]float64{
   538  				{"a": 3},
   539  			},
   540  		},
   541  		{
   542  			seq: []map[string]float64{
   543  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   544  			},
   545  			key: "b", op: "!=", match: nil,
   546  			expect: []map[string]float64{
   547  				{"a": 1, "b": 2}, {"a": 5, "b": 6},
   548  			},
   549  		},
   550  		{
   551  			seq: []map[string]float64{
   552  				{"a": 1, "b": 2}, {"a": 3}, {"a": 5, "b": 6},
   553  			},
   554  			key: "b", op: ">", match: nil,
   555  			expect: []map[string]float64{},
   556  		},
   557  		{
   558  			seq: []map[string]bool{
   559  				{"a": true, "b": false}, {"c": true, "b": true}, {"d": true, "b": false},
   560  			},
   561  			key: "b", op: "", match: true,
   562  			expect: []map[string]bool{
   563  				{"c": true, "b": true},
   564  			},
   565  		},
   566  		{
   567  			seq: []map[string]bool{
   568  				{"a": true, "b": false}, {"c": true, "b": true}, {"d": true, "b": false},
   569  			},
   570  			key: "b", op: "!=", match: true,
   571  			expect: []map[string]bool{
   572  				{"a": true, "b": false}, {"d": true, "b": false},
   573  			},
   574  		},
   575  		{
   576  			seq: []map[string]bool{
   577  				{"a": true, "b": false}, {"c": true, "b": true}, {"d": true, "b": false},
   578  			},
   579  			key: "b", op: ">", match: false,
   580  			expect: []map[string]bool{},
   581  		},
   582  		{
   583  			seq: []map[string]bool{
   584  				{"a": true, "b": false}, {"c": true, "b": true}, {"d": true, "b": false},
   585  			},
   586  			key: "b.z", match: false,
   587  			expect: []map[string]bool{},
   588  		},
   589  		{seq: (*[]TstX)(nil), key: "A", match: "a", expect: false},
   590  		{seq: TstX{A: "a", B: "b"}, key: "A", match: "a", expect: false},
   591  		{seq: []map[string]*TstX{{"foo": nil}}, key: "foo.B", match: "d", expect: []map[string]*TstX{}},
   592  		{seq: []map[string]*TstX{{"foo": nil}}, key: "foo.B.Z", match: "d", expect: []map[string]*TstX{}},
   593  		{
   594  			seq: []TstX{
   595  				{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
   596  			},
   597  			key: "B", op: "op", match: "f",
   598  			expect: false,
   599  		},
   600  		{
   601  			seq: map[string]interface{}{
   602  				"foo": []interface{}{map[interface{}]interface{}{"a": 1, "b": 2}},
   603  				"bar": []interface{}{map[interface{}]interface{}{"a": 3, "b": 4}},
   604  				"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
   605  			},
   606  			key: "b", op: "in", match: ns.Slice(3, 4, 5),
   607  			expect: map[string]interface{}{
   608  				"bar": []interface{}{map[interface{}]interface{}{"a": 3, "b": 4}},
   609  			},
   610  		},
   611  		{
   612  			seq: map[string]interface{}{
   613  				"foo": []interface{}{map[interface{}]interface{}{"a": 1, "b": 2}},
   614  				"bar": []interface{}{map[interface{}]interface{}{"a": 3, "b": 4}},
   615  				"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
   616  			},
   617  			key: "b", op: ">", match: 3,
   618  			expect: map[string]interface{}{
   619  				"bar": []interface{}{map[interface{}]interface{}{"a": 3, "b": 4}},
   620  				"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
   621  			},
   622  		},
   623  		{
   624  			seq: map[string]interface{}{
   625  				"foo": []interface{}{maps.Params{"a": 1, "b": 2}},
   626  				"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
   627  				"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
   628  			},
   629  			key: "B", op: ">", match: 3,
   630  			expect: map[string]interface{}{
   631  				"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
   632  				"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
   633  			},
   634  		},
   635  	} {
   636  
   637  		testVariants := createTestVariants(test)
   638  		for j, test := range testVariants {
   639  			name := fmt.Sprintf("%d/%d %T %s %s", i, j, test.seq, test.op, test.key)
   640  			name = strings.ReplaceAll(name, "[]", "slice-of-")
   641  			t.Run(name, func(t *testing.T) {
   642  				var results interface{}
   643  				var err error
   644  
   645  				if len(test.op) > 0 {
   646  					results, err = ns.Where(test.seq, test.key, test.op, test.match)
   647  				} else {
   648  					results, err = ns.Where(test.seq, test.key, test.match)
   649  				}
   650  				if b, ok := test.expect.(bool); ok && !b {
   651  					if err == nil {
   652  						t.Fatalf("[%d] Where didn't return an expected error", i)
   653  					}
   654  				} else {
   655  					if err != nil {
   656  						t.Fatalf("[%d] failed: %s", i, err)
   657  					}
   658  					if !reflect.DeepEqual(results, test.expect) {
   659  						t.Fatalf("Where clause matching %v with %v in seq %v (%T),\ngot\n%v (%T) but expected\n%v (%T)", test.key, test.match, test.seq, test.seq, results, results, test.expect, test.expect)
   660  					}
   661  				}
   662  			})
   663  		}
   664  	}
   665  
   666  	var err error
   667  	_, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1)
   668  	if err == nil {
   669  		t.Errorf("Where called with none string op value didn't return an expected error")
   670  	}
   671  
   672  	_, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1, 2)
   673  	if err == nil {
   674  		t.Errorf("Where called with more than two variable arguments didn't return an expected error")
   675  	}
   676  
   677  	_, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a")
   678  	if err == nil {
   679  		t.Errorf("Where called with no variable arguments didn't return an expected error")
   680  	}
   681  }
   682  
   683  func TestCheckCondition(t *testing.T) {
   684  	t.Parallel()
   685  
   686  	ns := New(&deps.Deps{})
   687  
   688  	type expect struct {
   689  		result  bool
   690  		isError bool
   691  	}
   692  
   693  	for i, test := range []struct {
   694  		value reflect.Value
   695  		match reflect.Value
   696  		op    string
   697  		expect
   698  	}{
   699  		{reflect.ValueOf(123), reflect.ValueOf(123), "", expect{true, false}},
   700  		{reflect.ValueOf("foo"), reflect.ValueOf("foo"), "", expect{true, false}},
   701  		{
   702  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   703  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   704  			"",
   705  			expect{true, false},
   706  		},
   707  		{reflect.ValueOf(true), reflect.ValueOf(true), "", expect{true, false}},
   708  		{reflect.ValueOf(nil), reflect.ValueOf(nil), "", expect{true, false}},
   709  		{reflect.ValueOf(123), reflect.ValueOf(456), "!=", expect{true, false}},
   710  		{reflect.ValueOf("foo"), reflect.ValueOf("bar"), "!=", expect{true, false}},
   711  		{
   712  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   713  			reflect.ValueOf(time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC)),
   714  			"!=",
   715  			expect{true, false},
   716  		},
   717  		{reflect.ValueOf(true), reflect.ValueOf(false), "!=", expect{true, false}},
   718  		{reflect.ValueOf(123), reflect.ValueOf(nil), "!=", expect{true, false}},
   719  		{reflect.ValueOf(456), reflect.ValueOf(123), ">=", expect{true, false}},
   720  		{reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">=", expect{true, false}},
   721  		{
   722  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   723  			reflect.ValueOf(time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC)),
   724  			">=",
   725  			expect{true, false},
   726  		},
   727  		{reflect.ValueOf(456), reflect.ValueOf(123), ">", expect{true, false}},
   728  		{reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">", expect{true, false}},
   729  		{
   730  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   731  			reflect.ValueOf(time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC)),
   732  			">",
   733  			expect{true, false},
   734  		},
   735  		{reflect.ValueOf(123), reflect.ValueOf(456), "<=", expect{true, false}},
   736  		{reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<=", expect{true, false}},
   737  		{
   738  			reflect.ValueOf(time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC)),
   739  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   740  			"<=",
   741  			expect{true, false},
   742  		},
   743  		{reflect.ValueOf(123), reflect.ValueOf(456), "<", expect{true, false}},
   744  		{reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<", expect{true, false}},
   745  		{
   746  			reflect.ValueOf(time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC)),
   747  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   748  			"<",
   749  			expect{true, false},
   750  		},
   751  		{reflect.ValueOf(123), reflect.ValueOf([]int{123, 45, 678}), "in", expect{true, false}},
   752  		{reflect.ValueOf("foo"), reflect.ValueOf([]string{"foo", "bar", "baz"}), "in", expect{true, false}},
   753  		{
   754  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   755  			reflect.ValueOf([]time.Time{
   756  				time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC),
   757  				time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC),
   758  				time.Date(2015, time.June, 26, 19, 18, 56, 12345, time.UTC),
   759  			}),
   760  			"in",
   761  			expect{true, false},
   762  		},
   763  		{reflect.ValueOf(123), reflect.ValueOf([]int{45, 678}), "not in", expect{true, false}},
   764  		{reflect.ValueOf("foo"), reflect.ValueOf([]string{"bar", "baz"}), "not in", expect{true, false}},
   765  		{
   766  			reflect.ValueOf(time.Date(2015, time.May, 26, 19, 18, 56, 12345, time.UTC)),
   767  			reflect.ValueOf([]time.Time{
   768  				time.Date(2015, time.February, 26, 19, 18, 56, 12345, time.UTC),
   769  				time.Date(2015, time.March, 26, 19, 18, 56, 12345, time.UTC),
   770  				time.Date(2015, time.April, 26, 19, 18, 56, 12345, time.UTC),
   771  			}),
   772  			"not in",
   773  			expect{true, false},
   774  		},
   775  		{reflect.ValueOf("foo"), reflect.ValueOf("bar-foo-baz"), "in", expect{true, false}},
   776  		{reflect.ValueOf("foo"), reflect.ValueOf("bar--baz"), "not in", expect{true, false}},
   777  		{reflect.Value{}, reflect.ValueOf("foo"), "", expect{false, false}},
   778  		{reflect.ValueOf("foo"), reflect.Value{}, "", expect{false, false}},
   779  		{reflect.ValueOf((*TstX)(nil)), reflect.ValueOf("foo"), "", expect{false, false}},
   780  		{reflect.ValueOf("foo"), reflect.ValueOf((*TstX)(nil)), "", expect{false, false}},
   781  		{reflect.ValueOf(true), reflect.ValueOf("foo"), "", expect{false, false}},
   782  		{reflect.ValueOf("foo"), reflect.ValueOf(true), "", expect{false, false}},
   783  		{reflect.ValueOf("foo"), reflect.ValueOf(map[int]string{}), "", expect{false, false}},
   784  		{reflect.ValueOf("foo"), reflect.ValueOf([]int{1, 2}), "", expect{false, false}},
   785  		{reflect.ValueOf((*TstX)(nil)), reflect.ValueOf((*TstX)(nil)), ">", expect{false, false}},
   786  		{reflect.ValueOf(true), reflect.ValueOf(false), ">", expect{false, false}},
   787  		{reflect.ValueOf(123), reflect.ValueOf([]int{}), "in", expect{false, false}},
   788  		{reflect.ValueOf(123), reflect.ValueOf(123), "op", expect{false, true}},
   789  
   790  		// Issue #3718
   791  		{reflect.ValueOf([]interface{}{"a"}), reflect.ValueOf([]string{"a", "b"}), "intersect", expect{true, false}},
   792  		{reflect.ValueOf([]string{"a"}), reflect.ValueOf([]interface{}{"a", "b"}), "intersect", expect{true, false}},
   793  		{reflect.ValueOf([]interface{}{1, 2}), reflect.ValueOf([]int{1}), "intersect", expect{true, false}},
   794  		{reflect.ValueOf([]int{1}), reflect.ValueOf([]interface{}{1, 2}), "intersect", expect{true, false}},
   795  	} {
   796  		result, err := ns.checkCondition(test.value, test.match, test.op)
   797  		if test.expect.isError {
   798  			if err == nil {
   799  				t.Errorf("[%d] checkCondition didn't return an expected error", i)
   800  			}
   801  		} else {
   802  			if err != nil {
   803  				t.Errorf("[%d] failed: %s", i, err)
   804  				continue
   805  			}
   806  			if result != test.expect.result {
   807  				t.Errorf("[%d] check condition %v %s %v, got %v but expected %v", i, test.value, test.op, test.match, result, test.expect.result)
   808  			}
   809  		}
   810  	}
   811  }
   812  
   813  func TestEvaluateSubElem(t *testing.T) {
   814  	t.Parallel()
   815  	tstx := TstX{A: "foo", B: "bar"}
   816  	var inner struct {
   817  		S fmt.Stringer
   818  	}
   819  	inner.S = tstx
   820  	interfaceValue := reflect.ValueOf(&inner).Elem().Field(0)
   821  
   822  	for i, test := range []struct {
   823  		value  reflect.Value
   824  		key    string
   825  		expect interface{}
   826  	}{
   827  		{reflect.ValueOf(tstx), "A", "foo"},
   828  		{reflect.ValueOf(&tstx), "TstRp", "rfoo"},
   829  		{reflect.ValueOf(tstx), "TstRv", "rbar"},
   830  		//{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1, "foo"},
   831  		{reflect.ValueOf(map[string]string{"key1": "foo", "key2": "bar"}), "key1", "foo"},
   832  		{interfaceValue, "String", "A: foo, B: bar"},
   833  		{reflect.Value{}, "foo", false},
   834  		//{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1.2, false},
   835  		{reflect.ValueOf(tstx), "unexported", false},
   836  		{reflect.ValueOf(tstx), "unexportedMethod", false},
   837  		{reflect.ValueOf(tstx), "MethodWithArg", false},
   838  		{reflect.ValueOf(tstx), "MethodReturnNothing", false},
   839  		{reflect.ValueOf(tstx), "MethodReturnErrorOnly", false},
   840  		{reflect.ValueOf(tstx), "MethodReturnTwoValues", false},
   841  		{reflect.ValueOf(tstx), "MethodReturnValueWithError", false},
   842  		{reflect.ValueOf((*TstX)(nil)), "A", false},
   843  		{reflect.ValueOf(tstx), "C", false},
   844  		{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), "1", false},
   845  		{reflect.ValueOf([]string{"foo", "bar"}), "1", false},
   846  	} {
   847  		result, err := evaluateSubElem(test.value, test.key)
   848  		if b, ok := test.expect.(bool); ok && !b {
   849  			if err == nil {
   850  				t.Errorf("[%d] evaluateSubElem didn't return an expected error", i)
   851  			}
   852  		} else {
   853  			if err != nil {
   854  				t.Errorf("[%d] failed: %s", i, err)
   855  				continue
   856  			}
   857  			if result.Kind() != reflect.String || result.String() != test.expect {
   858  				t.Errorf("[%d] evaluateSubElem with %v got %v but expected %v", i, test.key, result, test.expect)
   859  			}
   860  		}
   861  	}
   862  }