github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/flameql/parse_test.go (about) 1 package flameql 2 3 import ( 4 "errors" 5 "regexp" 6 "regexp/syntax" 7 8 . "github.com/onsi/ginkgo/v2" 9 . "github.com/onsi/gomega" 10 ) 11 12 var _ = Describe("ParseQuery", func() { 13 It("parses queries", func() { 14 type testCase struct { 15 query string 16 err error 17 q *Query 18 } 19 20 testCases := []testCase{ 21 {`app.name`, nil, &Query{AppName: "app.name", q: `app.name`}}, 22 {`app.name{}`, nil, &Query{AppName: "app.name", q: `app.name{}`}}, 23 {`app.name{foo="bar"}`, nil, 24 &Query{"app.name", []*TagMatcher{{"foo", "bar", OpEqual, nil}}, `app.name{foo="bar"}`}}, 25 {`app.name{foo="bar,baz"}`, nil, 26 &Query{"app.name", []*TagMatcher{{"foo", "bar,baz", OpEqual, nil}}, `app.name{foo="bar,baz"}`}}, 27 {`app.name{foo="bar",baz!="quo"}`, nil, 28 &Query{"app.name", []*TagMatcher{{"baz", "quo", OpNotEqual, nil}, {"foo", "bar", OpEqual, nil}}, `app.name{foo="bar",baz!="quo"}`}}, 29 30 {"", ErrAppNameIsRequired, nil}, 31 {"{}", ErrAppNameIsRequired, nil}, 32 {`app.name{,}`, ErrInvalidMatchersSyntax, nil}, 33 {`app.name[foo="bar"]`, ErrInvalidAppName, nil}, 34 {`app=name{}"`, ErrInvalidAppName, nil}, 35 {`app.name{foo="bar"`, ErrInvalidQuerySyntax, nil}, 36 {`app.name{__name__="foo"}`, ErrTagKeyReserved, nil}, 37 } 38 39 for _, tc := range testCases { 40 q, err := ParseQuery(tc.query) 41 if tc.err != nil { 42 Expect(errors.Is(err, tc.err)).To(BeTrue()) 43 } else { 44 Expect(err).To(BeNil()) 45 } 46 Expect(q).To(Equal(tc.q)) 47 } 48 }) 49 }) 50 51 var _ = Describe("ParseMatcher", func() { 52 It("parses tag matchers", func() { 53 type testCase struct { 54 expr string 55 err error 56 m *TagMatcher 57 } 58 59 testCases := []testCase{ 60 {expr: `foo="bar"`, m: &TagMatcher{"foo", "bar", OpEqual, nil}}, 61 {expr: `foo="z"`, m: &TagMatcher{"foo", "z", OpEqual, nil}}, 62 {expr: `foo=""`, err: ErrInvalidTagValueSyntax}, 63 {expr: `foo="`, err: ErrInvalidTagValueSyntax}, 64 {expr: `foo="z`, err: ErrInvalidTagValueSyntax}, 65 {expr: `foo=`, err: ErrInvalidTagValueSyntax}, 66 67 {expr: `foo!="bar"`, m: &TagMatcher{"foo", "bar", OpNotEqual, nil}}, 68 {expr: `foo!="z"`, m: &TagMatcher{"foo", "z", OpNotEqual, nil}}, 69 {expr: `foo=~""`, err: ErrInvalidTagValueSyntax}, 70 {expr: `foo=~"`, err: ErrInvalidTagValueSyntax}, 71 {expr: `foo=~"z`, err: ErrInvalidTagValueSyntax}, 72 {expr: `foo=~`, err: ErrInvalidTagValueSyntax}, 73 74 {expr: `foo=~"bar"`, m: &TagMatcher{"foo", "bar", OpEqualRegex, nil}}, 75 {expr: `foo=~"z"`, m: &TagMatcher{"foo", "z", OpEqualRegex, nil}}, 76 {expr: `foo!=""`, err: ErrInvalidTagValueSyntax}, 77 {expr: `foo!="`, err: ErrInvalidTagValueSyntax}, 78 {expr: `foo!="z`, err: ErrInvalidTagValueSyntax}, 79 {expr: `foo!=`, err: ErrInvalidTagValueSyntax}, 80 81 {expr: `foo!~"bar"`, m: &TagMatcher{"foo", "bar", OpNotEqualRegex, nil}}, 82 {expr: `foo!~"z"`, m: &TagMatcher{"foo", "z", OpNotEqualRegex, nil}}, 83 {expr: `foo!~""`, err: ErrInvalidTagValueSyntax}, 84 {expr: `foo!~"`, err: ErrInvalidTagValueSyntax}, 85 {expr: `foo!~"z`, err: ErrInvalidTagValueSyntax}, 86 {expr: `foo!~`, err: ErrInvalidTagValueSyntax}, 87 88 {expr: `foo="bar,baz"`, m: &TagMatcher{"foo", "bar,baz", OpEqual, nil}}, 89 {expr: `foo="bar\",\"baz"`, m: &TagMatcher{"foo", "bar\\\",\\\"baz", OpEqual, nil}}, 90 91 {expr: `foo;bar="baz"`, err: ErrInvalidTagKey}, 92 {expr: `foo""`, err: ErrInvalidTagKey}, 93 {expr: `foo`, err: ErrMatchOperatorIsRequired}, 94 {expr: `foo!`, err: ErrInvalidTagValueSyntax}, 95 {expr: `foo!!`, err: ErrInvalidTagValueSyntax}, 96 {expr: `foo!~@b@"`, err: ErrInvalidTagValueSyntax}, 97 {expr: `foo=bar`, err: ErrInvalidTagValueSyntax}, 98 {expr: `foo!"bar"`, err: ErrUnknownOp}, 99 {expr: `foo!!"bar"`, err: ErrUnknownOp}, 100 {expr: `foo=="bar"`, err: ErrUnknownOp}, 101 } 102 103 for _, tc := range testCases { 104 tm, err := ParseMatcher(tc.expr) 105 if tc.err != nil { 106 Expect(errors.Is(err, tc.err)).To(BeTrue()) 107 } else { 108 Expect(err).To(BeNil()) 109 } 110 if tm != nil { 111 tm.R = nil 112 } 113 Expect(tm).To(Equal(tc.m)) 114 } 115 }) 116 117 It("matchers are properly sorted by operator priority", func() { 118 m, err := ParseMatchers(`3="x",4=~"x",1!="x",2!~"x"`) 119 Expect(err).ToNot(HaveOccurred()) 120 x := regexp.MustCompile("x") 121 Expect(m).To(Equal([]*TagMatcher{ 122 {"1", "x", OpNotEqual, nil}, 123 {"2", "x", OpNotEqualRegex, x}, 124 {"3", "x", OpEqual, nil}, 125 {"4", "x", OpEqualRegex, x}, 126 })) 127 }) 128 }) 129 130 var _ = Describe("ParseMatcherRegex", func() { 131 It("parses tag matchers with regex", func() { 132 m, err := ParseMatcher(`foo=~".*_suffix"`) 133 Expect(err).To(BeNil()) 134 Expect(m).ToNot(BeNil()) 135 136 m, err = ParseMatcher(`foo=~"["`) 137 Expect(m).To(BeNil()) 138 Expect(err).ToNot(BeNil()) 139 140 var e1 *syntax.Error 141 Expect(errors.As(err, &e1)).To(BeTrue()) 142 143 var e2 *Error 144 Expect(errors.As(err, &e2)).To(BeTrue()) 145 }) 146 })