github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/logentry/logql/parser_test.go (about) 1 package logql 2 3 import ( 4 "strings" 5 "testing" 6 "text/scanner" 7 8 "github.com/prometheus/prometheus/model/labels" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func TestLex(t *testing.T) { 13 for _, tc := range []struct { 14 input string 15 expected []int 16 }{ 17 {`{foo="bar"}`, []int{OPEN_BRACE, IDENTIFIER, EQ, STRING, CLOSE_BRACE}}, 18 {`{ foo = "bar" }`, []int{OPEN_BRACE, IDENTIFIER, EQ, STRING, CLOSE_BRACE}}, 19 {`{ foo != "bar" }`, []int{OPEN_BRACE, IDENTIFIER, NEQ, STRING, CLOSE_BRACE}}, 20 {`{ foo =~ "bar" }`, []int{OPEN_BRACE, IDENTIFIER, RE, STRING, CLOSE_BRACE}}, 21 {`{ foo !~ "bar" }`, []int{OPEN_BRACE, IDENTIFIER, NRE, STRING, CLOSE_BRACE}}, 22 {`{ foo = "bar", bar != "baz" }`, []int{OPEN_BRACE, IDENTIFIER, EQ, STRING, 23 COMMA, IDENTIFIER, NEQ, STRING, CLOSE_BRACE}}, 24 {`{ foo = "ba\"r" }`, []int{OPEN_BRACE, IDENTIFIER, EQ, STRING, CLOSE_BRACE}}, 25 } { 26 t.Run(tc.input, func(t *testing.T) { 27 actual := []int{} 28 l := lexer{ 29 Scanner: scanner.Scanner{ 30 Mode: scanner.SkipComments | scanner.ScanStrings, 31 }, 32 } 33 l.Init(strings.NewReader(tc.input)) 34 var lval exprSymType 35 for { 36 tok := l.Lex(&lval) 37 if tok == 0 { 38 break 39 } 40 actual = append(actual, tok) 41 } 42 require.Equal(t, tc.expected, actual) 43 }) 44 } 45 } 46 47 func TestParse(t *testing.T) { 48 for _, tc := range []struct { 49 in string 50 exp Expr 51 err error 52 }{ 53 { 54 in: `{foo="bar"}`, 55 exp: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}}, 56 }, 57 { 58 in: `{ foo = "bar" }`, 59 exp: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}}, 60 }, 61 { 62 in: `{ foo != "bar" }`, 63 exp: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchNotEqual, "foo", "bar")}}, 64 }, 65 { 66 in: `{ foo =~ "bar" }`, 67 exp: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchRegexp, "foo", "bar")}}, 68 }, 69 { 70 in: `{ foo !~ "bar" }`, 71 exp: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchNotRegexp, "foo", "bar")}}, 72 }, 73 { 74 in: `{ foo = "bar", bar != "baz" }`, 75 exp: &matchersExpr{matchers: []*labels.Matcher{ 76 mustNewMatcher(labels.MatchEqual, "foo", "bar"), 77 mustNewMatcher(labels.MatchNotEqual, "bar", "baz"), 78 }}, 79 }, 80 { 81 in: `{foo="bar"} |= "baz"`, 82 exp: &filterExpr{ 83 left: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}}, 84 ty: labels.MatchEqual, 85 match: "baz", 86 }, 87 }, 88 { 89 in: `{foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap"`, 90 exp: &filterExpr{ 91 left: &filterExpr{ 92 left: &filterExpr{ 93 left: &filterExpr{ 94 left: &matchersExpr{matchers: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}}, 95 ty: labels.MatchEqual, 96 match: "baz", 97 }, 98 ty: labels.MatchRegexp, 99 match: "blip", 100 }, 101 ty: labels.MatchNotEqual, 102 match: "flip", 103 }, 104 ty: labels.MatchNotRegexp, 105 match: "flap", 106 }, 107 }, 108 { 109 in: `{foo="bar}`, 110 err: ParseError{ 111 msg: "literal not terminated", 112 line: 1, 113 col: 6, 114 }, 115 }, 116 { 117 in: `{foo="bar"`, 118 err: ParseError{ 119 msg: "syntax error: unexpected $end, expecting } or ,", 120 line: 1, 121 col: 11, 122 }, 123 }, 124 125 { 126 in: `{foo="bar"} |~`, 127 err: ParseError{ 128 msg: "syntax error: unexpected $end, expecting STRING", 129 line: 1, 130 col: 15, 131 }, 132 }, 133 134 { 135 in: `{foo="bar"} "foo"`, 136 err: ParseError{ 137 msg: "syntax error: unexpected STRING, expecting != or !~ or |~ or |=", 138 line: 1, 139 col: 13, 140 }, 141 }, 142 { 143 in: `{foo="bar"} foo`, 144 err: ParseError{ 145 msg: "syntax error: unexpected IDENTIFIER, expecting != or !~ or |~ or |=", 146 line: 1, 147 col: 13, 148 }, 149 }, 150 } { 151 t.Run(tc.in, func(t *testing.T) { 152 ast, err := ParseExpr(tc.in) 153 require.Equal(t, tc.err, err) 154 require.Equal(t, tc.exp, ast) 155 }) 156 } 157 }