mirror of https://github.com/grafana/loki
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
6.1 KiB
224 lines
6.1 KiB
|
4 years ago
|
package client
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"errors"
|
||
|
|
"io"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/grafana/loki/pkg/loghttp"
|
||
|
|
"github.com/grafana/loki/pkg/logproto"
|
||
|
|
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestFileClient_QueryRangeLogQueries(t *testing.T) {
|
||
|
|
input := []string{
|
||
|
|
`level=info event="loki started" caller=main.go ts=1625995076`,
|
||
|
|
`level=info event="runtime loader started" caller=main.go ts=1625995077`,
|
||
|
|
`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
|
||
|
|
`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
|
||
|
|
`level=info event="loki ready" caller=main.go ts=1625996095`,
|
||
|
|
}
|
||
|
|
|
||
|
|
reversed := make([]string, len(input))
|
||
|
|
copy(reversed, input)
|
||
|
|
sort.Slice(reversed, func(i, j int) bool {
|
||
|
|
return i > j
|
||
|
|
})
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
|
||
|
|
cases := []struct {
|
||
|
|
name string
|
||
|
|
limit int
|
||
|
|
start, end time.Time
|
||
|
|
direction logproto.Direction
|
||
|
|
step, interval time.Duration
|
||
|
|
expectedStatus loghttp.QueryStatus
|
||
|
|
expected []string
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "return-all-logs-backward",
|
||
|
|
limit: 10, // more than input
|
||
|
|
start: now.Add(-1 * time.Hour),
|
||
|
|
end: now,
|
||
|
|
direction: logproto.BACKWARD,
|
||
|
|
step: 0, // let client decide based on start and end
|
||
|
|
interval: 0,
|
||
|
|
expectedStatus: loghttp.QueryStatusSuccess,
|
||
|
|
expected: reversed,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "return-all-logs-forward",
|
||
|
|
limit: 10, // more than input
|
||
|
|
start: now.Add(-1 * time.Hour),
|
||
|
|
end: now,
|
||
|
|
direction: logproto.FORWARD,
|
||
|
|
step: 0, // let the client decide based on start and end
|
||
|
|
interval: 0,
|
||
|
|
expectedStatus: loghttp.QueryStatusSuccess,
|
||
|
|
expected: input,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, c := range cases {
|
||
|
|
t.Run(c.name, func(t *testing.T) {
|
||
|
|
client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
|
||
|
|
resp, err := client.QueryRange(
|
||
|
|
`{foo="bar"}`, // label matcher doesn't matter.
|
||
|
|
c.limit,
|
||
|
|
c.start,
|
||
|
|
c.end,
|
||
|
|
c.direction,
|
||
|
|
c.step,
|
||
|
|
c.interval,
|
||
|
|
true,
|
||
|
|
)
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
|
||
|
|
assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
|
||
|
|
assertStreams(t, resp.Data.Result, c.expected)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_Query(t *testing.T) {
|
||
|
|
input := []string{
|
||
|
|
`level=info event="loki started" caller=main.go ts=1625995076`,
|
||
|
|
`level=info event="runtime loader started" caller=main.go ts=1625995077`,
|
||
|
|
`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
|
||
|
|
`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
|
||
|
|
`level=info event="loki ready" caller=main.go ts=1625996095`,
|
||
|
|
}
|
||
|
|
|
||
|
|
reversed := make([]string, len(input))
|
||
|
|
copy(reversed, input)
|
||
|
|
sort.Slice(reversed, func(i, j int) bool {
|
||
|
|
return i > j
|
||
|
|
})
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
|
||
|
|
cases := []struct {
|
||
|
|
name string
|
||
|
|
limit int
|
||
|
|
ts time.Time
|
||
|
|
direction logproto.Direction
|
||
|
|
expectedStatus loghttp.QueryStatus
|
||
|
|
expected []string
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "return-all-logs-backward",
|
||
|
|
limit: 10, // more than input
|
||
|
|
ts: now.Add(-1 * time.Hour),
|
||
|
|
direction: logproto.BACKWARD,
|
||
|
|
expectedStatus: loghttp.QueryStatusSuccess,
|
||
|
|
expected: reversed,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "return-all-logs-forward",
|
||
|
|
limit: 10, // more than input
|
||
|
|
ts: now.Add(-1 * time.Hour),
|
||
|
|
direction: logproto.FORWARD,
|
||
|
|
expectedStatus: loghttp.QueryStatusSuccess,
|
||
|
|
expected: input,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, c := range cases {
|
||
|
|
t.Run(c.name, func(t *testing.T) {
|
||
|
|
client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
|
||
|
|
resp, err := client.Query(
|
||
|
|
`{foo="bar"}`, // label matcher doesn't matter.
|
||
|
|
c.limit,
|
||
|
|
c.ts,
|
||
|
|
c.direction,
|
||
|
|
true,
|
||
|
|
)
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
|
||
|
|
assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
|
||
|
|
assertStreams(t, resp.Data.Result, c.expected)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_ListLabelNames(t *testing.T) {
|
||
|
|
c := newEmptyClient(t)
|
||
|
|
values, err := c.ListLabelNames(true, time.Now(), time.Now())
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, &loghttp.LabelResponse{
|
||
|
|
Data: []string{defaultLabelKey},
|
||
|
|
Status: loghttp.QueryStatusSuccess,
|
||
|
|
}, values)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_ListLabelValues(t *testing.T) {
|
||
|
|
c := newEmptyClient(t)
|
||
|
|
values, err := c.ListLabelValues(defaultLabelKey, true, time.Now(), time.Now())
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, &loghttp.LabelResponse{
|
||
|
|
Data: []string{defaultLabelValue},
|
||
|
|
Status: loghttp.QueryStatusSuccess,
|
||
|
|
}, values)
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_Series(t *testing.T) {
|
||
|
|
c := newEmptyClient(t)
|
||
|
|
got, err := c.Series(nil, time.Now(), time.Now(), true)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
exp := &loghttp.SeriesResponse{
|
||
|
|
Data: []loghttp.LabelSet{
|
||
|
|
{defaultLabelKey: defaultLabelValue},
|
||
|
|
},
|
||
|
|
Status: loghttp.QueryStatusSuccess,
|
||
|
|
}
|
||
|
|
|
||
|
|
assert.Equal(t, exp, got)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_LiveTail(t *testing.T) {
|
||
|
|
c := newEmptyClient(t)
|
||
|
|
x, err := c.LiveTailQueryConn("", time.Second, 0, time.Now(), true)
|
||
|
|
require.Error(t, err)
|
||
|
|
require.Nil(t, x)
|
||
|
|
assert.True(t, errors.Is(err, ErrNotSupported))
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFileClient_GetOrgID(t *testing.T) {
|
||
|
|
c := newEmptyClient(t)
|
||
|
|
assert.Equal(t, defaultOrgID, c.GetOrgID())
|
||
|
|
}
|
||
|
|
|
||
|
|
func newEmptyClient(t *testing.T) *FileClient {
|
||
|
|
t.Helper()
|
||
|
|
return NewFileClient(io.NopCloser(&bytes.Buffer{}))
|
||
|
|
}
|
||
|
|
|
||
|
|
func assertStreams(t *testing.T, result loghttp.ResultValue, logLines []string) {
|
||
|
|
t.Helper()
|
||
|
|
|
||
|
|
streams, ok := result.(loghttp.Streams)
|
||
|
|
require.True(t, ok, "response type should be `loghttp.Streams`")
|
||
|
|
|
||
|
|
require.Len(t, streams, 1, "there should be only one stream for FileClient")
|
||
|
|
|
||
|
|
got := streams[0]
|
||
|
|
sort.Slice(got.Entries, func(i, j int) bool {
|
||
|
|
return got.Entries[i].Timestamp.UnixNano() < got.Entries[j].Timestamp.UnixNano()
|
||
|
|
})
|
||
|
|
require.Equal(t, len(got.Entries), len(logLines))
|
||
|
|
for i, entry := range got.Entries {
|
||
|
|
assert.Equal(t, entry.Line, logLines[i])
|
||
|
|
}
|
||
|
|
}
|