@@ -6,6 +6,12 @@ import (
66 "time"
77
88 "github.com/stretchr/testify/require"
9+
10+ "github.com/grafana/loki/v3/pkg/logproto"
11+ "github.com/grafana/loki/v3/pkg/logql"
12+ "github.com/grafana/loki/v3/pkg/logql/syntax"
13+ "github.com/grafana/loki/v3/pkg/querier/plan"
14+ "github.com/grafana/loki/v3/pkg/util/httpreq"
915)
1016
1117type fakeTimeLimits struct {
@@ -51,3 +57,118 @@ func Test_validateQueryTimeRangeLimits(t *testing.T) {
5157 })
5258 }
5359}
60+
61+ func TestValidateAggregatedMetricQuery (t * testing.T ) {
62+ makeReqAndAST := func (queryStr string ) logql.QueryParams {
63+ now := time .Now ()
64+ expr , err := syntax .ParseExpr (queryStr )
65+ if err != nil {
66+ panic (err )
67+ }
68+ switch expr .(type ) {
69+ case syntax.SampleExpr :
70+ return logql.SelectSampleParams {SampleQueryRequest : & logproto.SampleQueryRequest {
71+ Selector : queryStr ,
72+ Start : now .Add (- time .Hour ),
73+ End : now ,
74+ Plan : & plan.QueryPlan {AST : expr },
75+ },
76+ }
77+ default :
78+ return logql.SelectLogParams {QueryRequest : & logproto.QueryRequest {
79+ Selector : queryStr ,
80+ Start : now .Add (- time .Hour ),
81+ End : now ,
82+ Direction : logproto .BACKWARD ,
83+ Plan : & plan.QueryPlan {
84+ AST : expr ,
85+ },
86+ },
87+ }
88+ }
89+ }
90+
91+ tcs := []struct {
92+ desc string
93+ req logql.QueryParams
94+ queryTags string
95+ expectedError error
96+ }{
97+ {
98+ desc : "normal query, no error" ,
99+ req : makeReqAndAST (`{foo="bar"}` ),
100+ queryTags : "" ,
101+ expectedError : nil ,
102+ },
103+ {
104+ desc : "aggregated metric query from explore, no error" ,
105+ req : makeReqAndAST (`{__aggregated_metric__="service-name"}` ),
106+ queryTags : "source=" + logsDrilldownAppName ,
107+ expectedError : nil ,
108+ },
109+ {
110+ desc : "query tags are case insensitive" ,
111+ req : makeReqAndAST (`{__aggregated_metric__="service-name"}` ),
112+ queryTags : "Source=" + logsDrilldownAppName ,
113+ expectedError : nil ,
114+ },
115+ {
116+ desc : "aggregated metric query from explore, multiple selectors, no error" ,
117+ req : makeReqAndAST (`{app="service-name", __aggregated_metric__="true"}` ),
118+ queryTags : "source=" + logsDrilldownAppName ,
119+ expectedError : nil ,
120+ },
121+ {
122+ desc : "aggregated metric query from explore, multiple selectors, filter, no error" ,
123+ req : makeReqAndAST (`{app="service-name", __aggregated_metric__="true"} |= "test"` ),
124+ queryTags : "source=" + logsDrilldownAppName ,
125+ expectedError : nil ,
126+ },
127+ {
128+ desc : "aggregated metrics metric query from explore, multiple selectors, filter, no error" ,
129+ req : makeReqAndAST (`sum by (service_name)(count_over_time({app="service-name", __aggregated_metric__="true"} |= "test" [5m]))` ),
130+ queryTags : "source=" + logsDrilldownAppName ,
131+ expectedError : nil ,
132+ },
133+ {
134+ desc : "aggregated metric query from other source, blocked" ,
135+ req : makeReqAndAST (`{__aggregated_metric__="service-name"}` ),
136+ queryTags : "source=other-app" ,
137+ expectedError : ErrAggMetricsDrilldownOnly ,
138+ },
139+ {
140+ desc : "aggregated metric query with no source, blocked" ,
141+ req : makeReqAndAST (`{__aggregated_metric__="service-name"}` ),
142+ queryTags : "" ,
143+ expectedError : ErrAggMetricsDrilldownOnly ,
144+ },
145+ {
146+ desc : "aggregated metric query with no source, multiple selectors, blocked" ,
147+ req : makeReqAndAST (`{app="service-name", __aggregated_metric__="true"}` ),
148+ queryTags : "" ,
149+ expectedError : ErrAggMetricsDrilldownOnly ,
150+ },
151+ {
152+ desc : "aggregated metrics metric query with no source, multiple selectors, filter, blocked" ,
153+ req : makeReqAndAST (`sum by (service_name)(count_over_time({app="service-name", __aggregated_metric__="true"} |= "test" [5m]))` ),
154+ queryTags : "" ,
155+ expectedError : ErrAggMetricsDrilldownOnly ,
156+ },
157+ }
158+
159+ for _ , tc := range tcs {
160+ t .Run (tc .desc , func (t * testing.T ) {
161+ ctx := context .Background ()
162+ if tc .queryTags != "" {
163+ ctx = httpreq .InjectQueryTags (ctx , tc .queryTags )
164+ }
165+
166+ err := ValidateAggregatedMetricQuery (ctx , tc .req )
167+ if tc .expectedError != nil {
168+ require .ErrorIs (t , err , tc .expectedError )
169+ } else {
170+ require .NoError (t , err )
171+ }
172+ })
173+ }
174+ }
0 commit comments