github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logcli/query/query_test.go (about)

     1  package query
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/go-kit/log"
    14  	"github.com/gorilla/websocket"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/grafana/loki/pkg/logcli/output"
    19  	"github.com/grafana/loki/pkg/loghttp"
    20  	"github.com/grafana/loki/pkg/logproto"
    21  	"github.com/grafana/loki/pkg/logql"
    22  	"github.com/grafana/loki/pkg/loki"
    23  	"github.com/grafana/loki/pkg/storage"
    24  	"github.com/grafana/loki/pkg/storage/chunk/client/local"
    25  	"github.com/grafana/loki/pkg/storage/config"
    26  	"github.com/grafana/loki/pkg/storage/stores/indexshipper"
    27  	"github.com/grafana/loki/pkg/storage/stores/shipper"
    28  	"github.com/grafana/loki/pkg/util/marshal"
    29  )
    30  
    31  func Test_commonLabels(t *testing.T) {
    32  	type args struct {
    33  		lss []loghttp.LabelSet
    34  	}
    35  	tests := []struct {
    36  		name string
    37  		args args
    38  		want loghttp.LabelSet
    39  	}{
    40  		{
    41  			"Extract common labels source > target",
    42  			args{
    43  				[]loghttp.LabelSet{mustParseLabels(t, `{foo="bar", bar="foo"}`), mustParseLabels(t, `{bar="foo", foo="foo", baz="baz"}`)},
    44  			},
    45  			mustParseLabels(t, `{bar="foo"}`),
    46  		},
    47  		{
    48  			"Extract common labels source > target",
    49  			args{
    50  				[]loghttp.LabelSet{mustParseLabels(t, `{foo="bar", bar="foo"}`), mustParseLabels(t, `{bar="foo", foo="bar", baz="baz"}`)},
    51  			},
    52  			mustParseLabels(t, `{foo="bar", bar="foo"}`),
    53  		},
    54  		{
    55  			"Extract common labels source < target",
    56  			args{
    57  				[]loghttp.LabelSet{mustParseLabels(t, `{foo="bar", bar="foo"}`), mustParseLabels(t, `{bar="foo"}`)},
    58  			},
    59  			mustParseLabels(t, `{bar="foo"}`),
    60  		},
    61  		{
    62  			"Extract common labels source < target no common",
    63  			args{
    64  				[]loghttp.LabelSet{mustParseLabels(t, `{foo="bar", bar="foo"}`), mustParseLabels(t, `{fo="bar"}`)},
    65  			},
    66  			loghttp.LabelSet{},
    67  		},
    68  		{
    69  			"Extract common labels source = target no common",
    70  			args{
    71  				[]loghttp.LabelSet{mustParseLabels(t, `{foo="bar"}`), mustParseLabels(t, `{fooo="bar"}`)},
    72  			},
    73  			loghttp.LabelSet{},
    74  		},
    75  	}
    76  	for _, tt := range tests {
    77  		t.Run(tt.name, func(t *testing.T) {
    78  			var streams []loghttp.Stream
    79  
    80  			for _, lss := range tt.args.lss {
    81  				streams = append(streams, loghttp.Stream{
    82  					Entries: nil,
    83  					Labels:  lss,
    84  				})
    85  			}
    86  
    87  			if got := commonLabels(streams); !reflect.DeepEqual(got, tt.want) {
    88  				t.Errorf("commonLabels() = %v, want %v", got, tt.want)
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  func Test_subtract(t *testing.T) {
    95  	type args struct {
    96  		a loghttp.LabelSet
    97  		b loghttp.LabelSet
    98  	}
    99  	tests := []struct {
   100  		name string
   101  		args args
   102  		want loghttp.LabelSet
   103  	}{
   104  		{
   105  			"Subtract labels source > target",
   106  			args{
   107  				mustParseLabels(t, `{foo="bar", bar="foo"}`),
   108  				mustParseLabels(t, `{bar="foo", foo="foo", baz="baz"}`),
   109  			},
   110  			mustParseLabels(t, `{foo="bar"}`),
   111  		},
   112  		{
   113  			"Subtract labels source < target",
   114  			args{
   115  				mustParseLabels(t, `{foo="bar", bar="foo"}`),
   116  				mustParseLabels(t, `{bar="foo"}`),
   117  			},
   118  			mustParseLabels(t, `{foo="bar"}`),
   119  		},
   120  		{
   121  			"Subtract labels source < target no sub",
   122  			args{
   123  				mustParseLabels(t, `{foo="bar", bar="foo"}`),
   124  				mustParseLabels(t, `{fo="bar"}`),
   125  			},
   126  			mustParseLabels(t, `{bar="foo", foo="bar"}`),
   127  		},
   128  		{
   129  			"Subtract labels source = target no sub",
   130  			args{
   131  				mustParseLabels(t, `{foo="bar"}`),
   132  				mustParseLabels(t, `{fiz="buz"}`),
   133  			},
   134  			mustParseLabels(t, `{foo="bar"}`),
   135  		},
   136  		{
   137  			"Subtract labels source > target no sub",
   138  			args{
   139  				mustParseLabels(t, `{foo="bar"}`),
   140  				mustParseLabels(t, `{fiz="buz", foo="baz"}`),
   141  			},
   142  			mustParseLabels(t, `{foo="bar"}`),
   143  		},
   144  		{
   145  			"Subtract labels source > target no sub",
   146  			args{
   147  				mustParseLabels(t, `{a="b", foo="bar", baz="baz", fizz="fizz"}`),
   148  				mustParseLabels(t, `{foo="bar", baz="baz", buzz="buzz", fizz="fizz"}`),
   149  			},
   150  			mustParseLabels(t, `{a="b"}`),
   151  		},
   152  	}
   153  	for _, tt := range tests {
   154  		t.Run(tt.name, func(t *testing.T) {
   155  			if got := subtract(tt.args.a, tt.args.b); !reflect.DeepEqual(got, tt.want) {
   156  				t.Errorf("subtract() = %v, want %v", got, tt.want)
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  func Test_batch(t *testing.T) {
   163  	tests := []struct {
   164  		name          string
   165  		streams       []logproto.Stream
   166  		start, end    time.Time
   167  		limit, batch  int
   168  		labelMatcher  string
   169  		forward       bool
   170  		expectedCalls int
   171  		expected      []string
   172  	}{
   173  		{
   174  			name: "super simple forward",
   175  			streams: []logproto.Stream{
   176  				{
   177  					Labels: "{test=\"simple\"}",
   178  					Entries: []logproto.Entry{
   179  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   180  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   181  						{Timestamp: time.Unix(3, 0), Line: "line3"}, // End timestamp is exclusive
   182  					},
   183  				},
   184  			},
   185  			start:         time.Unix(1, 0),
   186  			end:           time.Unix(3, 0),
   187  			limit:         10,
   188  			batch:         10,
   189  			labelMatcher:  "{test=\"simple\"}",
   190  			forward:       true,
   191  			expectedCalls: 2, // Client doesn't know if the server hit a limit or there were no results so we have to query until there is no results, in this case 2 calls
   192  			expected: []string{
   193  				"line1",
   194  				"line2",
   195  			},
   196  		},
   197  		{
   198  			name: "super simple backward",
   199  			streams: []logproto.Stream{
   200  				{
   201  					Labels: "{test=\"simple\"}",
   202  					Entries: []logproto.Entry{
   203  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   204  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   205  						{Timestamp: time.Unix(3, 0), Line: "line3"}, // End timestamp is exclusive
   206  					},
   207  				},
   208  			},
   209  			start:         time.Unix(1, 0),
   210  			end:           time.Unix(3, 0),
   211  			limit:         10,
   212  			batch:         10,
   213  			labelMatcher:  "{test=\"simple\"}",
   214  			forward:       false,
   215  			expectedCalls: 2,
   216  			expected: []string{
   217  				"line2",
   218  				"line1",
   219  			},
   220  		},
   221  		{
   222  			name: "single stream forward batch",
   223  			streams: []logproto.Stream{
   224  				{
   225  					Labels: "{test=\"simple\"}",
   226  					Entries: []logproto.Entry{
   227  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   228  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   229  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   230  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   231  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   232  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   233  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   234  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   235  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   236  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   237  					},
   238  				},
   239  			},
   240  			start:        time.Unix(1, 0),
   241  			end:          time.Unix(11, 0),
   242  			limit:        9,
   243  			batch:        2,
   244  			labelMatcher: "{test=\"simple\"}",
   245  			forward:      true,
   246  			// Our batchsize is 2 but each query will also return the overlapping last element from the
   247  			// previous batch, as such we only get one item per call so we make a lot of calls
   248  			// Call one:   line1 line2
   249  			// Call two:   line2 line3
   250  			// Call three: line3 line4
   251  			// Call four:  line4 line5
   252  			// Call five:  line5 line6
   253  			// Call six:   line6 line7
   254  			// Call seven: line7 line8
   255  			// Call eight: line8 line9
   256  			expectedCalls: 8,
   257  			expected: []string{
   258  				"line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8", "line9",
   259  			},
   260  		},
   261  		{
   262  			name: "single stream backward batch",
   263  			streams: []logproto.Stream{
   264  				{
   265  					Labels: "{test=\"simple\"}",
   266  					Entries: []logproto.Entry{
   267  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   268  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   269  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   270  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   271  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   272  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   273  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   274  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   275  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   276  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   277  					},
   278  				},
   279  			},
   280  			start:         time.Unix(1, 0),
   281  			end:           time.Unix(11, 0),
   282  			limit:         9,
   283  			batch:         2,
   284  			labelMatcher:  "{test=\"simple\"}",
   285  			forward:       false,
   286  			expectedCalls: 8,
   287  			expected: []string{
   288  				"line10", "line9", "line8", "line7", "line6", "line5", "line4", "line3", "line2",
   289  			},
   290  		},
   291  		{
   292  			name: "two streams forward batch",
   293  			streams: []logproto.Stream{
   294  				{
   295  					Labels: "{test=\"one\"}",
   296  					Entries: []logproto.Entry{
   297  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   298  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   299  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   300  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   301  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   302  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   303  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   304  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   305  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   306  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   307  					},
   308  				},
   309  				{
   310  					Labels: "{test=\"two\"}",
   311  					Entries: []logproto.Entry{
   312  						{Timestamp: time.Unix(1, 1000), Line: "s2line1"},
   313  						{Timestamp: time.Unix(2, 1000), Line: "s2line2"},
   314  						{Timestamp: time.Unix(3, 1000), Line: "s2line3"},
   315  						{Timestamp: time.Unix(4, 1000), Line: "s2line4"},
   316  						{Timestamp: time.Unix(5, 1000), Line: "s2line5"},
   317  						{Timestamp: time.Unix(6, 1000), Line: "s2line6"},
   318  						{Timestamp: time.Unix(7, 1000), Line: "s2line7"},
   319  						{Timestamp: time.Unix(8, 1000), Line: "s2line8"},
   320  						{Timestamp: time.Unix(9, 1000), Line: "s2line9"},
   321  						{Timestamp: time.Unix(10, 1000), Line: "s2line10"},
   322  					},
   323  				},
   324  			},
   325  			start:        time.Unix(1, 0),
   326  			end:          time.Unix(11, 0),
   327  			limit:        12,
   328  			batch:        3,
   329  			labelMatcher: "{test=~\"one|two\"}",
   330  			forward:      true,
   331  			// Six calls
   332  			// 1 line1, s2line1, line2
   333  			// 2 line2, s2line2, line3
   334  			// 3 line3, s2line3, line4
   335  			// 4 line4, s2line4, line5
   336  			// 5 line5, s2line5, line6
   337  			// 6 line6, s2line6
   338  			expectedCalls: 6,
   339  			expected: []string{
   340  				"line1", "s2line1", "line2", "s2line2", "line3", "s2line3", "line4", "s2line4", "line5", "s2line5", "line6", "s2line6",
   341  			},
   342  		},
   343  		{
   344  			name: "two streams backward batch",
   345  			streams: []logproto.Stream{
   346  				{
   347  					Labels: "{test=\"one\"}",
   348  					Entries: []logproto.Entry{
   349  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   350  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   351  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   352  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   353  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   354  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   355  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   356  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   357  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   358  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   359  					},
   360  				},
   361  				{
   362  					Labels: "{test=\"two\"}",
   363  					Entries: []logproto.Entry{
   364  						{Timestamp: time.Unix(1, 1000), Line: "s2line1"},
   365  						{Timestamp: time.Unix(2, 1000), Line: "s2line2"},
   366  						{Timestamp: time.Unix(3, 1000), Line: "s2line3"},
   367  						{Timestamp: time.Unix(4, 1000), Line: "s2line4"},
   368  						{Timestamp: time.Unix(5, 1000), Line: "s2line5"},
   369  						{Timestamp: time.Unix(6, 1000), Line: "s2line6"},
   370  						{Timestamp: time.Unix(7, 1000), Line: "s2line7"},
   371  						{Timestamp: time.Unix(8, 1000), Line: "s2line8"},
   372  						{Timestamp: time.Unix(9, 1000), Line: "s2line9"},
   373  						{Timestamp: time.Unix(10, 1000), Line: "s2line10"},
   374  					},
   375  				},
   376  			},
   377  			start:         time.Unix(1, 0),
   378  			end:           time.Unix(11, 0),
   379  			limit:         12,
   380  			batch:         3,
   381  			labelMatcher:  "{test=~\"one|two\"}",
   382  			forward:       false,
   383  			expectedCalls: 6,
   384  			expected: []string{
   385  				"s2line10", "line10", "s2line9", "line9", "s2line8", "line8", "s2line7", "line7", "s2line6", "line6", "s2line5", "line5",
   386  			},
   387  		},
   388  		{
   389  			name: "single stream forward batch identical timestamps",
   390  			streams: []logproto.Stream{
   391  				{
   392  					Labels: "{test=\"simple\"}",
   393  					Entries: []logproto.Entry{
   394  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   395  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   396  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   397  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   398  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   399  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   400  						{Timestamp: time.Unix(6, 0), Line: "line6a"},
   401  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   402  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   403  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   404  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   405  					},
   406  				},
   407  			},
   408  			start:        time.Unix(1, 0),
   409  			end:          time.Unix(11, 0),
   410  			limit:        9,
   411  			batch:        4,
   412  			labelMatcher: "{test=\"simple\"}",
   413  			forward:      true,
   414  			// Our batchsize is 2 but each query will also return the overlapping last element from the
   415  			// previous batch, as such we only get one item per call so we make a lot of calls
   416  			// Call one:   line1 line2 line3 line4
   417  			// Call two:   line4 line5 line6 line6a
   418  			// Call three: line6 line6a line7 line8  <- notice line 6 and 6a share the same timestamp so they get returned as overlap in the next query.
   419  			expectedCalls: 3,
   420  			expected: []string{
   421  				"line1", "line2", "line3", "line4", "line5", "line6", "line6a", "line7", "line8",
   422  			},
   423  		},
   424  		{
   425  			name: "single stream backward batch identical timestamps",
   426  			streams: []logproto.Stream{
   427  				{
   428  					Labels: "{test=\"simple\"}",
   429  					Entries: []logproto.Entry{
   430  						{Timestamp: time.Unix(1, 0), Line: "line1"},
   431  						{Timestamp: time.Unix(2, 0), Line: "line2"},
   432  						{Timestamp: time.Unix(3, 0), Line: "line3"},
   433  						{Timestamp: time.Unix(4, 0), Line: "line4"},
   434  						{Timestamp: time.Unix(5, 0), Line: "line5"},
   435  						{Timestamp: time.Unix(6, 0), Line: "line6"},
   436  						{Timestamp: time.Unix(6, 0), Line: "line6a"},
   437  						{Timestamp: time.Unix(6, 0), Line: "line6b"},
   438  						{Timestamp: time.Unix(7, 0), Line: "line7"},
   439  						{Timestamp: time.Unix(8, 0), Line: "line8"},
   440  						{Timestamp: time.Unix(9, 0), Line: "line9"},
   441  						{Timestamp: time.Unix(10, 0), Line: "line10"},
   442  					},
   443  				},
   444  			},
   445  			start:        time.Unix(1, 0),
   446  			end:          time.Unix(11, 0),
   447  			limit:        11,
   448  			batch:        4,
   449  			labelMatcher: "{test=\"simple\"}",
   450  			forward:      false,
   451  			// Our batchsize is 2 but each query will also return the overlapping last element from the
   452  			// previous batch, as such we only get one item per call so we make a lot of calls
   453  			// Call one:   line10 line9 line8 line7
   454  			// Call two:   line7 line6b line6a line6
   455  			// Call three: line6b line6a line6 line5
   456  			// Call four:  line5 line5 line3 line2
   457  			expectedCalls: 4,
   458  			expected: []string{
   459  				"line10", "line9", "line8", "line7", "line6b", "line6a", "line6", "line5", "line4", "line3", "line2",
   460  			},
   461  		},
   462  	}
   463  	for _, tt := range tests {
   464  		t.Run(tt.name, func(t *testing.T) {
   465  			tc := newTestQueryClient(tt.streams...)
   466  			writer := &bytes.Buffer{}
   467  			out := output.NewRaw(writer, nil)
   468  			q := Query{
   469  				QueryString:     tt.labelMatcher,
   470  				Start:           tt.start,
   471  				End:             tt.end,
   472  				Limit:           tt.limit,
   473  				BatchSize:       tt.batch,
   474  				Forward:         tt.forward,
   475  				Step:            0,
   476  				Interval:        0,
   477  				Quiet:           false,
   478  				NoLabels:        false,
   479  				IgnoreLabelsKey: nil,
   480  				ShowLabelsKey:   nil,
   481  				FixedLabelsLen:  0,
   482  				LocalConfig:     "",
   483  			}
   484  			q.DoQuery(tc, out, false)
   485  			split := strings.Split(writer.String(), "\n")
   486  			// Remove the last entry because there is always a newline after the last line which
   487  			// leaves an entry element in the list of lines.
   488  			if len(split) > 0 {
   489  				split = split[:len(split)-1]
   490  			}
   491  			assert.Equal(t, tt.expected, split)
   492  			assert.Equal(t, tt.expectedCalls, tc.queryRangeCalls)
   493  		})
   494  	}
   495  }
   496  
   497  func mustParseLabels(t *testing.T, s string) loghttp.LabelSet {
   498  	t.Helper()
   499  	l, err := marshal.NewLabelSet(s)
   500  	require.NoErrorf(t, err, "Failed to parse %q", s)
   501  
   502  	return l
   503  }
   504  
   505  type testQueryClient struct {
   506  	engine          *logql.Engine
   507  	queryRangeCalls int
   508  }
   509  
   510  func newTestQueryClient(testStreams ...logproto.Stream) *testQueryClient {
   511  	q := logql.NewMockQuerier(0, testStreams)
   512  	e := logql.NewEngine(logql.EngineOpts{}, q, logql.NoLimits, log.NewNopLogger())
   513  	return &testQueryClient{
   514  		engine:          e,
   515  		queryRangeCalls: 0,
   516  	}
   517  }
   518  
   519  func (t *testQueryClient) Query(queryStr string, limit int, time time.Time, direction logproto.Direction, quiet bool) (*loghttp.QueryResponse, error) {
   520  	panic("implement me")
   521  }
   522  
   523  func (t *testQueryClient) QueryRange(queryStr string, limit int, from, through time.Time, direction logproto.Direction, step, interval time.Duration, quiet bool) (*loghttp.QueryResponse, error) {
   524  
   525  	params := logql.NewLiteralParams(queryStr, from, through, step, interval, direction, uint32(limit), nil)
   526  
   527  	v, err := t.engine.Query(params).Exec(context.Background())
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  
   532  	value, err := marshal.NewResultValue(v.Data)
   533  	if err != nil {
   534  		return nil, err
   535  	}
   536  
   537  	q := &loghttp.QueryResponse{
   538  		Status: "success",
   539  		Data: loghttp.QueryResponseData{
   540  			ResultType: value.Type(),
   541  			Result:     value,
   542  			Statistics: v.Statistics,
   543  		},
   544  	}
   545  	t.queryRangeCalls++
   546  	return q, nil
   547  }
   548  
   549  func (t *testQueryClient) ListLabelNames(quiet bool, from, through time.Time) (*loghttp.LabelResponse, error) {
   550  	panic("implement me")
   551  }
   552  
   553  func (t *testQueryClient) ListLabelValues(name string, quiet bool, from, through time.Time) (*loghttp.LabelResponse, error) {
   554  	panic("implement me")
   555  }
   556  
   557  func (t *testQueryClient) Series(matchers []string, from, through time.Time, quiet bool) (*loghttp.SeriesResponse, error) {
   558  	panic("implement me")
   559  }
   560  
   561  func (t *testQueryClient) LiveTailQueryConn(queryStr string, delayFor time.Duration, limit int, start time.Time, quiet bool) (*websocket.Conn, error) {
   562  	panic("implement me")
   563  }
   564  
   565  func (t *testQueryClient) GetOrgID() string {
   566  	panic("implement me")
   567  }
   568  
   569  var schemaConfigContents = `schema_config:
   570    configs:
   571    - from: 2020-05-15
   572      store: boltdb-shipper
   573      object_store: gcs
   574      schema: v10
   575      index:
   576        prefix: index_
   577        period: 168h
   578    - from: 2020-07-31
   579      store: boltdb-shipper
   580      object_store: gcs
   581      schema: v11
   582      index:
   583        prefix: index_
   584        period: 24h
   585  `
   586  
   587  func TestLoadFromURL(t *testing.T) {
   588  	tmpDir := t.TempDir()
   589  
   590  	conf := loki.Config{
   591  		StorageConfig: storage.Config{
   592  			FSConfig: local.FSConfig{
   593  				Directory: tmpDir,
   594  			},
   595  		},
   596  	}
   597  
   598  	// Missing SharedStoreType should error
   599  	cm := storage.NewClientMetrics()
   600  	client, err := GetObjectClient(conf, cm)
   601  	require.Error(t, err)
   602  	require.Nil(t, client)
   603  
   604  	conf.StorageConfig.BoltDBShipperConfig = shipper.Config{
   605  		Config: indexshipper.Config{
   606  			SharedStoreType: config.StorageTypeFileSystem,
   607  		},
   608  	}
   609  
   610  	client, err = GetObjectClient(conf, cm)
   611  	require.NoError(t, err)
   612  	require.NotNil(t, client)
   613  
   614  	// Missing schema.config file should error
   615  	schemaConfig, err := LoadSchemaUsingObjectClient(client, SchemaConfigFilename)
   616  	require.Error(t, err)
   617  	require.Nil(t, schemaConfig)
   618  
   619  	err = os.WriteFile(
   620  		filepath.Join(tmpDir, SchemaConfigFilename),
   621  		[]byte(schemaConfigContents),
   622  		0666,
   623  	)
   624  	require.NoError(t, err)
   625  
   626  	schemaConfig, err = LoadSchemaUsingObjectClient(client, SchemaConfigFilename)
   627  
   628  	require.NoError(t, err)
   629  	require.NotNil(t, schemaConfig)
   630  }