github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/roundtrip_test.go (about)

     1  package queryrange
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io/ioutil"
     7  	"math"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"net/url"
    11  	"strconv"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/prometheus/prometheus/model/labels"
    17  	"github.com/prometheus/prometheus/promql"
    18  	"github.com/prometheus/prometheus/promql/parser"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"github.com/weaveworks/common/httpgrpc"
    22  	"github.com/weaveworks/common/middleware"
    23  	"github.com/weaveworks/common/user"
    24  
    25  	"github.com/grafana/loki/pkg/logproto"
    26  	"github.com/grafana/loki/pkg/logqlmodel"
    27  	"github.com/grafana/loki/pkg/querier/queryrange/queryrangebase"
    28  	"github.com/grafana/loki/pkg/storage/chunk/cache"
    29  	"github.com/grafana/loki/pkg/storage/config"
    30  	util_log "github.com/grafana/loki/pkg/util/log"
    31  	"github.com/grafana/loki/pkg/util/marshal"
    32  )
    33  
    34  var (
    35  	testTime   = time.Date(2019, 12, 2, 11, 10, 10, 10, time.UTC)
    36  	testConfig = Config{queryrangebase.Config{
    37  		AlignQueriesWithStep: true,
    38  		MaxRetries:           3,
    39  		CacheResults:         true,
    40  		ResultsCacheConfig: queryrangebase.ResultsCacheConfig{
    41  			CacheConfig: cache.Config{
    42  				EnableFifoCache: true,
    43  				Fifocache: cache.FifoCacheConfig{
    44  					MaxSizeItems: 1024,
    45  					TTL:          24 * time.Hour,
    46  				},
    47  			},
    48  		},
    49  	}}
    50  	matrix = promql.Matrix{
    51  		{
    52  			Points: []promql.Point{
    53  				{
    54  					T: toMs(testTime.Add(-4 * time.Hour)),
    55  					V: 0.013333333333333334,
    56  				},
    57  			},
    58  			Metric: []labels.Label{
    59  				{
    60  					Name:  "filename",
    61  					Value: `/var/hostlog/apport.log`,
    62  				},
    63  				{
    64  					Name:  "job",
    65  					Value: "varlogs",
    66  				},
    67  			},
    68  		},
    69  	}
    70  	vector = promql.Vector{
    71  		{
    72  			Point: promql.Point{
    73  				T: toMs(testTime.Add(-4 * time.Hour)),
    74  				V: 0.013333333333333334,
    75  			},
    76  			Metric: []labels.Label{
    77  				{
    78  					Name:  "filename",
    79  					Value: `/var/hostlog/apport.log`,
    80  				},
    81  				{
    82  					Name:  "job",
    83  					Value: "varlogs",
    84  				},
    85  			},
    86  		},
    87  	}
    88  	streams = logqlmodel.Streams{
    89  		{
    90  			Entries: []logproto.Entry{
    91  				{Timestamp: testTime.Add(-4 * time.Hour), Line: "foo"},
    92  				{Timestamp: testTime.Add(-1 * time.Hour), Line: "barr"},
    93  			},
    94  			Labels: `{filename="/var/hostlog/apport.log", job="varlogs"}`,
    95  		},
    96  	}
    97  
    98  	series = logproto.SeriesResponse{
    99  		Series: []logproto.SeriesIdentifier{
   100  			{
   101  				Labels: map[string]string{"filename": "/var/hostlog/apport.log", "job": "varlogs"},
   102  			},
   103  			{
   104  				Labels: map[string]string{"filename": "/var/hostlog/test.log", "job": "varlogs"},
   105  			},
   106  		},
   107  	}
   108  )
   109  
   110  // those tests are mostly for testing the glue between all component and make sure they activate correctly.
   111  func TestMetricsTripperware(t *testing.T) {
   112  	l := WithSplitByLimits(fakeLimits{maxSeries: math.MaxInt32, maxQueryParallelism: 1}, 4*time.Hour)
   113  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, l, config.SchemaConfig{}, nil, nil)
   114  	if stopper != nil {
   115  		defer stopper.Stop()
   116  	}
   117  	require.NoError(t, err)
   118  
   119  	lreq := &LokiRequest{
   120  		Query:     `rate({app="foo"} |= "foo"[1m])`,
   121  		Limit:     1000,
   122  		Step:      30000, // 30sec
   123  		StartTs:   testTime.Add(-6 * time.Hour),
   124  		EndTs:     testTime,
   125  		Direction: logproto.FORWARD,
   126  		Path:      "/query_range",
   127  	}
   128  
   129  	ctx := user.InjectOrgID(context.Background(), "1")
   130  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   131  	require.NoError(t, err)
   132  
   133  	req = req.WithContext(ctx)
   134  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   135  	require.NoError(t, err)
   136  	rt, err := newfakeRoundTripper()
   137  	require.NoError(t, err)
   138  
   139  	// testing retry
   140  	retries, h := counter()
   141  	rt.setHandler(h)
   142  	_, err = tpw(rt).RoundTrip(req)
   143  	// 3 retries configured.
   144  	require.GreaterOrEqual(t, *retries, 3)
   145  	require.Error(t, err)
   146  	rt.Close()
   147  
   148  	rt, err = newfakeRoundTripper()
   149  	require.NoError(t, err)
   150  	defer rt.Close()
   151  
   152  	// testing split interval
   153  	count, h := promqlResult(matrix)
   154  	rt.setHandler(h)
   155  	resp, err := tpw(rt).RoundTrip(req)
   156  	// 2 queries
   157  	require.Equal(t, 2, *count)
   158  	require.NoError(t, err)
   159  	lokiResponse, err := LokiCodec.DecodeResponse(ctx, resp, lreq)
   160  	require.NoError(t, err)
   161  
   162  	// testing cache
   163  	count, h = counter()
   164  	rt.setHandler(h)
   165  	cacheResp, err := tpw(rt).RoundTrip(req)
   166  	// 0 queries result are cached.
   167  	require.Equal(t, 0, *count)
   168  	require.NoError(t, err)
   169  	lokiCacheResponse, err := LokiCodec.DecodeResponse(ctx, cacheResp, lreq)
   170  	require.NoError(t, err)
   171  
   172  	require.Equal(t, lokiResponse.(*LokiPromResponse).Response, lokiCacheResponse.(*LokiPromResponse).Response)
   173  }
   174  
   175  func TestLogFilterTripperware(t *testing.T) {
   176  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{maxQueryParallelism: 1}, config.SchemaConfig{}, nil, nil)
   177  	if stopper != nil {
   178  		defer stopper.Stop()
   179  	}
   180  	require.NoError(t, err)
   181  	rt, err := newfakeRoundTripper()
   182  	require.NoError(t, err)
   183  	defer rt.Close()
   184  
   185  	lreq := &LokiRequest{
   186  		Query:     `{app="foo"} |= "foo"`,
   187  		Limit:     1000,
   188  		StartTs:   testTime.Add(-10 * time.Hour), // bigger than the limit
   189  		EndTs:     testTime,
   190  		Direction: logproto.FORWARD,
   191  		Path:      "/loki/api/v1/query_range",
   192  	}
   193  
   194  	ctx := user.InjectOrgID(context.Background(), "1")
   195  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   196  	require.NoError(t, err)
   197  
   198  	req = req.WithContext(ctx)
   199  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   200  	require.NoError(t, err)
   201  
   202  	// testing limit
   203  	count, h := promqlResult(streams)
   204  	rt.setHandler(h)
   205  	_, err = tpw(rt).RoundTrip(req)
   206  	require.Equal(t, 0, *count)
   207  	require.Error(t, err)
   208  
   209  	// set the query length back to normal
   210  	lreq.StartTs = testTime.Add(-6 * time.Hour)
   211  	req, err = LokiCodec.EncodeRequest(ctx, lreq)
   212  	require.NoError(t, err)
   213  
   214  	// testing retry
   215  	retries, h := counter()
   216  	rt.setHandler(h)
   217  	_, err = tpw(rt).RoundTrip(req)
   218  	require.GreaterOrEqual(t, *retries, 3)
   219  	require.Error(t, err)
   220  }
   221  
   222  func TestInstantQueryTripperware(t *testing.T) {
   223  	testShardingConfig := testConfig
   224  	testShardingConfig.ShardedQueries = true
   225  	tpw, stopper, err := NewTripperware(testShardingConfig, util_log.Logger, fakeLimits{maxQueryParallelism: 1}, config.SchemaConfig{}, nil, nil)
   226  	if stopper != nil {
   227  		defer stopper.Stop()
   228  	}
   229  	require.NoError(t, err)
   230  	rt, err := newfakeRoundTripper()
   231  	require.NoError(t, err)
   232  	defer rt.Close()
   233  
   234  	lreq := &LokiInstantRequest{
   235  		Query:     `sum by (job) (bytes_rate({cluster="dev-us-central-0"}[15m]))`,
   236  		Limit:     1000,
   237  		Direction: logproto.FORWARD,
   238  		Path:      "/loki/api/v1/query",
   239  	}
   240  
   241  	ctx := user.InjectOrgID(context.Background(), "1")
   242  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   243  	require.NoError(t, err)
   244  
   245  	req = req.WithContext(ctx)
   246  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   247  	require.NoError(t, err)
   248  
   249  	count, h := promqlResult(vector)
   250  	rt.setHandler(h)
   251  	resp, err := tpw(rt).RoundTrip(req)
   252  	require.Equal(t, 1, *count)
   253  	require.NoError(t, err)
   254  
   255  	lokiResponse, err := LokiCodec.DecodeResponse(ctx, resp, lreq)
   256  	require.NoError(t, err)
   257  	require.IsType(t, &LokiPromResponse{}, lokiResponse)
   258  }
   259  
   260  func TestSeriesTripperware(t *testing.T) {
   261  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{maxQueryLength: 48 * time.Hour, maxQueryParallelism: 1}, config.SchemaConfig{}, nil, nil)
   262  	if stopper != nil {
   263  		defer stopper.Stop()
   264  	}
   265  	require.NoError(t, err)
   266  	rt, err := newfakeRoundTripper()
   267  	require.NoError(t, err)
   268  	defer rt.Close()
   269  
   270  	lreq := &LokiSeriesRequest{
   271  		Match:   []string{`{job="varlogs"}`},
   272  		StartTs: testTime.Add(-25 * time.Hour), // bigger than split by interval limit
   273  		EndTs:   testTime,
   274  		Path:    "/loki/api/v1/series",
   275  	}
   276  
   277  	ctx := user.InjectOrgID(context.Background(), "1")
   278  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   279  	require.NoError(t, err)
   280  
   281  	req = req.WithContext(ctx)
   282  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   283  	require.NoError(t, err)
   284  
   285  	count, h := seriesResult(series)
   286  	rt.setHandler(h)
   287  	resp, err := tpw(rt).RoundTrip(req)
   288  	// 2 queries
   289  	require.Equal(t, 2, *count)
   290  	require.NoError(t, err)
   291  	lokiSeriesResponse, err := LokiCodec.DecodeResponse(ctx, resp, lreq)
   292  	res, ok := lokiSeriesResponse.(*LokiSeriesResponse)
   293  	require.Equal(t, true, ok)
   294  
   295  	// make sure we return unique series since responses from
   296  	// SplitByInterval middleware might have duplicate series
   297  	require.Equal(t, series.Series, res.Data)
   298  	require.NoError(t, err)
   299  }
   300  
   301  func TestLabelsTripperware(t *testing.T) {
   302  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{maxQueryLength: 48 * time.Hour, maxQueryParallelism: 1}, config.SchemaConfig{}, nil, nil)
   303  	if stopper != nil {
   304  		defer stopper.Stop()
   305  	}
   306  	require.NoError(t, err)
   307  	rt, err := newfakeRoundTripper()
   308  	require.NoError(t, err)
   309  	defer rt.Close()
   310  
   311  	lreq := &LokiLabelNamesRequest{
   312  		StartTs: testTime.Add(-25 * time.Hour), // bigger than the limit
   313  		EndTs:   testTime,
   314  		Path:    "/loki/api/v1/labels",
   315  	}
   316  
   317  	ctx := user.InjectOrgID(context.Background(), "1")
   318  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   319  	require.NoError(t, err)
   320  
   321  	req = req.WithContext(ctx)
   322  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   323  	require.NoError(t, err)
   324  
   325  	handler := newFakeHandler(
   326  		// we expect 2 calls.
   327  		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   328  			require.NoError(t, marshal.WriteLabelResponseJSON(logproto.LabelResponse{Values: []string{"foo", "bar", "blop"}}, w))
   329  		}),
   330  		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   331  			require.NoError(t, marshal.WriteLabelResponseJSON(logproto.LabelResponse{Values: []string{"foo", "bar", "blip"}}, w))
   332  		}),
   333  	)
   334  	rt.setHandler(handler)
   335  	resp, err := tpw(rt).RoundTrip(req)
   336  	// verify 2 calls have been made to downstream.
   337  	require.Equal(t, 2, handler.count)
   338  	require.NoError(t, err)
   339  	lokiLabelsResponse, err := LokiCodec.DecodeResponse(ctx, resp, lreq)
   340  	res, ok := lokiLabelsResponse.(*LokiLabelNamesResponse)
   341  	require.Equal(t, true, ok)
   342  	require.Equal(t, []string{"foo", "bar", "blop", "blip"}, res.Data)
   343  	require.Equal(t, "success", res.Status)
   344  	require.NoError(t, err)
   345  }
   346  
   347  func TestLogNoRegex(t *testing.T) {
   348  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{}, config.SchemaConfig{}, nil, nil)
   349  	if stopper != nil {
   350  		defer stopper.Stop()
   351  	}
   352  	require.NoError(t, err)
   353  	rt, err := newfakeRoundTripper()
   354  	require.NoError(t, err)
   355  	defer rt.Close()
   356  
   357  	lreq := &LokiRequest{
   358  		Query:     `{app="foo"}`, // no regex so it should go to the querier
   359  		Limit:     1000,
   360  		StartTs:   testTime.Add(-6 * time.Hour),
   361  		EndTs:     testTime,
   362  		Direction: logproto.FORWARD,
   363  		Path:      "/loki/api/v1/query_range",
   364  	}
   365  
   366  	ctx := user.InjectOrgID(context.Background(), "1")
   367  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   368  	require.NoError(t, err)
   369  
   370  	req = req.WithContext(ctx)
   371  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   372  	require.NoError(t, err)
   373  
   374  	count, h := promqlResult(streams)
   375  	rt.setHandler(h)
   376  	_, err = tpw(rt).RoundTrip(req)
   377  	require.Equal(t, 1, *count)
   378  	require.NoError(t, err)
   379  }
   380  
   381  func TestRegexpParamsSupport(t *testing.T) {
   382  	l := WithSplitByLimits(fakeLimits{maxSeries: 1, maxQueryParallelism: 2}, 4*time.Hour)
   383  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, l, config.SchemaConfig{}, nil, nil)
   384  	if stopper != nil {
   385  		defer stopper.Stop()
   386  	}
   387  	require.NoError(t, err)
   388  	rt, err := newfakeRoundTripper()
   389  	require.NoError(t, err)
   390  	defer rt.Close()
   391  
   392  	lreq := &LokiRequest{
   393  		Query:     `{app="foo"}`, // no regex so it should go to the querier
   394  		Limit:     1000,
   395  		StartTs:   testTime.Add(-6 * time.Hour),
   396  		EndTs:     testTime,
   397  		Direction: logproto.FORWARD,
   398  		Path:      "/loki/api/v1/query_range",
   399  	}
   400  
   401  	ctx := user.InjectOrgID(context.Background(), "1")
   402  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   403  	require.NoError(t, err)
   404  
   405  	// fudge a regexp params
   406  	params := req.URL.Query()
   407  	params.Set("regexp", "foo")
   408  	req.URL.RawQuery = params.Encode()
   409  
   410  	req = req.WithContext(ctx)
   411  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   412  	require.NoError(t, err)
   413  
   414  	count, h := promqlResult(streams)
   415  	rt.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   416  		// the query params should contain the filter.
   417  		require.Contains(t, r.URL.Query().Get("query"), `|~ "foo"`)
   418  		h.ServeHTTP(rw, r)
   419  	}))
   420  	_, err = tpw(rt).RoundTrip(req)
   421  	require.Equal(t, 2, *count) // expecting the query to also be splitted since it has a filter.
   422  	require.NoError(t, err)
   423  }
   424  
   425  func TestPostQueries(t *testing.T) {
   426  	req, err := http.NewRequest(http.MethodPost, "/loki/api/v1/query_range", nil)
   427  	data := url.Values{
   428  		"query": {`{app="foo"} |~ "foo"`},
   429  	}
   430  	body := bytes.NewBufferString(data.Encode())
   431  	req.Body = ioutil.NopCloser(body)
   432  	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
   433  	req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
   434  	req = req.WithContext(user.InjectOrgID(context.Background(), "1"))
   435  	require.NoError(t, err)
   436  	_, err = newRoundTripper(
   437  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   438  			t.Error("unexpected default roundtripper called")
   439  			return nil, nil
   440  		}),
   441  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   442  			return nil, nil
   443  		}),
   444  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   445  			t.Error("unexpected metric roundtripper called")
   446  			return nil, nil
   447  		}),
   448  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   449  			t.Error("unexpected series roundtripper called")
   450  			return nil, nil
   451  		}),
   452  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   453  			t.Error("unexpected labels roundtripper called")
   454  			return nil, nil
   455  		}),
   456  		queryrangebase.RoundTripFunc(func(*http.Request) (*http.Response, error) {
   457  			t.Error("unexpected instant roundtripper called")
   458  			return nil, nil
   459  		}),
   460  		fakeLimits{},
   461  	).RoundTrip(req)
   462  	require.NoError(t, err)
   463  }
   464  
   465  func TestEntriesLimitsTripperware(t *testing.T) {
   466  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{maxEntriesLimitPerQuery: 5000}, config.SchemaConfig{}, nil, nil)
   467  	if stopper != nil {
   468  		defer stopper.Stop()
   469  	}
   470  	require.NoError(t, err)
   471  	rt, err := newfakeRoundTripper()
   472  	require.NoError(t, err)
   473  	defer rt.Close()
   474  
   475  	lreq := &LokiRequest{
   476  		Query:     `{app="foo"}`, // no regex so it should go to the querier
   477  		Limit:     10000,
   478  		StartTs:   testTime.Add(-6 * time.Hour),
   479  		EndTs:     testTime,
   480  		Direction: logproto.FORWARD,
   481  		Path:      "/loki/api/v1/query_range",
   482  	}
   483  
   484  	ctx := user.InjectOrgID(context.Background(), "1")
   485  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   486  	require.NoError(t, err)
   487  
   488  	req = req.WithContext(ctx)
   489  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   490  	require.NoError(t, err)
   491  
   492  	_, err = tpw(rt).RoundTrip(req)
   493  	require.Equal(t, httpgrpc.Errorf(http.StatusBadRequest, "max entries limit per query exceeded, limit > max_entries_limit (10000 > 5000)"), err)
   494  }
   495  
   496  func TestEntriesLimitWithZeroTripperware(t *testing.T) {
   497  	tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{}, config.SchemaConfig{}, nil, nil)
   498  	if stopper != nil {
   499  		defer stopper.Stop()
   500  	}
   501  	require.NoError(t, err)
   502  	rt, err := newfakeRoundTripper()
   503  	require.NoError(t, err)
   504  	defer rt.Close()
   505  
   506  	lreq := &LokiRequest{
   507  		Query:     `{app="foo"}`, // no regex so it should go to the querier
   508  		Limit:     10000,
   509  		StartTs:   testTime.Add(-6 * time.Hour),
   510  		EndTs:     testTime,
   511  		Direction: logproto.FORWARD,
   512  		Path:      "/loki/api/v1/query_range",
   513  	}
   514  
   515  	ctx := user.InjectOrgID(context.Background(), "1")
   516  	req, err := LokiCodec.EncodeRequest(ctx, lreq)
   517  	require.NoError(t, err)
   518  
   519  	req = req.WithContext(ctx)
   520  	err = user.InjectOrgIDIntoHTTPRequest(ctx, req)
   521  	require.NoError(t, err)
   522  
   523  	_, err = tpw(rt).RoundTrip(req)
   524  	require.NoError(t, err)
   525  }
   526  
   527  func Test_getOperation(t *testing.T) {
   528  	cases := []struct {
   529  		name       string
   530  		path       string
   531  		expectedOp string
   532  	}{
   533  		{
   534  			name:       "instant_query",
   535  			path:       "/loki/api/v1/query",
   536  			expectedOp: InstantQueryOp,
   537  		},
   538  		{
   539  			name:       "range_query_prom",
   540  			path:       "/prom/query",
   541  			expectedOp: QueryRangeOp,
   542  		},
   543  		{
   544  			name:       "range_query",
   545  			path:       "/loki/api/v1/query_range",
   546  			expectedOp: QueryRangeOp,
   547  		},
   548  		{
   549  			name:       "series_query",
   550  			path:       "/loki/api/v1/series",
   551  			expectedOp: SeriesOp,
   552  		},
   553  		{
   554  			name:       "series_query_prom",
   555  			path:       "/prom/series",
   556  			expectedOp: SeriesOp,
   557  		},
   558  		{
   559  			name:       "labels_query",
   560  			path:       "/loki/api/v1/labels",
   561  			expectedOp: LabelNamesOp,
   562  		},
   563  		{
   564  			name:       "labels_query_prom",
   565  			path:       "/prom/labels",
   566  			expectedOp: LabelNamesOp,
   567  		},
   568  		{
   569  			name:       "label_query",
   570  			path:       "/loki/api/v1/label",
   571  			expectedOp: LabelNamesOp,
   572  		},
   573  		{
   574  			name:       "labels_query_prom",
   575  			path:       "/prom/label",
   576  			expectedOp: LabelNamesOp,
   577  		},
   578  		{
   579  			name:       "label_values_query",
   580  			path:       "/loki/api/v1/label/__name__/values",
   581  			expectedOp: LabelNamesOp,
   582  		},
   583  		{
   584  			name:       "label_values_query_prom",
   585  			path:       "/prom/label/__name__/values",
   586  			expectedOp: LabelNamesOp,
   587  		},
   588  	}
   589  
   590  	for _, tc := range cases {
   591  		t.Run(tc.name, func(t *testing.T) {
   592  			got := getOperation(tc.path)
   593  			assert.Equal(t, tc.expectedOp, got)
   594  		})
   595  	}
   596  }
   597  
   598  type fakeLimits struct {
   599  	maxQueryLength          time.Duration
   600  	maxQueryParallelism     int
   601  	maxQueryLookback        time.Duration
   602  	maxEntriesLimitPerQuery int
   603  	maxSeries               int
   604  	splits                  map[string]time.Duration
   605  	minShardingLookback     time.Duration
   606  }
   607  
   608  func (f fakeLimits) QuerySplitDuration(key string) time.Duration {
   609  	if f.splits == nil {
   610  		return 0
   611  	}
   612  	return f.splits[key]
   613  }
   614  
   615  func (f fakeLimits) MaxQueryLength(string) time.Duration {
   616  	if f.maxQueryLength == 0 {
   617  		return time.Hour * 7
   618  	}
   619  	return f.maxQueryLength
   620  }
   621  
   622  func (f fakeLimits) MaxQueryParallelism(string) int {
   623  	return f.maxQueryParallelism
   624  }
   625  
   626  func (f fakeLimits) MaxEntriesLimitPerQuery(string) int {
   627  	return f.maxEntriesLimitPerQuery
   628  }
   629  
   630  func (f fakeLimits) MaxQuerySeries(string) int {
   631  	return f.maxSeries
   632  }
   633  
   634  func (f fakeLimits) MaxCacheFreshness(string) time.Duration {
   635  	return 1 * time.Minute
   636  }
   637  
   638  func (f fakeLimits) MaxQueryLookback(string) time.Duration {
   639  	return f.maxQueryLookback
   640  }
   641  
   642  func (f fakeLimits) MinShardingLookback(string) time.Duration {
   643  	return f.minShardingLookback
   644  }
   645  
   646  func counter() (*int, http.Handler) {
   647  	count := 0
   648  	var lock sync.Mutex
   649  	return &count, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   650  		lock.Lock()
   651  		defer lock.Unlock()
   652  		count++
   653  	})
   654  }
   655  
   656  func promqlResult(v parser.Value) (*int, http.Handler) {
   657  	count := 0
   658  	var lock sync.Mutex
   659  	return &count, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   660  		lock.Lock()
   661  		defer lock.Unlock()
   662  		if err := marshal.WriteQueryResponseJSON(logqlmodel.Result{Data: v}, w); err != nil {
   663  			panic(err)
   664  		}
   665  		count++
   666  	})
   667  }
   668  
   669  func seriesResult(v logproto.SeriesResponse) (*int, http.Handler) {
   670  	count := 0
   671  	var lock sync.Mutex
   672  	return &count, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   673  		lock.Lock()
   674  		defer lock.Unlock()
   675  		if err := marshal.WriteSeriesResponseJSON(v, w); err != nil {
   676  			panic(err)
   677  		}
   678  		count++
   679  	})
   680  }
   681  
   682  type fakeHandler struct {
   683  	count int
   684  	lock  sync.Mutex
   685  	calls []http.Handler
   686  }
   687  
   688  func newFakeHandler(calls ...http.Handler) *fakeHandler {
   689  	return &fakeHandler{calls: calls}
   690  }
   691  
   692  func (f *fakeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   693  	f.lock.Lock()
   694  	defer f.lock.Unlock()
   695  	f.calls[f.count].ServeHTTP(w, req)
   696  	f.count++
   697  }
   698  
   699  type fakeRoundTripper struct {
   700  	*httptest.Server
   701  	host string
   702  }
   703  
   704  func newfakeRoundTripper() (*fakeRoundTripper, error) {
   705  	s := httptest.NewServer(nil)
   706  	u, err := url.Parse(s.URL)
   707  	if err != nil {
   708  		return nil, err
   709  	}
   710  	return &fakeRoundTripper{
   711  		Server: s,
   712  		host:   u.Host,
   713  	}, nil
   714  }
   715  
   716  func (s *fakeRoundTripper) setHandler(h http.Handler) {
   717  	s.Config.Handler = middleware.AuthenticateUser.Wrap(h)
   718  }
   719  
   720  func (s fakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
   721  	r.URL.Scheme = "http"
   722  	r.URL.Host = s.host
   723  	return http.DefaultTransport.RoundTrip(r)
   724  }
   725  
   726  func toMs(t time.Time) int64 {
   727  	return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
   728  }