github.com/CiscoM31/godata@v1.0.10/filter_parser_test.go (about)

     1  package godata
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func TestFilterDateTime(t *testing.T) {
    10  	ctx := context.Background()
    11  	tokenizer := NewExpressionTokenizer()
    12  	tokens := map[string]TokenType{
    13  		"2011-08-29T21:58Z":             ExpressionTokenDateTime,
    14  		"2011-08-29T21:58:33Z":          ExpressionTokenDateTime,
    15  		"2011-08-29T21:58:33.123Z":      ExpressionTokenDateTime,
    16  		"2011-08-29T21:58+11:23":        ExpressionTokenDateTime,
    17  		"2011-08-29T21:58:33+11:23":     ExpressionTokenDateTime,
    18  		"2011-08-29T21:58:33.123+11:23": ExpressionTokenDateTime,
    19  		"2011-08-29T21:58:33-11:23":     ExpressionTokenDateTime,
    20  		"2011-08-29":                    ExpressionTokenDate,
    21  		"21:58:33":                      ExpressionTokenTime,
    22  	}
    23  	for tokenValue, tokenType := range tokens {
    24  		// Previously, the unit test had no space character after 'gt'
    25  		// E.g. 'CreateTime gt2011-08-29T21:58Z' was considered valid.
    26  		// However the ABNF notation for ODATA logical operators is:
    27  		//   gtExpr = RWS "gt" RWS commonExpr
    28  		//   RWS = 1*( SP / HTAB / "%20" / "%09" )  ; "required" whitespace
    29  		//
    30  		// See http://docs.oasis-open.org/odata/odata/v4.01/csprd03/abnf/odata-abnf-construction-rules.txt
    31  		input := "CreateTime gt " + tokenValue
    32  		expect := []*Token{
    33  			{Value: "CreateTime", Type: ExpressionTokenLiteral},
    34  			{Value: "gt", Type: ExpressionTokenLogical},
    35  			{Value: tokenValue, Type: tokenType},
    36  		}
    37  		output, err := tokenizer.Tokenize(ctx, input)
    38  		if err != nil {
    39  			t.Errorf("Failed to tokenize input %s. Error: %v", input, err)
    40  		}
    41  
    42  		result, err := CompareTokens(expect, output)
    43  		if !result {
    44  			var a []string
    45  			for _, t := range output {
    46  				a = append(a, t.Value)
    47  			}
    48  			t.Errorf("Unexpected tokens for input '%s'. Tokens: %s Error: %v", input, strings.Join(a, ", "), err)
    49  		}
    50  	}
    51  }
    52  
    53  func TestFilterAnyArrayOfObjects(t *testing.T) {
    54  	ctx := context.Background()
    55  	tokenizer := NewExpressionTokenizer()
    56  	input := "Tags/any(d:d/Key eq 'Site' and d/Value lt 10)"
    57  	expect := []*Token{
    58  		{Value: "Tags", Type: ExpressionTokenLiteral},
    59  		{Value: "/", Type: ExpressionTokenLambdaNav},
    60  		{Value: "any", Type: ExpressionTokenLambda},
    61  		{Value: "(", Type: ExpressionTokenOpenParen},
    62  		{Value: "d", Type: ExpressionTokenLiteral},
    63  		{Value: ",", Type: ExpressionTokenColon}, // ':' is replaced by ',' which is the function argument separator.
    64  		{Value: "d", Type: ExpressionTokenLiteral},
    65  		{Value: "/", Type: ExpressionTokenNav},
    66  		{Value: "Key", Type: ExpressionTokenLiteral},
    67  		{Value: "eq", Type: ExpressionTokenLogical},
    68  		{Value: "'Site'", Type: ExpressionTokenString},
    69  		{Value: "and", Type: ExpressionTokenLogical},
    70  		{Value: "d", Type: ExpressionTokenLiteral},
    71  		{Value: "/", Type: ExpressionTokenNav},
    72  		{Value: "Value", Type: ExpressionTokenLiteral},
    73  		{Value: "lt", Type: ExpressionTokenLogical},
    74  		{Value: "10", Type: ExpressionTokenInteger},
    75  		{Value: ")", Type: ExpressionTokenCloseParen},
    76  	}
    77  	output, err := tokenizer.Tokenize(ctx, input)
    78  	if err != nil {
    79  		t.Error(err)
    80  	}
    81  
    82  	result, err := CompareTokens(expect, output)
    83  	if !result {
    84  		t.Error(err)
    85  	}
    86  }
    87  
    88  func TestFilterAnyArrayOfPrimitiveTypes(t *testing.T) {
    89  	ctx := context.Background()
    90  	tokenizer := NewExpressionTokenizer()
    91  	input := "Tags/any(d:d eq 'Site')"
    92  	{
    93  		expect := []*Token{
    94  			{Value: "Tags", Type: ExpressionTokenLiteral},
    95  			{Value: "/", Type: ExpressionTokenLambdaNav},
    96  			{Value: "any", Type: ExpressionTokenLambda},
    97  			{Value: "(", Type: ExpressionTokenOpenParen},
    98  			{Value: "d", Type: ExpressionTokenLiteral},
    99  			{Value: ",", Type: ExpressionTokenColon},
   100  			{Value: "d", Type: ExpressionTokenLiteral},
   101  			{Value: "eq", Type: ExpressionTokenLogical},
   102  			{Value: "'Site'", Type: ExpressionTokenString},
   103  			{Value: ")", Type: ExpressionTokenCloseParen},
   104  		}
   105  		output, err := tokenizer.Tokenize(ctx, input)
   106  		if err != nil {
   107  			t.Error(err)
   108  		}
   109  
   110  		result, err := CompareTokens(expect, output)
   111  		if !result {
   112  			t.Error(err)
   113  		}
   114  	}
   115  	q, err := ParseFilterString(ctx, input)
   116  	if err != nil {
   117  		t.Errorf("Error parsing query %s. Error: %v", input, err)
   118  		return
   119  	}
   120  	var expect []expectedParseNode = []expectedParseNode{
   121  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
   122  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
   123  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
   124  		{Value: "d", Depth: 2, Type: ExpressionTokenLiteral},
   125  		{Value: "eq", Depth: 2, Type: ExpressionTokenLogical},
   126  		{Value: "d", Depth: 3, Type: ExpressionTokenLiteral},
   127  		{Value: "'Site'", Depth: 3, Type: ExpressionTokenString},
   128  	}
   129  	pos := 0
   130  	err = CompareTree(q.Tree, expect, &pos, 0)
   131  	if err != nil {
   132  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   133  	}
   134  }
   135  
   136  // geographyPolygon   = geographyPrefix SQUOTE fullPolygonLiteral SQUOTE
   137  // geographyPrefix = "geography"
   138  // fullPolygonLiteral = sridLiteral polygonLiteral
   139  // sridLiteral      = "SRID" EQ 1*5DIGIT SEMI
   140  // polygonLiteral     = "Polygon" polygonData
   141  // polygonData        = OPEN ringLiteral *( COMMA ringLiteral ) CLOSE
   142  // positionLiteral  = doubleValue SP doubleValue  ; longitude, then latitude
   143  /*
   144  func TestFilterGeographyPolygon(t *testing.T) {
   145  	input := "geo.intersects(location, geography'SRID=0;Polygon(-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581)')"
   146  	q, err := ParseFilterString(input)
   147  	if err != nil {
   148  		t.Errorf("Error parsing query %s. Error: %s", input, err.Error())
   149  		return
   150  	}
   151  	var expect []expectedParseNode = []expectedParseNode{
   152  		{Value:"geo.intersects", Depth:0, Type: 0},
   153  		{Value:"location", Depth:1, Type: 0},
   154  		{Value:"geography'SRID=0;Polygon(-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581)'", Depth:1, Type: 0},
   155  	}
   156  	pos := 0
   157  	err = CompareTree(q.Tree, expect, &pos, 0)
   158  	if err != nil {
   159  		fmt.Printf("Got tree:\n%v\n", q.Tree.String())
   160  		t.Errorf("Tree representation does not match expected value. error: %s", err.Error())
   161  	}
   162  }
   163  */
   164  
   165  // TestFilterAnyGeography matches documents where any of the geo coordinates in the locations field is within the given polygon.
   166  /*
   167  func TestFilterAnyGeography(t *testing.T) {
   168  	input := "locations/any(loc: geo.intersects(loc, geography'Polygon((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581))'))"
   169  	q, err := ParseFilterString(input)
   170  	if err != nil {
   171  		t.Errorf("Error parsing query %s. Error: %s", input, err.Error())
   172  		return
   173  	}
   174  	var expect []expectedParseNode = []expectedParseNode{
   175  		{Value:"/", Depth:0, Type: 0},
   176  		{Value:"Tags", Depth:1, Type: 0},
   177  		{Value:"any", Depth:1, Type: 0},
   178  		{Value:"d", Depth:2, Type: 0},
   179  		{Value:"or", Depth:2, Type: 0},
   180  		{Value:"or", Depth:3, Type: 0},
   181  		{Value:"or", Depth:4, Type: 0},
   182  		{Value:"eq", Depth:5, Type: 0},
   183  		{Value:"d", Depth:6, Type: 0},
   184  		{Value:"'Site'", Depth:6, Type: 0},
   185  		{Value:"eq", Depth:5, Type: 0},
   186  		{Value:"'Environment'", Depth:6, Type: 0},
   187  		{Value:"/", Depth:6, Type: 0},
   188  		{Value:"d", Depth:7, Type: 0},
   189  		{Value:"Key", Depth:7, Type: 0},
   190  		{Value:"eq", Depth:4, Type: 0},
   191  		{Value:"/", Depth:5, Type: 0},
   192  		{Value:"/", Depth:6, Type: 0},
   193  		{Value:"d", Depth:7, Type: 0},
   194  		{Value:"d", Depth:7, Type: 0},
   195  		{Value:"d", Depth:6, Type: 0},
   196  		{Value:"123456", Depth:5, Type: 0},
   197  		{Value:"eq", Depth:3, Type: 0},
   198  		{Value:"concat", Depth:4, Type: 0},
   199  		{Value:"/", Depth:5, Type: 0},
   200  		{Value:"d", Depth:6, Type: 0},
   201  		{Value:"FirstName", Depth:6, Type: 0},
   202  		{Value:"/", Depth:5, Type: 0},
   203  		{Value:"d", Depth:6, Type: 0},
   204  		{Value:"LastName", Depth:6, Type: 0},
   205  		{Value:"/", Depth:4, Type: 0},
   206  		{Value:"$it", Depth:5, Type: 0},
   207  		{Value:"FullName", Depth:5, Type: 0},
   208  	}
   209  	pos := 0
   210  	err = CompareTree(q.Tree, expect, &pos, 0)
   211  	if err != nil {
   212  		fmt.Printf("Got tree:\n%v\n", q.Tree.String())
   213  		t.Errorf("Tree representation does not match expected value. error: %s", err.Error())
   214  	}
   215  }
   216  */
   217  
   218  func TestFilterAnyMixedQuery(t *testing.T) {
   219  	/*
   220  		{
   221  			"Tags": [
   222  				"Site",
   223  				{ "Key": "Environment" },
   224  				{ "d" : { "d": 123456 }},
   225  				{ "FirstName" : "Bob", "LastName": "Smith"}
   226  			],
   227  			"FullName": "BobSmith"
   228  		}
   229  	*/
   230  	// The argument of a lambda operator is a case-sensitive lambda variable name followed by a colon (:) and a Boolean expression that
   231  	// uses the lambda variable name to refer to properties of members of the collection identified by the navigation path.
   232  	// If the name chosen for the lambda variable matches a property name of the current resource referenced by the resource path, the lambda variable takes precedence.
   233  	// Clients can prefix properties of the current resource referenced by the resource path with $it.
   234  	// Other path expressions in the Boolean expression neither prefixed with the lambda variable nor $it are evaluated in the scope of
   235  	// the collection instances at the origin of the navigation path prepended to the lambda operator.
   236  	input := "Tags/any(d:d eq 'Site' or 'Environment' eq d/Key or d/d/d eq 123456 or concat(d/FirstName, d/LastName) eq $it/FullName)"
   237  	ctx := context.Background()
   238  	q, err := ParseFilterString(ctx, input)
   239  	if err != nil {
   240  		t.Errorf("Error parsing query %s. Error: %v", input, err)
   241  		return
   242  	}
   243  	var expect []expectedParseNode = []expectedParseNode{
   244  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
   245  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
   246  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
   247  		{Value: "d", Depth: 2, Type: ExpressionTokenLiteral},
   248  		{Value: "or", Depth: 2, Type: ExpressionTokenLogical},
   249  		{Value: "or", Depth: 3, Type: ExpressionTokenLogical},
   250  		{Value: "or", Depth: 4, Type: ExpressionTokenLogical},
   251  		{Value: "eq", Depth: 5, Type: ExpressionTokenLogical},
   252  		{Value: "d", Depth: 6, Type: ExpressionTokenLiteral},
   253  		{Value: "'Site'", Depth: 6, Type: ExpressionTokenString},
   254  		{Value: "eq", Depth: 5, Type: ExpressionTokenLogical},
   255  		{Value: "'Environment'", Depth: 6, Type: ExpressionTokenString},
   256  		{Value: "/", Depth: 6, Type: ExpressionTokenNav},
   257  		{Value: "d", Depth: 7, Type: ExpressionTokenLiteral},
   258  		{Value: "Key", Depth: 7, Type: ExpressionTokenLiteral},
   259  		{Value: "eq", Depth: 4, Type: ExpressionTokenLogical},
   260  		{Value: "/", Depth: 5, Type: ExpressionTokenNav},
   261  		{Value: "/", Depth: 6, Type: ExpressionTokenNav},
   262  		{Value: "d", Depth: 7, Type: ExpressionTokenLiteral},
   263  		{Value: "d", Depth: 7, Type: ExpressionTokenLiteral},
   264  		{Value: "d", Depth: 6, Type: ExpressionTokenLiteral},
   265  		{Value: "123456", Depth: 5, Type: ExpressionTokenInteger},
   266  		{Value: "eq", Depth: 3, Type: ExpressionTokenLogical},
   267  		{Value: "concat", Depth: 4, Type: ExpressionTokenFunc},
   268  		{Value: "/", Depth: 5, Type: ExpressionTokenNav},
   269  		{Value: "d", Depth: 6, Type: ExpressionTokenLiteral},
   270  		{Value: "FirstName", Depth: 6, Type: ExpressionTokenLiteral},
   271  		{Value: "/", Depth: 5, Type: ExpressionTokenNav},
   272  		{Value: "d", Depth: 6, Type: ExpressionTokenLiteral},
   273  		{Value: "LastName", Depth: 6, Type: ExpressionTokenLiteral},
   274  		{Value: "/", Depth: 4, Type: ExpressionTokenNav},
   275  		{Value: "$it", Depth: 5, Type: ExpressionTokenIt},
   276  		{Value: "FullName", Depth: 5, Type: ExpressionTokenLiteral},
   277  	}
   278  	pos := 0
   279  	err = CompareTree(q.Tree, expect, &pos, 0)
   280  	if err != nil {
   281  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   282  	}
   283  }
   284  
   285  func TestFilterGuid(t *testing.T) {
   286  	ctx := context.Background()
   287  	tokenizer := NewExpressionTokenizer()
   288  	input := "GuidValue eq 01234567-89ab-cdef-0123-456789abcdef"
   289  
   290  	expect := []*Token{
   291  		{Value: "GuidValue", Type: ExpressionTokenLiteral},
   292  		{Value: "eq", Type: ExpressionTokenLogical},
   293  		{Value: "01234567-89ab-cdef-0123-456789abcdef", Type: ExpressionTokenGuid},
   294  	}
   295  	output, err := tokenizer.Tokenize(ctx, input)
   296  	if err != nil {
   297  		t.Error(err)
   298  	}
   299  	result, err := CompareTokens(expect, output)
   300  	if !result {
   301  		t.Error(err)
   302  	}
   303  }
   304  
   305  func TestFilterDurationWithType(t *testing.T) {
   306  	ctx := context.Background()
   307  	tokenizer := NewExpressionTokenizer()
   308  	input := "Task eq duration'P12DT23H59M59.999999999999S'"
   309  
   310  	expect := []*Token{
   311  		{Value: "Task", Type: ExpressionTokenLiteral},
   312  		{Value: "eq", Type: ExpressionTokenLogical},
   313  		// Note the duration token is extracted.
   314  		{Value: "P12DT23H59M59.999999999999S", Type: ExpressionTokenDuration},
   315  	}
   316  	output, err := tokenizer.Tokenize(ctx, input)
   317  	if err != nil {
   318  		t.Error(err)
   319  	}
   320  	result, err := CompareTokens(expect, output)
   321  	if !result {
   322  		printTokens(output)
   323  		t.Error(err)
   324  	}
   325  }
   326  
   327  func TestFilterDurationWithoutType(t *testing.T) {
   328  	ctx := context.Background()
   329  	tokenizer := NewExpressionTokenizer()
   330  	input := "Task eq 'P12DT23H59M59.999999999999S'"
   331  
   332  	expect := []*Token{
   333  		{Value: "Task", Type: ExpressionTokenLiteral},
   334  		{Value: "eq", Type: ExpressionTokenLogical},
   335  		{Value: "P12DT23H59M59.999999999999S", Type: ExpressionTokenDuration},
   336  	}
   337  	output, err := tokenizer.Tokenize(ctx, input)
   338  	if err != nil {
   339  		t.Error(err)
   340  	}
   341  	result, err := CompareTokens(expect, output)
   342  	if !result {
   343  		printTokens(output)
   344  		t.Error(err)
   345  	}
   346  }
   347  
   348  func TestFilterAnyWithNoArgs(t *testing.T) {
   349  	ctx := context.Background()
   350  	tokenizer := NewExpressionTokenizer()
   351  	input := "Tags/any()"
   352  	{
   353  		expect := []*Token{
   354  			{Value: "Tags", Type: ExpressionTokenLiteral},
   355  			{Value: "/", Type: ExpressionTokenLambdaNav},
   356  			{Value: "any", Type: ExpressionTokenLambda},
   357  			{Value: "(", Type: ExpressionTokenOpenParen},
   358  			{Value: ")", Type: ExpressionTokenCloseParen},
   359  		}
   360  		output, err := tokenizer.Tokenize(ctx, input)
   361  		if err != nil {
   362  			t.Error(err)
   363  		}
   364  
   365  		result, err := CompareTokens(expect, output)
   366  		if !result {
   367  			t.Error(err)
   368  		}
   369  	}
   370  	q, err := ParseFilterString(ctx, input)
   371  	if err != nil {
   372  		t.Errorf("Error parsing query %s. Error: %v", input, err)
   373  		return
   374  	}
   375  	var expect []expectedParseNode = []expectedParseNode{
   376  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
   377  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
   378  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
   379  	}
   380  	pos := 0
   381  	err = CompareTree(q.Tree, expect, &pos, 0)
   382  	if err != nil {
   383  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   384  	}
   385  }
   386  func TestFilterDivby(t *testing.T) {
   387  	ctx := context.Background()
   388  	{
   389  		tokenizer := NewExpressionTokenizer()
   390  		input := "Price div 2 gt 3.5"
   391  		expect := []*Token{
   392  			{Value: "Price", Type: ExpressionTokenLiteral},
   393  			{Value: "div", Type: ExpressionTokenOp},
   394  			{Value: "2", Type: ExpressionTokenInteger},
   395  			{Value: "gt", Type: ExpressionTokenLogical},
   396  			{Value: "3.5", Type: ExpressionTokenFloat},
   397  		}
   398  		output, err := tokenizer.Tokenize(ctx, input)
   399  		if err != nil {
   400  			t.Error(err)
   401  		}
   402  
   403  		result, err := CompareTokens(expect, output)
   404  		if !result {
   405  			t.Error(err)
   406  		}
   407  	}
   408  	{
   409  		tokenizer := NewExpressionTokenizer()
   410  		input := "Price divby 2 gt 3.5"
   411  		expect := []*Token{
   412  			{Value: "Price", Type: ExpressionTokenLiteral},
   413  			{Value: "divby", Type: ExpressionTokenOp},
   414  			{Value: "2", Type: ExpressionTokenInteger},
   415  			{Value: "gt", Type: ExpressionTokenLogical},
   416  			{Value: "3.5", Type: ExpressionTokenFloat},
   417  		}
   418  		output, err := tokenizer.Tokenize(ctx, input)
   419  		if err != nil {
   420  			t.Error(err)
   421  		}
   422  
   423  		result, err := CompareTokens(expect, output)
   424  		if !result {
   425  			t.Error(err)
   426  		}
   427  	}
   428  }
   429  
   430  func TestFilterNotBooleanProperty(t *testing.T) {
   431  	ctx := context.Background()
   432  	tokenizer := NewExpressionTokenizer()
   433  	input := "not Enabled"
   434  	{
   435  		expect := []*Token{
   436  			{Value: "not", Type: ExpressionTokenLogical},
   437  			{Value: "Enabled", Type: ExpressionTokenLiteral},
   438  		}
   439  		output, err := tokenizer.Tokenize(ctx, input)
   440  		if err != nil {
   441  			t.Error(err)
   442  		}
   443  		result, err := CompareTokens(expect, output)
   444  		if !result {
   445  			t.Error(err)
   446  		}
   447  	}
   448  	q, err := ParseFilterString(ctx, input)
   449  	if err != nil {
   450  		t.Errorf("Error parsing query %s. Error: %v", input, err)
   451  		return
   452  	}
   453  	var expect []expectedParseNode = []expectedParseNode{
   454  		{Value: "not", Depth: 0, Type: ExpressionTokenLogical},
   455  		{Value: "Enabled", Depth: 1, Type: ExpressionTokenLiteral},
   456  	}
   457  	pos := 0
   458  	err = CompareTree(q.Tree, expect, &pos, 0)
   459  	if err != nil {
   460  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   461  	}
   462  
   463  }
   464  
   465  func TestFilterEmptyStringToken(t *testing.T) {
   466  	ctx := context.Background()
   467  	tokenizer := NewExpressionTokenizer()
   468  	input := "City eq ''"
   469  	expect := []*Token{
   470  		{Value: "City", Type: ExpressionTokenLiteral},
   471  		{Value: "eq", Type: ExpressionTokenLogical},
   472  		{Value: "''", Type: ExpressionTokenString},
   473  	}
   474  	output, err := tokenizer.Tokenize(ctx, input)
   475  	if err != nil {
   476  		t.Error(err)
   477  	}
   478  	result, err := CompareTokens(expect, output)
   479  	if !result {
   480  		t.Error(err)
   481  	}
   482  }
   483  
   484  // Note: according to ODATA ABNF notation, there must be a space between not and open parenthesis.
   485  // http://docs.oasis-open.org/odata/odata/v4.01/csprd03/abnf/odata-abnf-construction-rules.txt
   486  func TestFilterNotWithNoSpace(t *testing.T) {
   487  	ctx := context.Background()
   488  	tokenizer := NewExpressionTokenizer()
   489  	input := "not(City eq 'Seattle')"
   490  	{
   491  		expect := []*Token{
   492  			{Value: "not", Type: ExpressionTokenLogical},
   493  			{Value: "(", Type: ExpressionTokenOpenParen},
   494  			{Value: "City", Type: ExpressionTokenLiteral},
   495  			{Value: "eq", Type: ExpressionTokenLogical},
   496  			{Value: "'Seattle'", Type: ExpressionTokenString},
   497  			{Value: ")", Type: ExpressionTokenCloseParen},
   498  		}
   499  		output, err := tokenizer.Tokenize(ctx, input)
   500  		if err != nil {
   501  			t.Error(err)
   502  		}
   503  		result, err := CompareTokens(expect, output)
   504  		if !result {
   505  			t.Error(err)
   506  		}
   507  	}
   508  
   509  	q, err := ParseFilterString(ctx, input)
   510  	if err != nil {
   511  		t.Errorf("Error parsing query %s. Error: %v", input, err)
   512  		return
   513  	}
   514  	var expect []expectedParseNode = []expectedParseNode{
   515  		{Value: "not", Depth: 0, Type: ExpressionTokenLogical},
   516  		{Value: "eq", Depth: 1, Type: ExpressionTokenLogical},
   517  		{Value: "City", Depth: 2, Type: ExpressionTokenLiteral},
   518  		{Value: "'Seattle'", Depth: 2, Type: ExpressionTokenString},
   519  	}
   520  	pos := 0
   521  	err = CompareTree(q.Tree, expect, &pos, 0)
   522  	if err != nil {
   523  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   524  	}
   525  }
   526  
   527  // TestFilterInOperator tests the "IN" operator with a comma-separated list of values.
   528  func TestFilterInOperator(t *testing.T) {
   529  	ctx := context.Background()
   530  	tokenizer := NewExpressionTokenizer()
   531  	input := "City in ( 'Seattle', 'Atlanta', 'Paris' )"
   532  
   533  	expect := []*Token{
   534  		{Value: "City", Type: ExpressionTokenLiteral},
   535  		{Value: "in", Type: ExpressionTokenLogical},
   536  		{Value: "(", Type: ExpressionTokenOpenParen},
   537  		{Value: "'Seattle'", Type: ExpressionTokenString},
   538  		{Value: ",", Type: ExpressionTokenComma},
   539  		{Value: "'Atlanta'", Type: ExpressionTokenString},
   540  		{Value: ",", Type: ExpressionTokenComma},
   541  		{Value: "'Paris'", Type: ExpressionTokenString},
   542  		{Value: ")", Type: ExpressionTokenCloseParen},
   543  	}
   544  	tokens, err := tokenizer.Tokenize(ctx, input)
   545  	if err != nil {
   546  		t.Error(err)
   547  	}
   548  	result, err := CompareTokens(expect, tokens)
   549  	if !result {
   550  		t.Error(err)
   551  	}
   552  	var postfix *tokenQueue
   553  	postfix, err = GlobalFilterParser.InfixToPostfix(ctx, tokens)
   554  	if err != nil {
   555  		t.Error(err)
   556  	}
   557  	expect = []*Token{
   558  		{Value: "City", Type: ExpressionTokenLiteral},
   559  		{Value: "'Seattle'", Type: ExpressionTokenString},
   560  		{Value: "'Atlanta'", Type: ExpressionTokenString},
   561  		{Value: "'Paris'", Type: ExpressionTokenString},
   562  		{Value: "3", Type: TokenTypeArgCount},
   563  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   564  		{Value: "in", Type: ExpressionTokenLogical},
   565  	}
   566  	if err := CompareQueue(expect, postfix); err != nil {
   567  		t.Error(err)
   568  	}
   569  
   570  	tree, err := GlobalFilterParser.PostfixToTree(ctx, postfix)
   571  	if err != nil {
   572  		t.Error(err)
   573  	}
   574  
   575  	var treeExpect []expectedParseNode = []expectedParseNode{
   576  		{Value: "in", Depth: 0, Type: ExpressionTokenLogical},
   577  		{Value: "City", Depth: 1, Type: ExpressionTokenLiteral},
   578  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   579  		{Value: "'Seattle'", Depth: 2, Type: ExpressionTokenString},
   580  		{Value: "'Atlanta'", Depth: 2, Type: ExpressionTokenString},
   581  		{Value: "'Paris'", Depth: 2, Type: ExpressionTokenString},
   582  	}
   583  	pos := 0
   584  	err = CompareTree(tree, treeExpect, &pos, 0)
   585  	if err != nil {
   586  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
   587  	}
   588  }
   589  
   590  // TestFilterInOperatorSingleValue tests the "IN" operator with a list containing a single value.
   591  func TestFilterInOperatorSingleValue(t *testing.T) {
   592  	ctx := context.Background()
   593  	tokenizer := NewExpressionTokenizer()
   594  	input := "City in ( 'Seattle' )"
   595  
   596  	expect := []*Token{
   597  		{Value: "City", Type: ExpressionTokenLiteral},
   598  		{Value: "in", Type: ExpressionTokenLogical},
   599  		{Value: "(", Type: ExpressionTokenOpenParen},
   600  		{Value: "'Seattle'", Type: ExpressionTokenString},
   601  		{Value: ")", Type: ExpressionTokenCloseParen},
   602  	}
   603  	tokens, err := tokenizer.Tokenize(ctx, input)
   604  	if err != nil {
   605  		t.Error(err)
   606  	}
   607  	result, err := CompareTokens(expect, tokens)
   608  	if !result {
   609  		t.Error(err)
   610  	}
   611  	var postfix *tokenQueue
   612  	postfix, err = GlobalFilterParser.InfixToPostfix(ctx, tokens)
   613  	if err != nil {
   614  		t.Error(err)
   615  	}
   616  	expect = []*Token{
   617  		{Value: "City", Type: ExpressionTokenLiteral},
   618  		{Value: "'Seattle'", Type: ExpressionTokenString},
   619  		{Value: "1", Type: TokenTypeArgCount},
   620  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   621  		{Value: "in", Type: ExpressionTokenLogical},
   622  	}
   623  	if err := CompareQueue(expect, postfix); err != nil {
   624  		t.Error(err)
   625  	}
   626  
   627  	tree, err := GlobalFilterParser.PostfixToTree(ctx, postfix)
   628  	if err != nil {
   629  		t.Error(err)
   630  	}
   631  
   632  	var treeExpect []expectedParseNode = []expectedParseNode{
   633  		{Value: "in", Depth: 0, Type: ExpressionTokenLogical},
   634  		{Value: "City", Depth: 1, Type: ExpressionTokenLiteral},
   635  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   636  		{Value: "'Seattle'", Depth: 2, Type: ExpressionTokenString},
   637  	}
   638  	pos := 0
   639  	err = CompareTree(tree, treeExpect, &pos, 0)
   640  	if err != nil {
   641  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
   642  	}
   643  }
   644  
   645  // TestFilterInOperatorEmptyList tests the "IN" operator with a list containing no value.
   646  func TestFilterInOperatorEmptyList(t *testing.T) {
   647  	ctx := context.Background()
   648  	tokenizer := NewExpressionTokenizer()
   649  	input := "City in ( )"
   650  
   651  	expect := []*Token{
   652  		{Value: "City", Type: ExpressionTokenLiteral},
   653  		{Value: "in", Type: ExpressionTokenLogical},
   654  		{Value: "(", Type: ExpressionTokenOpenParen},
   655  		{Value: ")", Type: ExpressionTokenCloseParen},
   656  	}
   657  	tokens, err := tokenizer.Tokenize(ctx, input)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  	result, err := CompareTokens(expect, tokens)
   662  	if !result {
   663  		t.Fatal(err)
   664  	}
   665  	var postfix *tokenQueue
   666  	postfix, err = GlobalFilterParser.InfixToPostfix(ctx, tokens)
   667  	if err != nil {
   668  		t.Fatal(err)
   669  	}
   670  	expect = []*Token{
   671  		{Value: "City", Type: ExpressionTokenLiteral},
   672  		{Value: "0", Type: TokenTypeArgCount},
   673  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   674  		{Value: "in", Type: ExpressionTokenLogical},
   675  	}
   676  	if err := CompareQueue(expect, postfix); err != nil {
   677  		t.Fatal(err)
   678  	}
   679  
   680  	tree, err := GlobalFilterParser.PostfixToTree(ctx, postfix)
   681  	if err != nil {
   682  		t.Fatal(err)
   683  	}
   684  
   685  	var treeExpect []expectedParseNode = []expectedParseNode{
   686  		{Value: "in", Depth: 0, Type: ExpressionTokenLogical},
   687  		{Value: "City", Depth: 1, Type: ExpressionTokenLiteral},
   688  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   689  	}
   690  	pos := 0
   691  	err = CompareTree(tree, treeExpect, &pos, 0)
   692  	if err != nil {
   693  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
   694  	}
   695  }
   696  
   697  // TestFilterInOperatorBothSides tests the "IN" operator.
   698  // Use a listExpr on both sides of the IN operator.
   699  //   listExpr  = OPEN BWS commonExpr BWS *( COMMA BWS commonExpr BWS ) CLOSE
   700  // Validate if a list is within another list.
   701  func TestFilterInOperatorBothSides(t *testing.T) {
   702  	ctx := context.Background()
   703  	tokenizer := NewExpressionTokenizer()
   704  	input := "(1, 2) in ( ('ab', 'cd'), (1, 2), ('abc', 'def') )"
   705  
   706  	expect := []*Token{
   707  		{Value: "(", Type: ExpressionTokenOpenParen},
   708  		{Value: "1", Type: ExpressionTokenInteger},
   709  		{Value: ",", Type: ExpressionTokenComma},
   710  		{Value: "2", Type: ExpressionTokenInteger},
   711  		{Value: ")", Type: ExpressionTokenCloseParen},
   712  		{Value: "in", Type: ExpressionTokenLogical},
   713  		{Value: "(", Type: ExpressionTokenOpenParen},
   714  
   715  		{Value: "(", Type: ExpressionTokenOpenParen},
   716  		{Value: "'ab'", Type: ExpressionTokenString},
   717  		{Value: ",", Type: ExpressionTokenComma},
   718  		{Value: "'cd'", Type: ExpressionTokenString},
   719  		{Value: ")", Type: ExpressionTokenCloseParen},
   720  		{Value: ",", Type: ExpressionTokenComma},
   721  
   722  		{Value: "(", Type: ExpressionTokenOpenParen},
   723  		{Value: "1", Type: ExpressionTokenInteger},
   724  		{Value: ",", Type: ExpressionTokenComma},
   725  		{Value: "2", Type: ExpressionTokenInteger},
   726  		{Value: ")", Type: ExpressionTokenCloseParen},
   727  		{Value: ",", Type: ExpressionTokenComma},
   728  
   729  		{Value: "(", Type: ExpressionTokenOpenParen},
   730  		{Value: "'abc'", Type: ExpressionTokenString},
   731  		{Value: ",", Type: ExpressionTokenComma},
   732  		{Value: "'def'", Type: ExpressionTokenString},
   733  		{Value: ")", Type: ExpressionTokenCloseParen},
   734  		{Value: ")", Type: ExpressionTokenCloseParen},
   735  	}
   736  	tokens, err := tokenizer.Tokenize(ctx, input)
   737  	if err != nil {
   738  		t.Fatal(err)
   739  	}
   740  	result, err := CompareTokens(expect, tokens)
   741  	if !result {
   742  		t.Fatal(err)
   743  	}
   744  	var postfix *tokenQueue
   745  	postfix, err = GlobalFilterParser.InfixToPostfix(ctx, tokens)
   746  	if err != nil {
   747  		t.Fatalf("failed to convert from infix to postfix: %v", err)
   748  	}
   749  	expect = []*Token{
   750  		{Value: "1", Type: ExpressionTokenInteger},
   751  		{Value: "2", Type: ExpressionTokenInteger},
   752  		{Value: "2", Type: TokenTypeArgCount},
   753  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   754  
   755  		{Value: "'ab'", Type: ExpressionTokenString},
   756  		{Value: "'cd'", Type: ExpressionTokenString}, // idx 5
   757  		{Value: "2", Type: TokenTypeArgCount},
   758  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   759  
   760  		{Value: "1", Type: ExpressionTokenInteger},
   761  		{Value: "2", Type: ExpressionTokenInteger},
   762  		{Value: "2", Type: TokenTypeArgCount}, // idx 10
   763  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   764  
   765  		{Value: "'abc'", Type: ExpressionTokenString},
   766  		{Value: "'def'", Type: ExpressionTokenString},
   767  		{Value: "2", Type: TokenTypeArgCount},
   768  		{Value: TokenListExpr, Type: TokenTypeListExpr}, // idx 15
   769  
   770  		{Value: "3", Type: TokenTypeArgCount},
   771  		{Value: TokenListExpr, Type: TokenTypeListExpr},
   772  
   773  		{Value: "in", Type: ExpressionTokenLogical},
   774  	}
   775  	if err := CompareQueue(expect, postfix); err != nil {
   776  		t.Fatalf("Unexpected postfix notation: %v. Error: %v", postfix, err)
   777  	}
   778  
   779  	tree, err := GlobalFilterParser.PostfixToTree(ctx, postfix)
   780  	if err != nil {
   781  		t.Fatalf("Failed to convert postfix to tree: %v", err)
   782  	}
   783  
   784  	var treeExpect []expectedParseNode = []expectedParseNode{
   785  		{Value: "in", Depth: 0, Type: ExpressionTokenLogical},
   786  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   787  		{Value: "1", Depth: 2, Type: ExpressionTokenInteger},
   788  		{Value: "2", Depth: 2, Type: ExpressionTokenInteger},
   789  		//  ('ab', 'cd'), (1, 2), ('abc', 'def')
   790  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   791  		{Value: TokenListExpr, Depth: 2, Type: TokenTypeListExpr},
   792  		{Value: "'ab'", Depth: 3, Type: ExpressionTokenString},
   793  		{Value: "'cd'", Depth: 3, Type: ExpressionTokenString},
   794  		{Value: TokenListExpr, Depth: 2, Type: TokenTypeListExpr},
   795  		{Value: "1", Depth: 3, Type: ExpressionTokenInteger},
   796  		{Value: "2", Depth: 3, Type: ExpressionTokenInteger},
   797  		{Value: TokenListExpr, Depth: 2, Type: TokenTypeListExpr},
   798  		{Value: "'abc'", Depth: 3, Type: ExpressionTokenString},
   799  		{Value: "'def'", Depth: 3, Type: ExpressionTokenString},
   800  	}
   801  	pos := 0
   802  	err = CompareTree(tree, treeExpect, &pos, 0)
   803  	if err != nil {
   804  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
   805  	}
   806  }
   807  
   808  // TestFilterInOperatorWithFunc tests the "IN" operator with a comma-separated list
   809  // of values, one of which is a function call which itself has a comma-separated list of values.
   810  func TestFilterInOperatorWithFunc(t *testing.T) {
   811  	ctx := context.Background()
   812  	tokenizer := NewExpressionTokenizer()
   813  	// 'Atlanta' is enclosed in a unecessary parenExpr to validate the expression is properly unwrapped.
   814  	input := "City in ( 'Seattle', concat('San', 'Francisco'), ('Atlanta') )"
   815  
   816  	{
   817  		expect := []*Token{
   818  			{Value: "City", Type: ExpressionTokenLiteral},
   819  			{Value: "in", Type: ExpressionTokenLogical},
   820  			{Value: "(", Type: ExpressionTokenOpenParen},
   821  			{Value: "'Seattle'", Type: ExpressionTokenString},
   822  			{Value: ",", Type: ExpressionTokenComma},
   823  			{Value: "concat", Type: ExpressionTokenFunc},
   824  			{Value: "(", Type: ExpressionTokenOpenParen},
   825  			{Value: "'San'", Type: ExpressionTokenString},
   826  			{Value: ",", Type: ExpressionTokenComma},
   827  			{Value: "'Francisco'", Type: ExpressionTokenString},
   828  			{Value: ")", Type: ExpressionTokenCloseParen},
   829  			{Value: ",", Type: ExpressionTokenComma},
   830  			{Value: "(", Type: ExpressionTokenOpenParen},
   831  			{Value: "'Atlanta'", Type: ExpressionTokenString},
   832  			{Value: ")", Type: ExpressionTokenCloseParen},
   833  			{Value: ")", Type: ExpressionTokenCloseParen},
   834  		}
   835  		output, err := tokenizer.Tokenize(ctx, input)
   836  		if err != nil {
   837  			t.Error(err)
   838  		}
   839  		result, err := CompareTokens(expect, output)
   840  		if !result {
   841  			t.Error(err)
   842  		}
   843  	}
   844  	q, err := ParseFilterString(ctx, input)
   845  	if err != nil {
   846  		t.Fatalf("Error parsing filter: %v", err)
   847  	}
   848  	var expect []expectedParseNode = []expectedParseNode{
   849  		{Value: "in", Depth: 0, Type: ExpressionTokenLogical},
   850  		{Value: "City", Depth: 1, Type: ExpressionTokenLiteral},
   851  		{Value: TokenListExpr, Depth: 1, Type: TokenTypeListExpr},
   852  		{Value: "'Seattle'", Depth: 2, Type: ExpressionTokenString},
   853  		{Value: "concat", Depth: 2, Type: ExpressionTokenFunc},
   854  		{Value: "'San'", Depth: 3, Type: ExpressionTokenString},
   855  		{Value: "'Francisco'", Depth: 3, Type: ExpressionTokenString},
   856  		{Value: "'Atlanta'", Depth: 2, Type: ExpressionTokenString},
   857  	}
   858  	pos := 0
   859  	err = CompareTree(q.Tree, expect, &pos, 0)
   860  	if err != nil {
   861  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
   862  	}
   863  }
   864  
   865  func TestFilterNotInListExpr(t *testing.T) {
   866  	ctx := context.Background()
   867  	tokenizer := NewExpressionTokenizer()
   868  	input := "not ( City in ( 'Seattle', 'Atlanta' ) )"
   869  
   870  	{
   871  		expect := []*Token{
   872  			{Value: "not", Type: ExpressionTokenLogical},
   873  			{Value: "(", Type: ExpressionTokenOpenParen},
   874  			{Value: "City", Type: ExpressionTokenLiteral},
   875  			{Value: "in", Type: ExpressionTokenLogical},
   876  			{Value: "(", Type: ExpressionTokenOpenParen},
   877  			{Value: "'Seattle'", Type: ExpressionTokenString},
   878  			{Value: ",", Type: ExpressionTokenComma},
   879  			{Value: "'Atlanta'", Type: ExpressionTokenString},
   880  			{Value: ")", Type: ExpressionTokenCloseParen},
   881  			{Value: ")", Type: ExpressionTokenCloseParen},
   882  		}
   883  		output, err := tokenizer.Tokenize(ctx, input)
   884  		if err != nil {
   885  			t.Error(err)
   886  		}
   887  		result, err := CompareTokens(expect, output)
   888  		if !result {
   889  			t.Error(err)
   890  		}
   891  	}
   892  	{
   893  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
   894  		if err != nil {
   895  			t.Error(err)
   896  			return
   897  		}
   898  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
   899  		if err != nil {
   900  			t.Error(err)
   901  			return
   902  		}
   903  
   904  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
   905  		if err != nil {
   906  			t.Error(err)
   907  			return
   908  		}
   909  		var expect []expectedParseNode = []expectedParseNode{
   910  			{Value: "not", Depth: 0, Type: ExpressionTokenLogical},
   911  			{Value: "in", Depth: 1, Type: ExpressionTokenLogical},
   912  			{Value: "City", Depth: 2, Type: ExpressionTokenLiteral},
   913  			{Value: TokenListExpr, Depth: 2, Type: TokenTypeListExpr},
   914  			{Value: "'Seattle'", Depth: 3, Type: ExpressionTokenString},
   915  			{Value: "'Atlanta'", Depth: 3, Type: ExpressionTokenString},
   916  		}
   917  		pos := 0
   918  		err = CompareTree(tree, expect, &pos, 0)
   919  		if err != nil {
   920  			t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
   921  		}
   922  
   923  	}
   924  }
   925  
   926  func TestFilterAll(t *testing.T) {
   927  	ctx := context.Background()
   928  	tokenizer := NewExpressionTokenizer()
   929  	input := "Tags/all(d:d/Key eq 'Site')"
   930  	expect := []*Token{
   931  		{Value: "Tags", Type: ExpressionTokenLiteral},
   932  		{Value: "/", Type: ExpressionTokenLambdaNav},
   933  		{Value: "all", Type: ExpressionTokenLambda},
   934  		{Value: "(", Type: ExpressionTokenOpenParen},
   935  		{Value: "d", Type: ExpressionTokenLiteral},
   936  		{Value: ",", Type: ExpressionTokenColon},
   937  		{Value: "d", Type: ExpressionTokenLiteral},
   938  		{Value: "/", Type: ExpressionTokenNav},
   939  		{Value: "Key", Type: ExpressionTokenLiteral},
   940  		{Value: "eq", Type: ExpressionTokenLogical},
   941  		{Value: "'Site'", Type: ExpressionTokenString},
   942  		{Value: ")", Type: ExpressionTokenCloseParen},
   943  	}
   944  	output, err := tokenizer.Tokenize(ctx, input)
   945  	if err != nil {
   946  		t.Error(err)
   947  	}
   948  
   949  	result, err := CompareTokens(expect, output)
   950  	if !result {
   951  		t.Error(err)
   952  	}
   953  }
   954  
   955  func TestExpressionTokenizer(t *testing.T) {
   956  	ctx := context.Background()
   957  	tokenizer := NewExpressionTokenizer()
   958  	input := "Name eq 'Milk' and Price lt 2.55"
   959  	expect := []*Token{
   960  		{Value: "Name", Type: ExpressionTokenLiteral},
   961  		{Value: "eq", Type: ExpressionTokenLogical},
   962  		{Value: "'Milk'", Type: ExpressionTokenString},
   963  		{Value: "and", Type: ExpressionTokenLogical},
   964  		{Value: "Price", Type: ExpressionTokenLiteral},
   965  		{Value: "lt", Type: ExpressionTokenLogical},
   966  		{Value: "2.55", Type: ExpressionTokenFloat},
   967  	}
   968  	output, err := tokenizer.Tokenize(ctx, input)
   969  	if err != nil {
   970  		t.Error(err)
   971  	}
   972  
   973  	result, err := CompareTokens(expect, output)
   974  	if !result {
   975  		t.Error(err)
   976  	}
   977  }
   978  
   979  func TestFilterFunction(t *testing.T) {
   980  	ctx := context.Background()
   981  	tokenizer := NewExpressionTokenizer()
   982  	// The syntax for ODATA functions follows the inline parameter syntax. The function name must be followed
   983  	// by an opening parenthesis, followed by a comma-separated list of parameters, followed by a closing parenthesis.
   984  	// For example:
   985  	// GET serviceRoot/Airports?$filter=contains(Location/Address, 'San Francisco')
   986  	input := "contains(LastName, 'Smith') and FirstName eq 'John' and City eq 'Houston'"
   987  	expect := []*Token{
   988  		{Value: "contains", Type: ExpressionTokenFunc},
   989  		{Value: "(", Type: ExpressionTokenOpenParen},
   990  		{Value: "LastName", Type: ExpressionTokenLiteral},
   991  		{Value: ",", Type: ExpressionTokenComma},
   992  		{Value: "'Smith'", Type: ExpressionTokenString},
   993  		{Value: ")", Type: ExpressionTokenCloseParen},
   994  		{Value: "and", Type: ExpressionTokenLogical},
   995  		{Value: "FirstName", Type: ExpressionTokenLiteral},
   996  		{Value: "eq", Type: ExpressionTokenLogical},
   997  		{Value: "'John'", Type: ExpressionTokenString},
   998  		{Value: "and", Type: ExpressionTokenLogical},
   999  		{Value: "City", Type: ExpressionTokenLiteral},
  1000  		{Value: "eq", Type: ExpressionTokenLogical},
  1001  		{Value: "'Houston'", Type: ExpressionTokenString},
  1002  	}
  1003  	{
  1004  		output, err := tokenizer.Tokenize(ctx, input)
  1005  		if err != nil {
  1006  			t.Error(err)
  1007  		}
  1008  		result, err := CompareTokens(expect, output)
  1009  		if !result {
  1010  			t.Error(err)
  1011  		}
  1012  	}
  1013  	{
  1014  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1015  		if err != nil {
  1016  			t.Error(err)
  1017  			return
  1018  		}
  1019  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1020  		if err != nil {
  1021  			t.Error(err)
  1022  			return
  1023  		}
  1024  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1025  		if err != nil {
  1026  			t.Error(err)
  1027  			return
  1028  		}
  1029  		if tree.Token.Value != "and" {
  1030  			t.Errorf("Root is '%v', not 'and'", tree.Token.Value)
  1031  		}
  1032  		if len(tree.Children) != 2 {
  1033  			t.Errorf("Unexpected number of operators. Expected 2, got %d", len(tree.Children))
  1034  		}
  1035  		if tree.Children[0].Token.Value != "and" {
  1036  			t.Errorf("First child is '%v', not 'and'", tree.Children[0].Token.Value)
  1037  		}
  1038  		if len(tree.Children[0].Children) != 2 {
  1039  			t.Errorf("Unexpected number of operators. Expected 2, got %d", len(tree.Children))
  1040  		}
  1041  		if tree.Children[0].Children[0].Token.Value != "contains" {
  1042  			t.Errorf("First child is '%v', not 'contains'", tree.Children[0].Children[0].Token.Value)
  1043  		}
  1044  		if tree.Children[1].Token.Value != "eq" {
  1045  			t.Errorf("First child is '%v', not 'eq'", tree.Children[1].Token.Value)
  1046  		}
  1047  	}
  1048  }
  1049  
  1050  func TestFilterNestedFunction(t *testing.T) {
  1051  	ctx := context.Background()
  1052  	tokenizer := NewExpressionTokenizer()
  1053  	// Test ODATA syntax with nested function calls
  1054  	input := "contains(LastName, toupper('Smith')) or FirstName eq 'John'"
  1055  	expect := []*Token{
  1056  		{Value: "contains", Type: ExpressionTokenFunc},
  1057  		{Value: "(", Type: ExpressionTokenOpenParen},
  1058  		{Value: "LastName", Type: ExpressionTokenLiteral},
  1059  		{Value: ",", Type: ExpressionTokenComma},
  1060  		{Value: "toupper", Type: ExpressionTokenFunc},
  1061  		{Value: "(", Type: ExpressionTokenOpenParen},
  1062  		{Value: "'Smith'", Type: ExpressionTokenString},
  1063  		{Value: ")", Type: ExpressionTokenCloseParen},
  1064  		{Value: ")", Type: ExpressionTokenCloseParen},
  1065  		{Value: "or", Type: ExpressionTokenLogical},
  1066  		{Value: "FirstName", Type: ExpressionTokenLiteral},
  1067  		{Value: "eq", Type: ExpressionTokenLogical},
  1068  		{Value: "'John'", Type: ExpressionTokenString},
  1069  	}
  1070  	{
  1071  		output, err := tokenizer.Tokenize(ctx, input)
  1072  		if err != nil {
  1073  			t.Error(err)
  1074  		}
  1075  		result, err := CompareTokens(expect, output)
  1076  		if !result {
  1077  			t.Error(err)
  1078  		}
  1079  	}
  1080  	{
  1081  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1082  		if err != nil {
  1083  			t.Error(err)
  1084  			return
  1085  		}
  1086  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1087  		if err != nil {
  1088  			t.Error(err)
  1089  			return
  1090  		}
  1091  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1092  		if err != nil {
  1093  			t.Error(err)
  1094  			return
  1095  		}
  1096  		if tree.Token.Value != "or" {
  1097  			t.Errorf("Root is '%v', not 'or'", tree.Token.Value)
  1098  		}
  1099  		if len(tree.Children) != 2 {
  1100  			t.Errorf("Unexpected number of operators. Expected 2, got %d", len(tree.Children))
  1101  		}
  1102  		if tree.Children[0].Token.Value != "contains" {
  1103  			t.Errorf("First child is '%v', not 'contains'", tree.Children[0].Token.Value)
  1104  		}
  1105  		if len(tree.Children[0].Children) != 2 {
  1106  			t.Errorf("Unexpected number of nested children. Expected 2, got %d", len(tree.Children[0].Children))
  1107  		}
  1108  		if tree.Children[0].Children[1].Token.Value != "toupper" {
  1109  			t.Errorf("First child is '%v', not 'toupper'", tree.Children[0].Children[1].Token.Value)
  1110  		}
  1111  		if tree.Children[1].Token.Value != "eq" {
  1112  			t.Errorf("First child is '%v', not 'eq'", tree.Children[1].Token.Value)
  1113  		}
  1114  	}
  1115  }
  1116  
  1117  func TestValidFilterSyntax(t *testing.T) {
  1118  	queries := []string{
  1119  		"substring(CompanyName,1,2) eq 'lf'", // substring with 3 arguments.
  1120  		// Bolean values
  1121  		"true",
  1122  		"false",
  1123  		"(true)",
  1124  		"((true))",
  1125  		"((true)) or false",
  1126  		"not true",
  1127  		"not false",
  1128  		"not (not true)",
  1129  		//"not not true", // TODO: I think this should work. 'not not true' is true
  1130  		// String functions
  1131  		"contains(CompanyName,'freds')",
  1132  		"endswith(CompanyName,'Futterkiste')",
  1133  		"startswith(CompanyName,'Alfr')",
  1134  		"length(CompanyName) eq 19",
  1135  		"indexof(CompanyName,'lfreds') eq 1",
  1136  		"substring(CompanyName,1) eq 'lfreds Futterkiste'", // substring() with 2 arguments.
  1137  		"'lfreds Futterkiste' eq substring(CompanyName,1)", // Same as above, but order of operands is reversed.
  1138  		"substring(CompanyName,1,2) eq 'lf'",               // substring() with 3 arguments.
  1139  		"'lf' eq substring(CompanyName,1,2) ",              // Same as above, but order of operands is reversed.
  1140  		"substringof('Alfreds', CompanyName) eq true",
  1141  		"tolower(CompanyName) eq 'alfreds futterkiste'",
  1142  		"toupper(CompanyName) eq 'ALFREDS FUTTERKISTE'",
  1143  		"trim(CompanyName) eq 'Alfreds Futterkiste'",
  1144  		"concat(concat(City,', '), Country) eq 'Berlin, Germany'",
  1145  		// GUID
  1146  		"GuidValue eq 01234567-89ab-cdef-0123-456789abcdef", // TODO According to ODATA ABNF notation, GUID values do not have quotes.
  1147  		// Date and Time functions
  1148  		"StartDate eq 2012-12-03",
  1149  		"DateTimeOffsetValue eq 2012-12-03T07:16:23Z",
  1150  		// duration      = [ "duration" ] SQUOTE durationValue SQUOTE
  1151  		// "DurationValue eq duration'P12DT23H59M59.999999999999S'", // TODO See ODATA ABNF notation
  1152  		"TimeOfDayValue eq 07:59:59.999",
  1153  		"year(BirthDate) eq 0",
  1154  		"month(BirthDate) eq 12",
  1155  		"day(StartTime) eq 8",
  1156  		"hour(StartTime) eq 1",
  1157  		"hour    (StartTime) eq 12",     // function followed by space characters
  1158  		"hour    ( StartTime   ) eq 15", // function followed by space characters
  1159  		"minute(StartTime) eq 0",
  1160  		"totaloffsetminutes(StartTime) eq 0",
  1161  		"second(StartTime) eq 0",
  1162  		"fractionalseconds(StartTime) lt 0.123456", // The fractionalseconds function returns the fractional seconds component of the
  1163  		// DateTimeOffset or TimeOfDay parameter value as a non-negative decimal value less than 1.
  1164  		"date(StartTime) ne date(EndTime)",
  1165  		"totaloffsetminutes(StartTime) eq 60",
  1166  		"StartTime eq mindatetime()",
  1167  		"totalseconds(EndTime sub StartTime) lt duration'PT23H59M'", // TODO The totalseconds function returns the duration of the value in total seconds, including fractional seconds.
  1168  		"EndTime eq maxdatetime()",
  1169  		"time(StartTime) le StartOfDay",
  1170  		"time('2015-10-14T23:30:00.104+02:00') lt now()",
  1171  		"time(2015-10-14T23:30:00.104+02:00) lt now()",
  1172  		// Math functions
  1173  		"round(Freight) eq 32",
  1174  		"floor(Freight) eq 32",
  1175  		"ceiling(Freight) eq 33",
  1176  		"Rating mod 5 eq 0",
  1177  		"Price div 2 eq 3",
  1178  		// Type functions
  1179  		"isof(ShipCountry,Edm.String)",
  1180  		"isof(NorthwindModel.BigOrder)",
  1181  		"cast(ShipCountry,Edm.String)",
  1182  		// Parameter aliases
  1183  		// See http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_Toc453752288
  1184  		"Region eq @p1", // Aliases start with @
  1185  		// Geo functions
  1186  		"geo.distance(CurrentPosition,TargetPosition)",
  1187  		"geo.length(DirectRoute)",
  1188  		"geo.intersects(Position,TargetArea)",
  1189  		"GEO.INTERSECTS(Position,TargetArea)", // functions are case insensitive in ODATA 4.0.1
  1190  		// Logical operators
  1191  		"'Milk' eq 'Milk'",  // Compare two literals
  1192  		"'Water' ne 'Milk'", // Compare two literals
  1193  		"Name eq 'Milk'",
  1194  		"Name EQ 'Milk'", // operators are case insensitive in ODATA 4.0.1
  1195  		"Name ne 'Milk'",
  1196  		"Name NE 'Milk'",
  1197  		"Name gt 'Milk'",
  1198  		"Name ge 'Milk'",
  1199  		"Name lt 'Milk'",
  1200  		"Name le 'Milk'",
  1201  		"Name eq Name", // parameter equals to itself
  1202  		"Name eq 'Milk' and Price lt 2.55",
  1203  		"not endswith(Name,'ilk')",
  1204  		"Name eq 'Milk' or Price lt 2.55",
  1205  		"City eq 'Dallas' or City eq 'Houston'",
  1206  		// Nested properties
  1207  		"Product/Name eq 'Milk'",
  1208  		"Region/Product/Name eq 'Milk'",
  1209  		"Country/Region/Product/Name eq 'Milk'",
  1210  		//"style has Sales.Pattern'Yellow'", // TODO
  1211  		// Arithmetic operators
  1212  		"Price add 2.45 eq 5.00",
  1213  		"2.46 add Price eq 5.00",
  1214  		"Price add (2.47) eq 5.00",
  1215  		"(Price add (2.48)) eq 5.00",
  1216  		"Price ADD 2.49 eq 5.00", // 4.01 Services MUST support case-insensitive operator names.
  1217  		"Price sub 0.55 eq 2.00",
  1218  		"Price SUB 0.56 EQ 2.00", // 4.01 Services MUST support case-insensitive operator names.
  1219  		"Price mul 2.0 eq 5.10",
  1220  		"Price div 2.55 eq 1",
  1221  		"Rating div 2 eq 2",
  1222  		"Rating mod 5 eq 0",
  1223  		// Grouping
  1224  		"(4 add 5) mod (4 sub 1) eq 0",
  1225  		"not (City eq 'Dallas') or Name in ('a', 'b', 'c') and not (State eq 'California')",
  1226  		// Nested functions
  1227  		"length(trim(CompanyName)) eq length(CompanyName)",
  1228  		"concat(concat(City, ', '), Country) eq 'Berlin, Germany'",
  1229  		// Various parenthesis combinations
  1230  		"City eq 'Dallas'",
  1231  		"City eq ('Dallas')",
  1232  		"'Dallas' eq City",
  1233  		"not (City eq 'Dallas')",
  1234  		"City in ('Dallas')",
  1235  		"(City in ('Dallas'))",
  1236  		"(City in ('Dallas', 'Houston'))",
  1237  		"not (City in ('Dallas'))",
  1238  		"not (City in ('Dallas', 'Houston'))",
  1239  		"not (((City eq 'Dallas')))",
  1240  		"not(S1 eq 'foo')",
  1241  		// Lambda operators
  1242  		"Tags/any()",                    // The any operator without an argument returns true if the collection is not empty
  1243  		"Tags/any(tag:tag eq 'London')", // 'Tags' is array of strings
  1244  		"Tags/any(tag:tag eq 'London' or tag eq 'Berlin')",          // 'Tags' is array of strings
  1245  		"Tags/any(var:var/Key eq 'Site' and var/Value eq 'London')", // 'Tags' is array of {"Key": "abc", "Value": "def"}
  1246  		"Tags/ANY(var:var/Key eq 'Site' AND var/Value eq 'London')",
  1247  		"Tags/any(var:var/Key eq 'Site' and var/Value eq 'London') and not (City in ('Dallas'))",
  1248  		"Tags/all(var:var/Key eq 'Site' and var/Value eq 'London')",
  1249  		"Price/any(t:not (12345 eq t))",
  1250  		// A long query.
  1251  		"Tags/any(var:var/Key eq 'Site' and var/Value eq 'London') or " +
  1252  			"Tags/any(var:var/Key eq 'Site' and var/Value eq 'Berlin') or " +
  1253  			"Tags/any(var:var/Key eq 'Site' and var/Value eq 'Paris') or " +
  1254  			"Tags/any(var:var/Key eq 'Site' and var/Value eq 'New York City') or " +
  1255  			"Tags/any(var:var/Key eq 'Site' and var/Value eq 'San Francisco')",
  1256  	}
  1257  	ctx := context.Background()
  1258  	for _, input := range queries {
  1259  		q, err := ParseFilterString(ctx, input)
  1260  		if err != nil {
  1261  			t.Errorf("Error parsing query %s. Error: %v", input, err)
  1262  			return
  1263  		} else if q.Tree == nil {
  1264  			t.Errorf("Error parsing query %s. Tree is nil", input)
  1265  		}
  1266  		if q.Tree.Token == nil {
  1267  			t.Errorf("Error parsing query %s. Root token is nil", input)
  1268  		}
  1269  		if q.Tree.Token.Type == ExpressionTokenLiteral {
  1270  			t.Errorf("Error parsing query %s. Unexpected root token type: %+v", input, q.Tree.Token)
  1271  		}
  1272  		//printTree(q.Tree)
  1273  	}
  1274  }
  1275  
  1276  // The URLs below are not valid ODATA syntax, the parser should return an error.
  1277  func TestInvalidFilterSyntax(t *testing.T) {
  1278  	queries := []string{
  1279  		"()", // It's not a boolean expression
  1280  		"(TRUE)",
  1281  		"(City)",
  1282  		"(",
  1283  		"((((",
  1284  		")",
  1285  		"12345",                                // Number 12345 is not a boolean expression
  1286  		"0",                                    // Number 0 is not a boolean expression
  1287  		"'123'",                                // String '123' is not a boolean expression
  1288  		"TRUE",                                 // Should be 'true' lowercase
  1289  		"FALSE",                                // Should be 'false' lowercase
  1290  		"yes",                                  // yes is not a boolean expression
  1291  		"no",                                   // yes is not a boolean expression
  1292  		"",                                     // Empty string.
  1293  		"eq",                                   // Just a single logical operator
  1294  		"and",                                  // Just a single logical operator
  1295  		"add",                                  // Just a single arithmetic operator
  1296  		"add ",                                 // Just a single arithmetic operator
  1297  		"add 2",                                // Missing operands
  1298  		"City",                                 // Just a single literal
  1299  		"City City",                            // Sequence of two literals
  1300  		"City City City City",                  // Sequence of literals
  1301  		"eq eq",                                // Two consecutive operators
  1302  		"City eq",                              // Missing operand
  1303  		"City eq (",                            // Wrong operand
  1304  		"City eq )",                            // Wrong operand
  1305  		"City equals 'Dallas'",                 // Unknown operator that starts with the same letters as a known operator
  1306  		"City near 'Dallas'",                   // Unknown operator that starts with the same letters as a known operator
  1307  		"City isNot 'Dallas'",                  // Unknown operator
  1308  		"not [City eq 'Dallas']",               // Wrong delimiter
  1309  		"not (City eq )",                       // Missing operand
  1310  		"not ((City eq 'Dallas'",               // Missing closing parenthesis
  1311  		"not (City eq 'Dallas'",                // Missing closing parenthesis
  1312  		"not (City eq 'Dallas'))",              // Extraneous closing parenthesis
  1313  		"not City eq 'Dallas')",                // Missing open parenthesis
  1314  		"City eq 'Dallas' orCity eq 'Houston'", // missing space between or and City
  1315  		// TODO: the query below should fail.
  1316  		//"Tags/any(var:var/Key eq 'Site') orTags/any(var:var/Key eq 'Site')",
  1317  		"not (City eq 'Dallas') and Name eq 'Houston')",
  1318  		"Tags/all()",                   // The all operator cannot be used without an argument expression.
  1319  		"LastName contains 'Smith'",    // Previously the godata library was not returning an error.
  1320  		"contains",                     // Function with missing parenthesis and arguments
  1321  		"contains()",                   // Function with missing arguments
  1322  		"contains LastName, 'Smith'",   // Missing parenthesis
  1323  		"contains(LastName)",           // Insufficent number of function arguments
  1324  		"contains(LastName, 'Smith'))", // Extraneous closing parenthesis
  1325  		"contains(LastName, 'Smith'",   // Missing closing parenthesis
  1326  		"contains LastName, 'Smith')",  // Missing open parenthesis
  1327  		"City eq 'Dallas' 'Houston'",   // extraneous string value
  1328  		"(numCore neq 12)",             // Invalid operator. It should be 'ne'
  1329  		"(a b c d)",                    // Invalid list
  1330  		"numCore neq 12",               // Invalid operator. It should be 'ne'
  1331  		//"contains(Name, 'a', 'b', 'c', 'd')", // Too many function arguments
  1332  	}
  1333  	ctx := context.Background()
  1334  	for _, input := range queries {
  1335  		q, err := ParseFilterString(ctx, input)
  1336  		if err == nil {
  1337  			// The parser has incorrectly determined the syntax is valid.
  1338  			t.Errorf("The query '$filter=%s' is not valid ODATA syntax. The ODATA parser should return an error. Tree:\n%v", input, q.Tree)
  1339  		}
  1340  	}
  1341  }
  1342  
  1343  // See http://docs.oasis-open.org/odata/odata/v4.01/csprd02/part1-protocol/odata-v4.01-csprd02-part1-protocol.html#_Toc486263411
  1344  // Test 'in', which is the 'Is a member of' operator.
  1345  func TestFilterIn(t *testing.T) {
  1346  	ctx := context.Background()
  1347  	tokenizer := NewExpressionTokenizer()
  1348  	input := "contains(LastName, 'Smith') and Site in ('London', 'Paris', 'San Francisco', 'Dallas') and FirstName eq 'John'"
  1349  	expect := []*Token{
  1350  		{Value: "contains", Type: ExpressionTokenFunc},
  1351  		{Value: "(", Type: ExpressionTokenOpenParen},
  1352  		{Value: "LastName", Type: ExpressionTokenLiteral},
  1353  		{Value: ",", Type: ExpressionTokenComma},
  1354  		{Value: "'Smith'", Type: ExpressionTokenString},
  1355  		{Value: ")", Type: ExpressionTokenCloseParen},
  1356  		{Value: "and", Type: ExpressionTokenLogical},
  1357  		{Value: "Site", Type: ExpressionTokenLiteral},
  1358  		{Value: "in", Type: ExpressionTokenLogical},
  1359  		{Value: "(", Type: ExpressionTokenOpenParen},
  1360  		{Value: "'London'", Type: ExpressionTokenString},
  1361  		{Value: ",", Type: ExpressionTokenComma},
  1362  		{Value: "'Paris'", Type: ExpressionTokenString},
  1363  		{Value: ",", Type: ExpressionTokenComma},
  1364  		{Value: "'San Francisco'", Type: ExpressionTokenString},
  1365  		{Value: ",", Type: ExpressionTokenComma},
  1366  		{Value: "'Dallas'", Type: ExpressionTokenString},
  1367  		{Value: ")", Type: ExpressionTokenCloseParen},
  1368  		{Value: "and", Type: ExpressionTokenLogical},
  1369  		{Value: "FirstName", Type: ExpressionTokenLiteral},
  1370  		{Value: "eq", Type: ExpressionTokenLogical},
  1371  		{Value: "'John'", Type: ExpressionTokenString},
  1372  	}
  1373  	{
  1374  		output, err := tokenizer.Tokenize(ctx, input)
  1375  		if err != nil {
  1376  			t.Error(err)
  1377  		}
  1378  		result, err := CompareTokens(expect, output)
  1379  		if !result {
  1380  			t.Error(err)
  1381  		}
  1382  	}
  1383  	{
  1384  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1385  		if err != nil {
  1386  			t.Error(err)
  1387  			return
  1388  		}
  1389  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1390  		if err != nil {
  1391  			t.Error(err)
  1392  			return
  1393  		}
  1394  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1395  		if err != nil {
  1396  			t.Error(err)
  1397  			return
  1398  		}
  1399  
  1400  		/*
  1401  		  The expected tree is:
  1402  		  and        6
  1403  		   and        6
  1404  		     contains   8
  1405  		       LastName   20
  1406  		       'Smith'    15
  1407  		     in         6
  1408  		       Site       20
  1409  		       (          0
  1410  		         'London'   15
  1411  		         'Paris'    15
  1412  		         'San Francisco' 15
  1413  		         'Dallas'   15
  1414  		   eq         6
  1415  		     FirstName  20
  1416  		     'John'     15
  1417  
  1418  		*/
  1419  		if tree.Token.Value != "and" {
  1420  			t.Errorf("Root is '%v', not 'and'", tree.Token.Value)
  1421  		}
  1422  		if len(tree.Children) != 2 {
  1423  			t.Errorf("Unexpected number of operators. Expected 2, got %d", len(tree.Children))
  1424  		}
  1425  		if tree.Children[0].Token.Value != "and" {
  1426  			t.Errorf("First child is '%v', not 'and'", tree.Children[0].Token.Value)
  1427  		}
  1428  		if len(tree.Children[0].Children) != 2 {
  1429  			t.Errorf("Unexpected number of operators. Expected 2, got %d", len(tree.Children))
  1430  		}
  1431  		if tree.Children[0].Children[0].Token.Value != "contains" {
  1432  			t.Errorf("First child is '%v', not 'contains'", tree.Children[0].Children[0].Token.Value)
  1433  		}
  1434  		if tree.Children[0].Children[1].Token.Value != "in" {
  1435  			t.Errorf("First child is '%v', not 'in'", tree.Children[0].Children[1].Token.Value)
  1436  		}
  1437  		if len(tree.Children[0].Children[1].Children) != 2 {
  1438  			t.Errorf("Unexpected number of operands for the 'in' operator. Expected 2, got %d",
  1439  				len(tree.Children[0].Children[1].Children))
  1440  		}
  1441  		if tree.Children[0].Children[1].Children[0].Token.Value != "Site" {
  1442  			t.Errorf("Unexpected operand for the 'in' operator. Expected 'Site', got %s",
  1443  				tree.Children[0].Children[1].Children[0].Token.Value)
  1444  		}
  1445  		if tree.Children[0].Children[1].Children[1].Token.Value != TokenListExpr {
  1446  			t.Errorf("Unexpected operand for the 'in' operator. Expected 'list', got %s",
  1447  				tree.Children[0].Children[1].Children[1].Token.Value)
  1448  		}
  1449  		if len(tree.Children[0].Children[1].Children[1].Children) != 4 {
  1450  			t.Errorf("Unexpected number of operands for the 'in' operator. Expected 4, got %d",
  1451  				len(tree.Children[0].Children[1].Children[1].Token.Value))
  1452  		}
  1453  		if tree.Children[1].Token.Value != "eq" {
  1454  			t.Errorf("First child is '%v', not 'eq'", tree.Children[1].Token.Value)
  1455  		}
  1456  	}
  1457  }
  1458  
  1459  func TestExpressionTokenizerFunc(t *testing.T) {
  1460  	ctx := context.Background()
  1461  	tokenizer := NewExpressionTokenizer()
  1462  	input := "not endswith(Name,'ilk')"
  1463  	expect := []*Token{
  1464  		{Value: "not", Type: ExpressionTokenLogical},
  1465  		{Value: "endswith", Type: ExpressionTokenFunc},
  1466  		{Value: "(", Type: ExpressionTokenOpenParen},
  1467  		{Value: "Name", Type: ExpressionTokenLiteral},
  1468  		{Value: ",", Type: ExpressionTokenComma},
  1469  		{Value: "'ilk'", Type: ExpressionTokenString},
  1470  		{Value: ")", Type: ExpressionTokenCloseParen},
  1471  	}
  1472  	output, err := tokenizer.Tokenize(ctx, input)
  1473  	if err != nil {
  1474  		t.Error(err)
  1475  	}
  1476  
  1477  	result, err := CompareTokens(expect, output)
  1478  	if !result {
  1479  		t.Error(err)
  1480  	}
  1481  }
  1482  
  1483  func BenchmarkFilterTokenizer(b *testing.B) {
  1484  	ctx := context.Background()
  1485  	t := NewExpressionTokenizer()
  1486  	for i := 0; i < b.N; i++ {
  1487  		input := "Name eq 'Milk' and Price lt 2.55"
  1488  		if _, err := t.Tokenize(ctx, input); err != nil {
  1489  			b.Fatalf("Failed to tokenize filter: %v", err)
  1490  		}
  1491  	}
  1492  }
  1493  
  1494  func TestFilterParserTree(t *testing.T) {
  1495  	ctx := context.Background()
  1496  	input := "not (A eq B)"
  1497  
  1498  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1499  	if err != nil {
  1500  		t.Error(err)
  1501  		return
  1502  	}
  1503  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1504  
  1505  	if err != nil {
  1506  		t.Error(err)
  1507  		return
  1508  	}
  1509  
  1510  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1511  
  1512  	if err != nil {
  1513  		t.Error(err)
  1514  		return
  1515  	}
  1516  
  1517  	if tree.Token.Value != "not" {
  1518  		t.Error("Root is '" + tree.Token.Value + "' not 'not'")
  1519  	}
  1520  	if tree.Children[0].Token.Value != "eq" {
  1521  		t.Error("First child is '" + tree.Children[1].Token.Value + "' not 'eq'")
  1522  	}
  1523  
  1524  }
  1525  
  1526  func TestFilterNestedPath(t *testing.T) {
  1527  	ctx := context.Background()
  1528  	input := "Address/City eq 'Redmond'"
  1529  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1530  	if err != nil {
  1531  		t.Error(err)
  1532  		return
  1533  	}
  1534  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1535  	if err != nil {
  1536  		t.Error(err)
  1537  		return
  1538  	}
  1539  
  1540  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1541  	if err != nil {
  1542  		t.Error(err)
  1543  		return
  1544  	}
  1545  
  1546  	var expect []expectedParseNode = []expectedParseNode{
  1547  		{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1548  		{Value: "/", Depth: 1, Type: ExpressionTokenNav},
  1549  		{Value: "Address", Depth: 2, Type: ExpressionTokenLiteral},
  1550  		{Value: "City", Depth: 2, Type: ExpressionTokenLiteral},
  1551  		{Value: "'Redmond'", Depth: 1, Type: ExpressionTokenString},
  1552  	}
  1553  	pos := 0
  1554  	err = CompareTree(tree, expect, &pos, 0)
  1555  	if err != nil {
  1556  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1557  	}
  1558  }
  1559  
  1560  func TestFilterMultipleNestedPath(t *testing.T) {
  1561  	ctx := context.Background()
  1562  	input := "Product/Address/City eq 'Redmond'"
  1563  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1564  	if err != nil {
  1565  		t.Error(err)
  1566  		return
  1567  	}
  1568  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1569  	if err != nil {
  1570  		t.Error(err)
  1571  		return
  1572  	}
  1573  
  1574  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1575  	if err != nil {
  1576  		t.Error(err)
  1577  		return
  1578  	}
  1579  
  1580  	var expect []expectedParseNode = []expectedParseNode{
  1581  		{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1582  		{Value: "/", Depth: 1, Type: ExpressionTokenNav},
  1583  		{Value: "/", Depth: 2, Type: ExpressionTokenNav},
  1584  		{Value: "Product", Depth: 3, Type: ExpressionTokenLiteral},
  1585  		{Value: "Address", Depth: 3, Type: ExpressionTokenLiteral},
  1586  		{Value: "City", Depth: 2, Type: ExpressionTokenLiteral},
  1587  		{Value: "'Redmond'", Depth: 1, Type: ExpressionTokenString},
  1588  	}
  1589  	pos := 0
  1590  	err = CompareTree(tree, expect, &pos, 0)
  1591  	if err != nil {
  1592  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1593  	}
  1594  }
  1595  
  1596  func TestFilterSubstringFunction(t *testing.T) {
  1597  	ctx := context.Background()
  1598  	// substring can take 2 or 3 arguments.
  1599  	{
  1600  		input := "substring(CompanyName,1) eq 'Foo'"
  1601  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1602  		if err != nil {
  1603  			t.Error(err)
  1604  			return
  1605  		}
  1606  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1607  		if err != nil {
  1608  			t.Error(err)
  1609  			return
  1610  		}
  1611  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1612  		if err != nil {
  1613  			t.Error(err)
  1614  			return
  1615  		}
  1616  		var expect []expectedParseNode = []expectedParseNode{
  1617  			{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1618  			{Value: "substring", Depth: 1, Type: ExpressionTokenFunc},
  1619  			{Value: "CompanyName", Depth: 2, Type: ExpressionTokenLiteral},
  1620  			{Value: "1", Depth: 2, Type: ExpressionTokenInteger},
  1621  			{Value: "'Foo'", Depth: 1, Type: ExpressionTokenString},
  1622  		}
  1623  		pos := 0
  1624  		err = CompareTree(tree, expect, &pos, 0)
  1625  		if err != nil {
  1626  			t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1627  		}
  1628  	}
  1629  	{
  1630  		input := "substring(CompanyName,1,2) eq 'lf'"
  1631  		tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1632  		if err != nil {
  1633  			t.Error(err)
  1634  			return
  1635  		}
  1636  		output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1637  		if err != nil {
  1638  			t.Error(err)
  1639  			return
  1640  		}
  1641  		tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1642  		if err != nil {
  1643  			t.Error(err)
  1644  			return
  1645  		}
  1646  		var expect []expectedParseNode = []expectedParseNode{
  1647  			{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1648  			{Value: "substring", Depth: 1, Type: ExpressionTokenFunc},
  1649  			{Value: "CompanyName", Depth: 2, Type: ExpressionTokenLiteral},
  1650  			{Value: "1", Depth: 2, Type: ExpressionTokenInteger},
  1651  			{Value: "2", Depth: 2, Type: ExpressionTokenInteger},
  1652  			{Value: "'lf'", Depth: 1, Type: ExpressionTokenString},
  1653  		}
  1654  		pos := 0
  1655  		err = CompareTree(tree, expect, &pos, 0)
  1656  		if err != nil {
  1657  			t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1658  		}
  1659  	}
  1660  }
  1661  
  1662  func TestFilterSubstringofFunction(t *testing.T) {
  1663  	ctx := context.Background()
  1664  	// Previously, the parser was incorrectly interpreting the 'substringof' function as the 'sub' operator.
  1665  	input := "substringof('Alfreds', CompanyName) eq true"
  1666  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1667  	if err != nil {
  1668  		t.Error(err)
  1669  		return
  1670  	}
  1671  	{
  1672  		expect := []*Token{
  1673  			{Value: "substringof", Type: ExpressionTokenFunc},
  1674  			{Value: "(", Type: ExpressionTokenOpenParen},
  1675  			{Value: "'Alfreds'", Type: ExpressionTokenString},
  1676  			{Value: ",", Type: ExpressionTokenComma},
  1677  			{Value: "CompanyName", Type: ExpressionTokenLiteral},
  1678  			{Value: ")", Type: ExpressionTokenCloseParen},
  1679  			{Value: "eq", Type: ExpressionTokenLogical},
  1680  			{Value: "true", Type: ExpressionTokenBoolean},
  1681  		}
  1682  		result, err := CompareTokens(expect, tokens)
  1683  		if !result {
  1684  			t.Error(err)
  1685  		}
  1686  	}
  1687  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1688  	if err != nil {
  1689  		t.Error(err)
  1690  		return
  1691  	}
  1692  	{
  1693  		expect := []*Token{
  1694  			{Value: "'Alfreds'", Type: ExpressionTokenString},
  1695  			{Value: "CompanyName", Type: ExpressionTokenLiteral},
  1696  			{Value: "2", Type: TokenTypeArgCount}, // The number of function arguments.
  1697  			{Value: TokenListExpr, Type: TokenTypeListExpr},
  1698  			{Value: "substringof", Type: ExpressionTokenFunc},
  1699  			{Value: "true", Type: ExpressionTokenBoolean},
  1700  			{Value: "eq", Type: ExpressionTokenLogical},
  1701  		}
  1702  		if err := CompareQueue(expect, output); err != nil {
  1703  			t.Error(err)
  1704  		}
  1705  	}
  1706  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1707  	if err != nil {
  1708  		t.Error(err)
  1709  		return
  1710  	}
  1711  	var expect []expectedParseNode = []expectedParseNode{
  1712  		{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1713  		{Value: "substringof", Depth: 1, Type: ExpressionTokenFunc},
  1714  		{Value: "'Alfreds'", Depth: 2, Type: ExpressionTokenString},
  1715  		{Value: "CompanyName", Depth: 2, Type: ExpressionTokenLiteral},
  1716  		{Value: "true", Depth: 1, Type: ExpressionTokenBoolean},
  1717  	}
  1718  	pos := 0
  1719  	err = CompareTree(tree, expect, &pos, 0)
  1720  	if err != nil {
  1721  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1722  	}
  1723  }
  1724  
  1725  // TestSubstringNestedFunction tests the substring function with a nested call
  1726  // to substring, with the use of 2-argument and 3-argument substring.
  1727  func TestFilterSubstringNestedFunction(t *testing.T) {
  1728  	ctx := context.Background()
  1729  	// Previously, the parser was incorrectly interpreting the 'substringof' function as the 'sub' operator.
  1730  	input := "substring(substring('Francisco', 1), 3, 2) eq 'ci'"
  1731  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1732  	if err != nil {
  1733  		t.Error(err)
  1734  		return
  1735  	}
  1736  	{
  1737  		expect := []*Token{
  1738  			{Value: "substring", Type: ExpressionTokenFunc},
  1739  			{Value: "(", Type: ExpressionTokenOpenParen},
  1740  			{Value: "substring", Type: ExpressionTokenFunc},
  1741  			{Value: "(", Type: ExpressionTokenOpenParen},
  1742  			{Value: "'Francisco'", Type: ExpressionTokenString},
  1743  			{Value: ",", Type: ExpressionTokenComma},
  1744  			{Value: "1", Type: ExpressionTokenInteger},
  1745  			{Value: ")", Type: ExpressionTokenCloseParen},
  1746  			{Value: ",", Type: ExpressionTokenComma},
  1747  			{Value: "3", Type: ExpressionTokenInteger},
  1748  			{Value: ",", Type: ExpressionTokenComma},
  1749  			{Value: "2", Type: ExpressionTokenInteger},
  1750  			{Value: ")", Type: ExpressionTokenCloseParen},
  1751  			{Value: "eq", Type: ExpressionTokenLogical},
  1752  			{Value: "'ci'", Type: ExpressionTokenString},
  1753  		}
  1754  		result, err := CompareTokens(expect, tokens)
  1755  		if !result {
  1756  			t.Error(err)
  1757  		}
  1758  	}
  1759  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1760  	if err != nil {
  1761  		t.Error(err)
  1762  		return
  1763  	}
  1764  	{
  1765  		expect := []*Token{
  1766  			{Value: "'Francisco'", Type: ExpressionTokenString},
  1767  			{Value: "1", Type: ExpressionTokenInteger},
  1768  			{Value: "2", Type: TokenTypeArgCount}, // The number of function arguments.
  1769  			{Value: TokenListExpr, Type: TokenTypeListExpr},
  1770  			{Value: "substring", Type: ExpressionTokenFunc},
  1771  			{Value: "3", Type: ExpressionTokenInteger},
  1772  			{Value: "2", Type: ExpressionTokenInteger},
  1773  			{Value: "3", Type: TokenTypeArgCount}, // The number of function arguments.
  1774  			{Value: TokenListExpr, Type: TokenTypeListExpr},
  1775  			{Value: "substring", Type: ExpressionTokenFunc},
  1776  			{Value: "'ci'", Type: ExpressionTokenString},
  1777  			{Value: "eq", Type: ExpressionTokenLogical},
  1778  		}
  1779  		if err := CompareQueue(expect, output); err != nil {
  1780  			t.Error(err)
  1781  		}
  1782  	}
  1783  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1784  	if err != nil {
  1785  		t.Error(err)
  1786  		return
  1787  	}
  1788  	var expect []expectedParseNode = []expectedParseNode{
  1789  		{Value: "eq", Depth: 0, Type: ExpressionTokenLogical},
  1790  		{Value: "substring", Depth: 1, Type: ExpressionTokenFunc},
  1791  		{Value: "substring", Depth: 2, Type: ExpressionTokenFunc},
  1792  		{Value: "'Francisco'", Depth: 3, Type: ExpressionTokenString},
  1793  		{Value: "1", Depth: 3, Type: ExpressionTokenInteger},
  1794  		{Value: "3", Depth: 2, Type: ExpressionTokenInteger},
  1795  		{Value: "2", Depth: 2, Type: ExpressionTokenInteger},
  1796  		{Value: "'ci'", Depth: 1, Type: ExpressionTokenString},
  1797  	}
  1798  	pos := 0
  1799  	err = CompareTree(tree, expect, &pos, 0)
  1800  	if err != nil {
  1801  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1802  	}
  1803  }
  1804  func TestFilterGeoFunctions(t *testing.T) {
  1805  	ctx := context.Background()
  1806  	// Previously, the parser was incorrectly interpreting the 'geo.xxx' functions as the 'ge' operator.
  1807  	input := "geo.distance(CurrentPosition,TargetPosition)"
  1808  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1809  	if err != nil {
  1810  		t.Error(err)
  1811  		return
  1812  	}
  1813  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1814  	if err != nil {
  1815  		t.Error(err)
  1816  		return
  1817  	}
  1818  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1819  	if err != nil {
  1820  		t.Error(err)
  1821  		return
  1822  	}
  1823  	var expect []expectedParseNode = []expectedParseNode{
  1824  		{Value: "geo.distance", Depth: 0, Type: ExpressionTokenFunc},
  1825  		{Value: "CurrentPosition", Depth: 1, Type: ExpressionTokenLiteral},
  1826  		{Value: "TargetPosition", Depth: 1, Type: ExpressionTokenLiteral},
  1827  	}
  1828  	pos := 0
  1829  	err = CompareTree(tree, expect, &pos, 0)
  1830  	if err != nil {
  1831  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1832  	}
  1833  }
  1834  
  1835  func TestFilterLambdaAny(t *testing.T) {
  1836  	ctx := context.Background()
  1837  	input := "Tags/any(var:var/Key eq 'Site')"
  1838  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1839  	if err != nil {
  1840  		t.Error(err)
  1841  		return
  1842  	}
  1843  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1844  	if err != nil {
  1845  		t.Error(err)
  1846  		return
  1847  	}
  1848  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1849  	if err != nil {
  1850  		t.Error(err)
  1851  		return
  1852  	}
  1853  
  1854  	var expect []expectedParseNode = []expectedParseNode{
  1855  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  1856  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
  1857  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  1858  		{Value: "var", Depth: 2, Type: ExpressionTokenLiteral},
  1859  		{Value: "eq", Depth: 2, Type: ExpressionTokenLogical},
  1860  		{Value: "/", Depth: 3, Type: ExpressionTokenNav},
  1861  		{Value: "var", Depth: 4, Type: ExpressionTokenLiteral},
  1862  		{Value: "Key", Depth: 4, Type: ExpressionTokenLiteral},
  1863  		{Value: "'Site'", Depth: 3, Type: ExpressionTokenString},
  1864  	}
  1865  	pos := 0
  1866  	err = CompareTree(tree, expect, &pos, 0)
  1867  	if err != nil {
  1868  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1869  	}
  1870  }
  1871  
  1872  func TestFilterLambdaAnyNot(t *testing.T) {
  1873  	ctx := context.Background()
  1874  	input := "Price/any(t:not (12345 eq t ))"
  1875  
  1876  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1877  	if err != nil {
  1878  		t.Error(err)
  1879  		return
  1880  	}
  1881  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1882  	if err != nil {
  1883  		t.Error(err)
  1884  		return
  1885  	}
  1886  	{
  1887  		expect := []*Token{
  1888  			{Value: "Price", Type: ExpressionTokenLiteral},
  1889  			{Value: "t", Type: ExpressionTokenLiteral},
  1890  			{Value: "12345", Type: ExpressionTokenInteger},
  1891  			{Value: "t", Type: ExpressionTokenLiteral},
  1892  			{Value: "eq", Type: ExpressionTokenLogical},
  1893  			{Value: "not", Type: ExpressionTokenLogical},
  1894  			{Value: "2", Type: TokenTypeArgCount},
  1895  			{Value: TokenListExpr, Type: TokenTypeListExpr},
  1896  			{Value: "any", Type: ExpressionTokenLambda},
  1897  			{Value: "/", Type: ExpressionTokenLambdaNav},
  1898  		}
  1899  		if err = CompareQueue(expect, output); err != nil {
  1900  			t.Error(err)
  1901  		}
  1902  	}
  1903  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1904  	if err != nil {
  1905  		t.Error(err)
  1906  		return
  1907  	}
  1908  	var expect []expectedParseNode = []expectedParseNode{
  1909  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  1910  		{Value: "Price", Depth: 1, Type: ExpressionTokenLiteral},
  1911  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  1912  		{Value: "t", Depth: 2, Type: ExpressionTokenLiteral},
  1913  		{Value: "not", Depth: 2, Type: ExpressionTokenLogical},
  1914  		{Value: "eq", Depth: 3, Type: ExpressionTokenLogical},
  1915  		{Value: "12345", Depth: 4, Type: ExpressionTokenInteger},
  1916  		{Value: "t", Depth: 4, Type: ExpressionTokenLiteral},
  1917  	}
  1918  	pos := 0
  1919  	err = CompareTree(tree, expect, &pos, 0)
  1920  	if err != nil {
  1921  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1922  	}
  1923  }
  1924  
  1925  func TestFilterLambdaAnyAnd(t *testing.T) {
  1926  	ctx := context.Background()
  1927  	input := "Tags/any(var:var/Key eq 'Site' and var/Value eq 'London')"
  1928  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  1929  	if err != nil {
  1930  		t.Error(err)
  1931  		return
  1932  	}
  1933  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  1934  	if err != nil {
  1935  		t.Error(err)
  1936  		return
  1937  	}
  1938  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  1939  	if err != nil {
  1940  		t.Error(err)
  1941  		return
  1942  	}
  1943  
  1944  	var expect []expectedParseNode = []expectedParseNode{
  1945  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  1946  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
  1947  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  1948  		{Value: "var", Depth: 2, Type: ExpressionTokenLiteral},
  1949  		{Value: "and", Depth: 2, Type: ExpressionTokenLogical},
  1950  		{Value: "eq", Depth: 3, Type: ExpressionTokenLogical},
  1951  		{Value: "/", Depth: 4, Type: ExpressionTokenNav},
  1952  		{Value: "var", Depth: 5, Type: ExpressionTokenLiteral},
  1953  		{Value: "Key", Depth: 5, Type: ExpressionTokenLiteral},
  1954  		{Value: "'Site'", Depth: 4, Type: ExpressionTokenString},
  1955  		{Value: "eq", Depth: 3, Type: ExpressionTokenLogical},
  1956  		{Value: "/", Depth: 4, Type: ExpressionTokenNav},
  1957  		{Value: "var", Depth: 5, Type: ExpressionTokenLiteral},
  1958  		{Value: "Value", Depth: 5, Type: ExpressionTokenLiteral},
  1959  		{Value: "'London'", Depth: 4, Type: ExpressionTokenString},
  1960  	}
  1961  	pos := 0
  1962  	err = CompareTree(tree, expect, &pos, 0)
  1963  	if err != nil {
  1964  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  1965  	}
  1966  }
  1967  
  1968  func TestFilterLambdaNestedAny(t *testing.T) {
  1969  	input := "Enabled/any(t:t/Value eq Config/any(c:c/AdminState eq 'TRUE'))"
  1970  	ctx := context.Background()
  1971  	q, err := ParseFilterString(ctx, input)
  1972  	if err != nil {
  1973  		t.Errorf("Error parsing query %s. Error: %v", input, err)
  1974  		return
  1975  	}
  1976  	var expect []expectedParseNode = []expectedParseNode{
  1977  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  1978  		{Value: "Enabled", Depth: 1, Type: ExpressionTokenLiteral},
  1979  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  1980  		{Value: "t", Depth: 2, Type: ExpressionTokenLiteral},
  1981  		{Value: "eq", Depth: 2, Type: ExpressionTokenLogical},
  1982  		{Value: "/", Depth: 3, Type: ExpressionTokenNav},
  1983  		{Value: "t", Depth: 4, Type: ExpressionTokenLiteral},
  1984  		{Value: "Value", Depth: 4, Type: ExpressionTokenLiteral},
  1985  		{Value: "/", Depth: 3, Type: ExpressionTokenLambdaNav},
  1986  		{Value: "Config", Depth: 4, Type: ExpressionTokenLiteral},
  1987  		{Value: "any", Depth: 4, Type: ExpressionTokenLambda},
  1988  		{Value: "c", Depth: 5, Type: ExpressionTokenLiteral},
  1989  		{Value: "eq", Depth: 5, Type: ExpressionTokenLogical},
  1990  		{Value: "/", Depth: 6, Type: ExpressionTokenNav},
  1991  		{Value: "c", Depth: 7, Type: ExpressionTokenLiteral},
  1992  		{Value: "AdminState", Depth: 7, Type: ExpressionTokenLiteral},
  1993  		{Value: "'TRUE'", Depth: 6, Type: ExpressionTokenString},
  1994  	}
  1995  	pos := 0
  1996  	err = CompareTree(q.Tree, expect, &pos, 0)
  1997  	if err != nil {
  1998  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, q.Tree)
  1999  	}
  2000  }
  2001  
  2002  // TestLambdaAnyNested validates the any() lambda function with multiple nested properties.
  2003  func TestFilterLambdaAnyNestedProperties(t *testing.T) {
  2004  	ctx := context.Background()
  2005  	input := "Config/any(var:var/Config/Priority eq 123)"
  2006  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  2007  	if err != nil {
  2008  		t.Error(err)
  2009  		return
  2010  	}
  2011  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  2012  	if err != nil {
  2013  		t.Error(err)
  2014  		return
  2015  	}
  2016  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  2017  	if err != nil {
  2018  		t.Error(err)
  2019  		return
  2020  	}
  2021  
  2022  	var expect []expectedParseNode = []expectedParseNode{
  2023  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  2024  		{Value: "Config", Depth: 1, Type: ExpressionTokenLiteral},
  2025  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  2026  		{Value: "var", Depth: 2, Type: ExpressionTokenLiteral},
  2027  		{Value: "eq", Depth: 2, Type: ExpressionTokenLogical},
  2028  		{Value: "/", Depth: 3, Type: ExpressionTokenNav},
  2029  		{Value: "/", Depth: 4, Type: ExpressionTokenNav},
  2030  		{Value: "var", Depth: 5, Type: ExpressionTokenLiteral},
  2031  		{Value: "Config", Depth: 5, Type: ExpressionTokenLiteral},
  2032  		{Value: "Priority", Depth: 4, Type: ExpressionTokenLiteral},
  2033  		{Value: "123", Depth: 3, Type: ExpressionTokenInteger},
  2034  	}
  2035  	pos := 0
  2036  	err = CompareTree(tree, expect, &pos, 0)
  2037  	if err != nil {
  2038  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  2039  	}
  2040  }
  2041  
  2042  func TestFilterLambda2(t *testing.T) {
  2043  	ctx := context.Background()
  2044  	input := "Tags/any(var:var/Key eq 'Site' and var/Value eq 'London' or Price gt 1.0)"
  2045  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  2046  	if err != nil {
  2047  		t.Error(err)
  2048  		return
  2049  	}
  2050  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  2051  	if err != nil {
  2052  		t.Error(err)
  2053  		return
  2054  	}
  2055  
  2056  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  2057  	if err != nil {
  2058  		t.Error(err)
  2059  		return
  2060  	}
  2061  	var expect []expectedParseNode = []expectedParseNode{
  2062  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  2063  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
  2064  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  2065  		{Value: "var", Depth: 2, Type: ExpressionTokenLiteral},
  2066  		{Value: "or", Depth: 2, Type: ExpressionTokenLogical},
  2067  		{Value: "and", Depth: 3, Type: ExpressionTokenLogical},
  2068  		{Value: "eq", Depth: 4, Type: ExpressionTokenLogical},
  2069  		{Value: "/", Depth: 5, Type: ExpressionTokenNav},
  2070  		{Value: "var", Depth: 6, Type: ExpressionTokenLiteral},
  2071  		{Value: "Key", Depth: 6, Type: ExpressionTokenLiteral},
  2072  		{Value: "'Site'", Depth: 5, Type: ExpressionTokenString},
  2073  		{Value: "eq", Depth: 4, Type: ExpressionTokenLogical},
  2074  		{Value: "/", Depth: 5, Type: ExpressionTokenNav},
  2075  		{Value: "var", Depth: 6, Type: ExpressionTokenLiteral},
  2076  		{Value: "Value", Depth: 6, Type: ExpressionTokenLiteral},
  2077  		{Value: "'London'", Depth: 5, Type: ExpressionTokenString},
  2078  		{Value: "gt", Depth: 3, Type: ExpressionTokenLogical},
  2079  		{Value: "Price", Depth: 4, Type: ExpressionTokenLiteral},
  2080  		{Value: "1.0", Depth: 4, Type: ExpressionTokenFloat},
  2081  	}
  2082  	pos := 0
  2083  	err = CompareTree(tree, expect, &pos, 0)
  2084  	if err != nil {
  2085  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  2086  	}
  2087  }
  2088  
  2089  func TestFilterLambda3(t *testing.T) {
  2090  	ctx := context.Background()
  2091  	input := "Tags/any(var:var/Key eq 'Site' and var/Value eq 'London' or Price gt 1.0 or contains(var/Value, 'Smith'))"
  2092  	tokens, err := GlobalExpressionTokenizer.Tokenize(ctx, input)
  2093  	if err != nil {
  2094  		t.Error(err)
  2095  		return
  2096  	}
  2097  	output, err := GlobalFilterParser.InfixToPostfix(ctx, tokens)
  2098  	if err != nil {
  2099  		t.Error(err)
  2100  		return
  2101  	}
  2102  
  2103  	tree, err := GlobalFilterParser.PostfixToTree(ctx, output)
  2104  	if err != nil {
  2105  		t.Error(err)
  2106  		return
  2107  	}
  2108  	var expect []expectedParseNode = []expectedParseNode{
  2109  		{Value: "/", Depth: 0, Type: ExpressionTokenLambdaNav},
  2110  		{Value: "Tags", Depth: 1, Type: ExpressionTokenLiteral},
  2111  		{Value: "any", Depth: 1, Type: ExpressionTokenLambda},
  2112  		{Value: "var", Depth: 2, Type: ExpressionTokenLiteral},
  2113  		{Value: "or", Depth: 2, Type: ExpressionTokenLogical},
  2114  		{Value: "or", Depth: 3, Type: ExpressionTokenLogical},
  2115  		{Value: "and", Depth: 4, Type: ExpressionTokenLogical},
  2116  		{Value: "eq", Depth: 5, Type: ExpressionTokenLogical},
  2117  		{Value: "/", Depth: 6, Type: ExpressionTokenNav},
  2118  		{Value: "var", Depth: 7, Type: ExpressionTokenLiteral},
  2119  		{Value: "Key", Depth: 7, Type: ExpressionTokenLiteral},
  2120  		{Value: "'Site'", Depth: 6, Type: ExpressionTokenString},
  2121  		{Value: "eq", Depth: 5, Type: ExpressionTokenLogical},
  2122  		{Value: "/", Depth: 6, Type: ExpressionTokenNav},
  2123  		{Value: "var", Depth: 7, Type: ExpressionTokenLiteral},
  2124  		{Value: "Value", Depth: 7, Type: ExpressionTokenLiteral},
  2125  		{Value: "'London'", Depth: 6, Type: ExpressionTokenString},
  2126  		{Value: "gt", Depth: 4, Type: ExpressionTokenLogical},
  2127  		{Value: "Price", Depth: 5, Type: ExpressionTokenLiteral},
  2128  		{Value: "1.0", Depth: 5, Type: ExpressionTokenFloat},
  2129  		{Value: "contains", Depth: 3, Type: ExpressionTokenFunc},
  2130  		{Value: "/", Depth: 4, Type: ExpressionTokenNav},
  2131  		{Value: "var", Depth: 5, Type: ExpressionTokenLiteral},
  2132  		{Value: "Value", Depth: 5, Type: ExpressionTokenLiteral},
  2133  		{Value: "'Smith'", Depth: 4, Type: ExpressionTokenString},
  2134  	}
  2135  	pos := 0
  2136  	err = CompareTree(tree, expect, &pos, 0)
  2137  	if err != nil {
  2138  		t.Errorf("Tree representation does not match expected value. error: %v. Tree:\n%v", err, tree)
  2139  	}
  2140  }
  2141  
  2142  func TestExpressionTokenizerExists(t *testing.T) {
  2143  	ctx := context.Background()
  2144  	tokenizer := NewExpressionTokenizer()
  2145  	input := "exists(Name,false)"
  2146  	expect := []*Token{
  2147  		{Value: "exists", Type: ExpressionTokenFunc},
  2148  		{Value: "(", Type: ExpressionTokenOpenParen},
  2149  		{Value: "Name", Type: ExpressionTokenLiteral},
  2150  		{Value: ",", Type: ExpressionTokenComma},
  2151  		{Value: "false", Type: ExpressionTokenBoolean},
  2152  		{Value: ")", Type: ExpressionTokenCloseParen},
  2153  	}
  2154  	output, err := tokenizer.Tokenize(ctx, input)
  2155  	if err != nil {
  2156  		t.Error(err)
  2157  	}
  2158  
  2159  	result, err := CompareTokens(expect, output)
  2160  	if !result {
  2161  		t.Error(err)
  2162  	}
  2163  }