github.com/safing/portbase@v0.19.5/database/query/parser_test.go (about)

     1  package query
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/davecgh/go-spew/spew"
     8  )
     9  
    10  func TestExtractSnippets(t *testing.T) {
    11  	t.Parallel()
    12  
    13  	text1 := `query test: where ( "bananas" > 100 and monkeys.# <= "12")or(coconuts < 10 "and" area > 50) or name sameas Julian or name matches ^King\ `
    14  	result1 := []*snippet{
    15  		{text: "query", globalPosition: 1},
    16  		{text: "test:", globalPosition: 7},
    17  		{text: "where", globalPosition: 13},
    18  		{text: "(", globalPosition: 19},
    19  		{text: "bananas", globalPosition: 21},
    20  		{text: ">", globalPosition: 31},
    21  		{text: "100", globalPosition: 33},
    22  		{text: "and", globalPosition: 37},
    23  		{text: "monkeys.#", globalPosition: 41},
    24  		{text: "<=", globalPosition: 51},
    25  		{text: "12", globalPosition: 54},
    26  		{text: ")", globalPosition: 58},
    27  		{text: "or", globalPosition: 59},
    28  		{text: "(", globalPosition: 61},
    29  		{text: "coconuts", globalPosition: 62},
    30  		{text: "<", globalPosition: 71},
    31  		{text: "10", globalPosition: 73},
    32  		{text: "and", globalPosition: 76},
    33  		{text: "area", globalPosition: 82},
    34  		{text: ">", globalPosition: 87},
    35  		{text: "50", globalPosition: 89},
    36  		{text: ")", globalPosition: 91},
    37  		{text: "or", globalPosition: 93},
    38  		{text: "name", globalPosition: 96},
    39  		{text: "sameas", globalPosition: 101},
    40  		{text: "Julian", globalPosition: 108},
    41  		{text: "or", globalPosition: 115},
    42  		{text: "name", globalPosition: 118},
    43  		{text: "matches", globalPosition: 123},
    44  		{text: "^King ", globalPosition: 131},
    45  	}
    46  
    47  	snippets, err := extractSnippets(text1)
    48  	if err != nil {
    49  		t.Errorf("failed to extract snippets: %s", err)
    50  	}
    51  
    52  	if !reflect.DeepEqual(result1, snippets) {
    53  		t.Errorf("unexpected results:")
    54  		for _, el := range snippets {
    55  			t.Errorf("%+v", el)
    56  		}
    57  	}
    58  
    59  	// t.Error(spew.Sprintf("%v", treeElement))
    60  }
    61  
    62  func testParsing(t *testing.T, queryText string, expectedResult *Query) {
    63  	t.Helper()
    64  
    65  	_, err := expectedResult.Check()
    66  	if err != nil {
    67  		t.Errorf("failed to create query: %s", err)
    68  		return
    69  	}
    70  
    71  	q, err := ParseQuery(queryText)
    72  	if err != nil {
    73  		t.Errorf("failed to parse query: %s", err)
    74  		return
    75  	}
    76  
    77  	if queryText != q.Print() {
    78  		t.Errorf("string match failed: %s", q.Print())
    79  		return
    80  	}
    81  	if !reflect.DeepEqual(expectedResult, q) {
    82  		t.Error("deepqual match failed.")
    83  		t.Error("got:")
    84  		t.Error(spew.Sdump(q))
    85  		t.Error("expected:")
    86  		t.Error(spew.Sdump(expectedResult))
    87  	}
    88  }
    89  
    90  func TestParseQuery(t *testing.T) {
    91  	t.Parallel()
    92  
    93  	text1 := `query test: where (bananas > 100 and monkeys.# <= 12) or not (coconuts < 10 and area not > 50) or name sameas Julian or name matches "^King " orderby name limit 10 offset 20`
    94  	result1 := New("test:").Where(Or(
    95  		And(
    96  			Where("bananas", GreaterThan, 100),
    97  			Where("monkeys.#", LessThanOrEqual, 12),
    98  		),
    99  		Not(And(
   100  			Where("coconuts", LessThan, 10),
   101  			Not(Where("area", GreaterThan, 50)),
   102  		)),
   103  		Where("name", SameAs, "Julian"),
   104  		Where("name", Matches, "^King "),
   105  	)).OrderBy("name").Limit(10).Offset(20)
   106  	testParsing(t, text1, result1)
   107  
   108  	testParsing(t, `query test: orderby name`, New("test:").OrderBy("name"))
   109  	testParsing(t, `query test: limit 10`, New("test:").Limit(10))
   110  	testParsing(t, `query test: offset 10`, New("test:").Offset(10))
   111  	testParsing(t, `query test: where banana matches ^ban`, New("test:").Where(Where("banana", Matches, "^ban")))
   112  	testParsing(t, `query test: where banana exists`, New("test:").Where(Where("banana", Exists, nil)))
   113  	testParsing(t, `query test: where banana not exists`, New("test:").Where(Not(Where("banana", Exists, nil))))
   114  
   115  	// test all operators
   116  	testParsing(t, `query test: where banana == 1`, New("test:").Where(Where("banana", Equals, 1)))
   117  	testParsing(t, `query test: where banana > 1`, New("test:").Where(Where("banana", GreaterThan, 1)))
   118  	testParsing(t, `query test: where banana >= 1`, New("test:").Where(Where("banana", GreaterThanOrEqual, 1)))
   119  	testParsing(t, `query test: where banana < 1`, New("test:").Where(Where("banana", LessThan, 1)))
   120  	testParsing(t, `query test: where banana <= 1`, New("test:").Where(Where("banana", LessThanOrEqual, 1)))
   121  	testParsing(t, `query test: where banana f== 1.1`, New("test:").Where(Where("banana", FloatEquals, 1.1)))
   122  	testParsing(t, `query test: where banana f> 1.1`, New("test:").Where(Where("banana", FloatGreaterThan, 1.1)))
   123  	testParsing(t, `query test: where banana f>= 1.1`, New("test:").Where(Where("banana", FloatGreaterThanOrEqual, 1.1)))
   124  	testParsing(t, `query test: where banana f< 1.1`, New("test:").Where(Where("banana", FloatLessThan, 1.1)))
   125  	testParsing(t, `query test: where banana f<= 1.1`, New("test:").Where(Where("banana", FloatLessThanOrEqual, 1.1)))
   126  	testParsing(t, `query test: where banana sameas banana`, New("test:").Where(Where("banana", SameAs, "banana")))
   127  	testParsing(t, `query test: where banana contains banana`, New("test:").Where(Where("banana", Contains, "banana")))
   128  	testParsing(t, `query test: where banana startswith banana`, New("test:").Where(Where("banana", StartsWith, "banana")))
   129  	testParsing(t, `query test: where banana endswith banana`, New("test:").Where(Where("banana", EndsWith, "banana")))
   130  	testParsing(t, `query test: where banana in banana,coconut`, New("test:").Where(Where("banana", In, []string{"banana", "coconut"})))
   131  	testParsing(t, `query test: where banana matches banana`, New("test:").Where(Where("banana", Matches, "banana")))
   132  	testParsing(t, `query test: where banana is true`, New("test:").Where(Where("banana", Is, true)))
   133  	testParsing(t, `query test: where banana exists`, New("test:").Where(Where("banana", Exists, nil)))
   134  
   135  	// special
   136  	testParsing(t, `query test: where banana not exists`, New("test:").Where(Not(Where("banana", Exists, nil))))
   137  }
   138  
   139  func testParseError(t *testing.T, queryText string, expectedErrorString string) {
   140  	t.Helper()
   141  
   142  	_, err := ParseQuery(queryText)
   143  	if err == nil {
   144  		t.Errorf("should fail to parse: %s", queryText)
   145  		return
   146  	}
   147  	if err.Error() != expectedErrorString {
   148  		t.Errorf("unexpected error for query: %s\nwanted: %s\n   got: %s", queryText, expectedErrorString, err)
   149  	}
   150  }
   151  
   152  func TestParseErrors(t *testing.T) {
   153  	t.Parallel()
   154  
   155  	// syntax
   156  	testParseError(t, `query`, `unexpected end at position 5`)
   157  	testParseError(t, `query test: where`, `unexpected end at position 17`)
   158  	testParseError(t, `query test: where (`, `unexpected end at position 19`)
   159  	testParseError(t, `query test: where )`, `unknown clause ")" at position 19`)
   160  	testParseError(t, `query test: where not`, `unexpected end at position 21`)
   161  	testParseError(t, `query test: where banana`, `unexpected end at position 24`)
   162  	testParseError(t, `query test: where banana >`, `unexpected end at position 26`)
   163  	testParseError(t, `query test: where banana nope`, `unknown operator at position 26`)
   164  	testParseError(t, `query test: where banana exists or`, `unexpected end at position 34`)
   165  	testParseError(t, `query test: where banana exists and`, `unexpected end at position 35`)
   166  	testParseError(t, `query test: where banana exists and (`, `unexpected end at position 37`)
   167  	testParseError(t, `query test: where banana exists and banana is true or`, `you may not mix "and" and "or" (position: 52)`)
   168  	testParseError(t, `query test: where banana exists or banana is true and`, `you may not mix "and" and "or" (position: 51)`)
   169  	// testParseError(t, `query test: where banana exists and (`, ``)
   170  
   171  	// value parsing error
   172  	testParseError(t, `query test: where banana == banana`, `could not parse banana to int64: strconv.ParseInt: parsing "banana": invalid syntax (hint: use "sameas" to compare strings)`)
   173  	testParseError(t, `query test: where banana f== banana`, `could not parse banana to float64: strconv.ParseFloat: parsing "banana": invalid syntax`)
   174  	testParseError(t, `query test: where banana in banana`, `could not parse "banana" to []string`)
   175  	testParseError(t, `query test: where banana matches [banana`, "could not compile regex \"[banana\": error parsing regexp: missing closing ]: `[banana`")
   176  	testParseError(t, `query test: where banana is great`, `could not parse "great" to bool: strconv.ParseBool: parsing "great": invalid syntax`)
   177  }