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  })