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