| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | package models | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 	scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name         string | 
					
						
							|  |  |  | 		query        string | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 		adhocFilters []scope.ScopeFilter | 
					
						
							|  |  |  | 		scopeFilters []scope.ScopeFilter | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 		expected     string | 
					
						
							|  |  |  | 		expectErr    bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "No filters with no existing filter", | 
					
						
							|  |  |  | 			query:     `http_requests_total`, | 
					
						
							|  |  |  | 			expected:  `http_requests_total`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "No filters with existing filter", | 
					
						
							|  |  |  | 			query:     `http_requests_total{job="prometheus"}`, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filter with existing filter", | 
					
						
							|  |  |  | 			query: `http_requests_total{job="prometheus"}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "method", Value: "get", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",method="get"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filter with no existing filter", | 
					
						
							|  |  |  | 			query: `http_requests_total`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "method", Value: "get", Operator: scope.FilterOperatorEquals}, | 
					
						
							|  |  |  | 				{Key: "job", Value: "prometheus", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",method="get"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Scope filter", | 
					
						
							|  |  |  | 			query: `http_requests_total{job="prometheus"}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",status="200"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc and Scope filter no existing filter", | 
					
						
							|  |  |  | 			query: `http_requests_total`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "prometheus", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",status="200"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2024-09-09 21:56:43 +08:00
										 |  |  | 			name:  "Adhoc and Scope filter conflict - adhoc wins (if not oneOf or notOneOf)", | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			query: `http_requests_total{job="prometheus"}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "status", Value: "404", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "status", Value: "200", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",status="200"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filters with more complex expression", | 
					
						
							|  |  |  | 			query: `capacity_bytes{job="prometheus"} + available_bytes{job="grafana"} / 1024`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `capacity_bytes{job="alloy"} + available_bytes{job="alloy"} / 1024`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-09-09 21:56:43 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "OneOf Operator is combined into a single regex filter", | 
					
						
							|  |  |  | 			query: `http_requests_total{job="prometheus"}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "status", Values: []string{"404", "400"}, Operator: scope.FilterOperatorOneOf}, | 
					
						
							| 
									
										
										
										
											2024-09-09 21:56:43 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{job="prometheus",status=~"404|400"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-10-21 20:18:40 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "using __name__ as part of the query", | 
					
						
							|  |  |  | 			query: `{__name__="http_requests_total"}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "istio", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-10-21 20:18:40 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `{__name__="http_requests_total",namespace="istio"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-12-03 02:16:49 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "merge scopes filters into using OR if they share filter key", | 
					
						
							|  |  |  | 			query: `http_requests_total{}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "kube-system", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-12-03 02:16:49 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{namespace=~"default|kube-system"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "adhoc filters win over scope filters if they share filter key", | 
					
						
							|  |  |  | 			query: `http_requests_total{}`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "default", Operator: scope.FilterOperatorEquals}, | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "kube-system", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-12-03 02:16:49 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "namespace", Value: "adhoc-wins", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-12-03 02:16:49 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{namespace="adhoc-wins"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, tt.scopeFilters, tt.adhocFilters, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2024-12-03 02:16:49 +08:00
										 |  |  | 				require.Equal(t, tt.expected, expr, tt.name) | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | func TestApplyQueryFiltersAndGroupBy_Filters_utf8(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name         string | 
					
						
							|  |  |  | 		query        string | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 		adhocFilters []scope.ScopeFilter | 
					
						
							|  |  |  | 		scopeFilters []scope.ScopeFilter | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 		expected     string | 
					
						
							|  |  |  | 		expectErr    bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "No filters with existing utf8 filter", | 
					
						
							|  |  |  | 			query:     `http_requests_total{"job.name"="prometheus"}`, | 
					
						
							|  |  |  | 			expected:  `http_requests_total{"job.name"="prometheus"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, tt.scopeFilters, tt.adhocFilters, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				require.Equal(t, tt.expected, expr, tt.name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | func TestApplyQueryFiltersAndGroupBy_GroupBy(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name      string | 
					
						
							|  |  |  | 		query     string | 
					
						
							|  |  |  | 		groupBy   []string | 
					
						
							|  |  |  | 		expected  string | 
					
						
							|  |  |  | 		expectErr bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with no aggregate expression", | 
					
						
							|  |  |  | 			groupBy:   []string{"job"}, | 
					
						
							|  |  |  | 			query:     `http_requests_total`, | 
					
						
							|  |  |  | 			expected:  `http_requests_total`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "No GroupBy with aggregate expression", | 
					
						
							|  |  |  | 			query:     `sum by () (http_requests_total)`, | 
					
						
							|  |  |  | 			expected:  `sum(http_requests_total)`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with aggregate expression with no existing group by", | 
					
						
							|  |  |  | 			groupBy:   []string{"job"}, | 
					
						
							|  |  |  | 			query:     `sum(http_requests_total)`, | 
					
						
							|  |  |  | 			expected:  `sum by (job) (http_requests_total)`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with aggregate expression with existing group by", | 
					
						
							|  |  |  | 			groupBy:   []string{"status"}, | 
					
						
							|  |  |  | 			query:     `sum by (job) (http_requests_total)`, | 
					
						
							|  |  |  | 			expected:  `sum by (job, status) (http_requests_total)`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with aggregate expression with existing group by (already exists)", | 
					
						
							|  |  |  | 			groupBy:   []string{"job"}, | 
					
						
							|  |  |  | 			query:     `sum by (job) (http_requests_total)`, | 
					
						
							|  |  |  | 			expected:  `sum by (job) (http_requests_total)`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, nil, nil, tt.groupBy) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 				require.Equal(t, tt.expected, expr) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestApplyQueryFiltersAndGroupBy_GroupBy_utf8(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name      string | 
					
						
							|  |  |  | 		query     string | 
					
						
							|  |  |  | 		groupBy   []string | 
					
						
							|  |  |  | 		expected  string | 
					
						
							|  |  |  | 		expectErr bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with no aggregate expression and utf8 metric", | 
					
						
							|  |  |  | 			groupBy:   []string{"job"}, | 
					
						
							|  |  |  | 			query:     `{"http.requests_total"}`, | 
					
						
							|  |  |  | 			expected:  `{__name__="http.requests_total"}`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:      "GroupBy with aggregate expression with existing utf8 group by", | 
					
						
							|  |  |  | 			groupBy:   []string{"status"}, | 
					
						
							|  |  |  | 			query:     `sum by ("utf8.job") (http_requests_total)`, | 
					
						
							|  |  |  | 			expected:  `sum by ("utf8.job", status) (http_requests_total)`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, nil, nil, tt.groupBy) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				require.Equal(t, tt.expected, expr) | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestApplyQueryFiltersAndGroupBy(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name         string | 
					
						
							|  |  |  | 		query        string | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 		adhocFilters []scope.ScopeFilter | 
					
						
							|  |  |  | 		scopeFilters []scope.ScopeFilter | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 		groupby      []string | 
					
						
							|  |  |  | 		expected     string | 
					
						
							|  |  |  | 		expectErr    bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filters with more complex expression", | 
					
						
							|  |  |  | 			query: `sum(capacity_bytes{job="prometheus"} + available_bytes{job="grafana"}) / 1024`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2024-05-16 20:36:03 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			groupby:   []string{"job"}, | 
					
						
							|  |  |  | 			expected:  `sum by (job) (capacity_bytes{job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, tt.scopeFilters, tt.adhocFilters, tt.groupby) | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 				require.Equal(t, tt.expected, expr) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestApplyQueryFiltersAndGroupBy_utf8(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name         string | 
					
						
							|  |  |  | 		query        string | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 		adhocFilters []scope.ScopeFilter | 
					
						
							|  |  |  | 		scopeFilters []scope.ScopeFilter | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 		groupby      []string | 
					
						
							|  |  |  | 		expected     string | 
					
						
							|  |  |  | 		expectErr    bool | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filters with more complex expression and utf8 metric name", | 
					
						
							|  |  |  | 			query: `sum({"capacity_bytes", job="prometheus"} + {"available_bytes", job="grafana"}) / 1024`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			groupby:   []string{"job"}, | 
					
						
							|  |  |  | 			expected:  `sum by (job) ({__name__="capacity_bytes",job="alloy",vol="/"} + {__name__="available_bytes",job="alloy",vol="/"}) / 1024`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filters with more complex expression with utf8 label", | 
					
						
							|  |  |  | 			query: `sum(capacity_bytes{job="prometheus", "utf8.label"="value"} + available_bytes{job="grafana"}) / 1024`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			groupby:   []string{"job"}, | 
					
						
							|  |  |  | 			expected:  `sum by (job) (capacity_bytes{"utf8.label"="value",job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:  "Adhoc filters with more complex expression with utf8 metric and label", | 
					
						
							|  |  |  | 			query: `sum({"capacity_bytes", job="prometheus", "utf8.label"="value"} + available_bytes{job="grafana"}) / 1024`, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			adhocFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "job", Value: "alloy", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2025-10-09 16:56:12 +08:00
										 |  |  | 			scopeFilters: []scope.ScopeFilter{ | 
					
						
							|  |  |  | 				{Key: "vol", Value: "/", Operator: scope.FilterOperatorEquals}, | 
					
						
							| 
									
										
										
										
											2025-01-17 00:11:44 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			groupby:   []string{"job"}, | 
					
						
							|  |  |  | 			expected:  `sum by (job) ({"utf8.label"="value",__name__="capacity_bytes",job="alloy",vol="/"} + available_bytes{job="alloy",vol="/"}) / 1024`, | 
					
						
							|  |  |  | 			expectErr: false, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			expr, err := ApplyFiltersAndGroupBy(tt.query, tt.scopeFilters, tt.adhocFilters, tt.groupby) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if tt.expectErr { | 
					
						
							|  |  |  | 				require.Error(t, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				require.Equal(t, tt.expected, expr) | 
					
						
							| 
									
										
										
										
											2024-04-16 21:56:50 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |