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

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"sort"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/grafana/loki/pkg/loghttp"
    13  	"github.com/grafana/loki/pkg/logproto"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestFileClient_QueryRangeLogQueries(t *testing.T) {
    20  	input := []string{
    21  		`level=info event="loki started" caller=main.go ts=1625995076`,
    22  		`level=info event="runtime loader started" caller=main.go ts=1625995077`,
    23  		`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
    24  		`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
    25  		`level=info event="loki ready" caller=main.go ts=1625996095`,
    26  	}
    27  
    28  	reversed := make([]string, len(input))
    29  	copy(reversed, input)
    30  	sort.Slice(reversed, func(i, j int) bool {
    31  		return i > j
    32  	})
    33  
    34  	now := time.Now()
    35  
    36  	cases := []struct {
    37  		name           string
    38  		limit          int
    39  		start, end     time.Time
    40  		direction      logproto.Direction
    41  		step, interval time.Duration
    42  		expectedStatus loghttp.QueryStatus
    43  		expected       []string
    44  	}{
    45  		{
    46  			name:           "return-all-logs-backward",
    47  			limit:          10, // more than input
    48  			start:          now.Add(-1 * time.Hour),
    49  			end:            now,
    50  			direction:      logproto.BACKWARD,
    51  			step:           0, // let client decide based on start and end
    52  			interval:       0,
    53  			expectedStatus: loghttp.QueryStatusSuccess,
    54  			expected:       reversed,
    55  		},
    56  		{
    57  			name:           "return-all-logs-forward",
    58  			limit:          10, // more than input
    59  			start:          now.Add(-1 * time.Hour),
    60  			end:            now,
    61  			direction:      logproto.FORWARD,
    62  			step:           0, // let the client decide based on start and end
    63  			interval:       0,
    64  			expectedStatus: loghttp.QueryStatusSuccess,
    65  			expected:       input,
    66  		},
    67  	}
    68  
    69  	for _, c := range cases {
    70  		t.Run(c.name, func(t *testing.T) {
    71  			client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
    72  			resp, err := client.QueryRange(
    73  				`{foo="bar"}`, // label matcher doesn't matter.
    74  				c.limit,
    75  				c.start,
    76  				c.end,
    77  				c.direction,
    78  				c.step,
    79  				c.interval,
    80  				true,
    81  			)
    82  
    83  			require.NoError(t, err)
    84  			require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
    85  			assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
    86  			assertStreams(t, resp.Data.Result, c.expected)
    87  		})
    88  	}
    89  }
    90  
    91  func TestFileClient_Query(t *testing.T) {
    92  	input := []string{
    93  		`level=info event="loki started" caller=main.go ts=1625995076`,
    94  		`level=info event="runtime loader started" caller=main.go ts=1625995077`,
    95  		`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
    96  		`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
    97  		`level=info event="loki ready" caller=main.go ts=1625996095`,
    98  	}
    99  
   100  	reversed := make([]string, len(input))
   101  	copy(reversed, input)
   102  	sort.Slice(reversed, func(i, j int) bool {
   103  		return i > j
   104  	})
   105  
   106  	now := time.Now()
   107  
   108  	cases := []struct {
   109  		name           string
   110  		limit          int
   111  		ts             time.Time
   112  		direction      logproto.Direction
   113  		expectedStatus loghttp.QueryStatus
   114  		expected       []string
   115  	}{
   116  		{
   117  			name:           "return-all-logs-backward",
   118  			limit:          10, // more than input
   119  			ts:             now.Add(-1 * time.Hour),
   120  			direction:      logproto.BACKWARD,
   121  			expectedStatus: loghttp.QueryStatusSuccess,
   122  			expected:       reversed,
   123  		},
   124  		{
   125  			name:           "return-all-logs-forward",
   126  			limit:          10, // more than input
   127  			ts:             now.Add(-1 * time.Hour),
   128  			direction:      logproto.FORWARD,
   129  			expectedStatus: loghttp.QueryStatusSuccess,
   130  			expected:       input,
   131  		},
   132  	}
   133  
   134  	for _, c := range cases {
   135  		t.Run(c.name, func(t *testing.T) {
   136  			client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
   137  			resp, err := client.Query(
   138  				`{foo="bar"}`, // label matcher doesn't matter.
   139  				c.limit,
   140  				c.ts,
   141  				c.direction,
   142  				true,
   143  			)
   144  
   145  			require.NoError(t, err)
   146  			require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
   147  			assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
   148  			assertStreams(t, resp.Data.Result, c.expected)
   149  		})
   150  	}
   151  }
   152  
   153  func TestFileClient_ListLabelNames(t *testing.T) {
   154  	c := newEmptyClient(t)
   155  	values, err := c.ListLabelNames(true, time.Now(), time.Now())
   156  	require.NoError(t, err)
   157  	assert.Equal(t, &loghttp.LabelResponse{
   158  		Data:   []string{defaultLabelKey},
   159  		Status: loghttp.QueryStatusSuccess,
   160  	}, values)
   161  }
   162  
   163  func TestFileClient_ListLabelValues(t *testing.T) {
   164  	c := newEmptyClient(t)
   165  	values, err := c.ListLabelValues(defaultLabelKey, true, time.Now(), time.Now())
   166  	require.NoError(t, err)
   167  	assert.Equal(t, &loghttp.LabelResponse{
   168  		Data:   []string{defaultLabelValue},
   169  		Status: loghttp.QueryStatusSuccess,
   170  	}, values)
   171  
   172  }
   173  
   174  func TestFileClient_Series(t *testing.T) {
   175  	c := newEmptyClient(t)
   176  	got, err := c.Series(nil, time.Now(), time.Now(), true)
   177  	require.NoError(t, err)
   178  
   179  	exp := &loghttp.SeriesResponse{
   180  		Data: []loghttp.LabelSet{
   181  			{defaultLabelKey: defaultLabelValue},
   182  		},
   183  		Status: loghttp.QueryStatusSuccess,
   184  	}
   185  
   186  	assert.Equal(t, exp, got)
   187  }
   188  
   189  func TestFileClient_LiveTail(t *testing.T) {
   190  	c := newEmptyClient(t)
   191  	x, err := c.LiveTailQueryConn("", time.Second, 0, time.Now(), true)
   192  	require.Error(t, err)
   193  	require.Nil(t, x)
   194  	assert.True(t, errors.Is(err, ErrNotSupported))
   195  }
   196  
   197  func TestFileClient_GetOrgID(t *testing.T) {
   198  	c := newEmptyClient(t)
   199  	assert.Equal(t, defaultOrgID, c.GetOrgID())
   200  }
   201  
   202  func newEmptyClient(t *testing.T) *FileClient {
   203  	t.Helper()
   204  	return NewFileClient(io.NopCloser(&bytes.Buffer{}))
   205  }
   206  
   207  func assertStreams(t *testing.T, result loghttp.ResultValue, logLines []string) {
   208  	t.Helper()
   209  
   210  	streams, ok := result.(loghttp.Streams)
   211  	require.True(t, ok, "response type should be `loghttp.Streams`")
   212  
   213  	require.Len(t, streams, 1, "there should be only one stream for FileClient")
   214  
   215  	got := streams[0]
   216  	sort.Slice(got.Entries, func(i, j int) bool {
   217  		return got.Entries[i].Timestamp.UnixNano() < got.Entries[j].Timestamp.UnixNano()
   218  	})
   219  	require.Equal(t, len(got.Entries), len(logLines))
   220  	for i, entry := range got.Entries {
   221  		assert.Equal(t, entry.Line, logLines[i])
   222  	}
   223  }