336 lines
9.1 KiB
Go
336 lines
9.1 KiB
Go
package reads_test
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2/models"
|
|
"github.com/influxdata/influxdb/v2/storage/reads"
|
|
"github.com/influxdata/influxdb/v2/storage/reads/datatypes"
|
|
"github.com/influxdata/influxdb/v2/tsdb/cursors"
|
|
)
|
|
|
|
func TestNewWindowAggregateResultSet_Tags(t *testing.T) {
|
|
|
|
newCursor := sliceSeriesCursor{
|
|
rows: newSeriesRows(
|
|
"clicks click=1 1",
|
|
)}
|
|
|
|
request := datatypes.ReadWindowAggregateRequest{
|
|
Aggregate: []*datatypes.Aggregate{
|
|
{
|
|
Type: datatypes.Aggregate_AggregateTypeMean,
|
|
},
|
|
},
|
|
}
|
|
resultSet, err := reads.NewWindowAggregateResultSet(context.Background(), &request, &newCursor)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error creating WindowAggregateResultSet: %s", err)
|
|
}
|
|
|
|
// If .Next() was never called, seriesRow is nil and tags are empty.
|
|
expectedTags := "[]"
|
|
if resultSet.Tags().String() != expectedTags {
|
|
t.Errorf("expected tags: %s got: %s", expectedTags, resultSet.Tags().String())
|
|
}
|
|
|
|
resultSet.Next()
|
|
expectedTags = "[{_m clicks}]"
|
|
if resultSet.Tags().String() != expectedTags {
|
|
t.Errorf("expected tags: %s got: %s", expectedTags, resultSet.Tags().String())
|
|
}
|
|
}
|
|
|
|
type mockIntegerArrayCursor struct {
|
|
callCount int
|
|
}
|
|
|
|
func (i *mockIntegerArrayCursor) Close() {}
|
|
func (i *mockIntegerArrayCursor) Err() error { return nil }
|
|
func (i *mockIntegerArrayCursor) Stats() cursors.CursorStats { return cursors.CursorStats{} }
|
|
func (i *mockIntegerArrayCursor) Next() *cursors.IntegerArray {
|
|
if i.callCount == 1 {
|
|
return &cursors.IntegerArray{}
|
|
}
|
|
i.callCount++
|
|
return &cursors.IntegerArray{
|
|
Timestamps: []int64{
|
|
1000000000,
|
|
1000000005,
|
|
1000000010,
|
|
1000000011,
|
|
1000000012,
|
|
1000000013,
|
|
1000000014,
|
|
1000000020,
|
|
2678400000000000,
|
|
5000000000000000,
|
|
5097600000000001,
|
|
},
|
|
Values: []int64{100, 55, 256, 83, 99, 124, 1979, 4, 67, 49929, 51000},
|
|
}
|
|
}
|
|
|
|
type mockStringArrayCursor struct{}
|
|
|
|
func (i *mockStringArrayCursor) Close() {}
|
|
func (i *mockStringArrayCursor) Err() error { return nil }
|
|
func (i *mockStringArrayCursor) Stats() cursors.CursorStats { return cursors.CursorStats{} }
|
|
func (i *mockStringArrayCursor) Next() *cursors.StringArray {
|
|
return &cursors.StringArray{
|
|
Timestamps: []int64{1000000000},
|
|
Values: []string{"a"},
|
|
}
|
|
}
|
|
|
|
type mockCursorIterator struct {
|
|
newCursorFn func(req *cursors.CursorRequest) cursors.Cursor
|
|
statsFn func() cursors.CursorStats
|
|
}
|
|
|
|
func (i *mockCursorIterator) Next(ctx context.Context, req *cursors.CursorRequest) (cursors.Cursor, error) {
|
|
return i.newCursorFn(req), nil
|
|
}
|
|
func (i *mockCursorIterator) Stats() cursors.CursorStats {
|
|
if i.statsFn == nil {
|
|
return cursors.CursorStats{}
|
|
}
|
|
return i.statsFn()
|
|
}
|
|
|
|
type mockReadCursor struct {
|
|
rows []reads.SeriesRow
|
|
index int64
|
|
}
|
|
|
|
func newMockReadCursor(keys ...string) mockReadCursor {
|
|
rows := make([]reads.SeriesRow, len(keys))
|
|
for i := range keys {
|
|
rows[i].Name, rows[i].SeriesTags = models.ParseKeyBytes([]byte(keys[i]))
|
|
rows[i].Tags = rows[i].SeriesTags.Clone()
|
|
var itrs cursors.CursorIterators
|
|
cur := &mockCursorIterator{
|
|
newCursorFn: func(req *cursors.CursorRequest) cursors.Cursor {
|
|
return &mockIntegerArrayCursor{}
|
|
},
|
|
statsFn: func() cursors.CursorStats {
|
|
return cursors.CursorStats{ScannedBytes: 500, ScannedValues: 10}
|
|
},
|
|
}
|
|
itrs = append(itrs, cur)
|
|
rows[i].Query = itrs
|
|
}
|
|
return mockReadCursor{rows: rows}
|
|
}
|
|
|
|
func (c *mockReadCursor) Next() *reads.SeriesRow {
|
|
if c.index == int64(len(c.rows)) {
|
|
return nil
|
|
}
|
|
row := c.rows[c.index]
|
|
c.index++
|
|
return &row
|
|
}
|
|
func (c *mockReadCursor) Close() {}
|
|
func (c *mockReadCursor) Err() error { return nil }
|
|
|
|
// The stats from a WindowAggregateResultSet are retrieved from the cursor.
|
|
func TestNewWindowAggregateResultSet_Stats(t *testing.T) {
|
|
|
|
newCursor := newMockReadCursor(
|
|
"clicks click=1 1",
|
|
)
|
|
|
|
request := datatypes.ReadWindowAggregateRequest{
|
|
Aggregate: []*datatypes.Aggregate{
|
|
{
|
|
Type: datatypes.Aggregate_AggregateTypeMean,
|
|
},
|
|
},
|
|
}
|
|
resultSet, err := reads.NewWindowAggregateResultSet(context.Background(), &request, &newCursor)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error creating WindowAggregateResultSet: %s", err)
|
|
}
|
|
|
|
// If .Next() was never called, seriesRow is nil and stats are empty.
|
|
stats := resultSet.Stats()
|
|
if stats.ScannedBytes != 0 || stats.ScannedValues != 0 {
|
|
t.Errorf("expected statistics to be empty")
|
|
}
|
|
|
|
resultSet.Next()
|
|
stats = resultSet.Stats()
|
|
if stats.ScannedBytes != 500 {
|
|
t.Errorf("Expected scanned bytes: %d got: %d", 500, stats.ScannedBytes)
|
|
}
|
|
if stats.ScannedValues != 10 {
|
|
t.Errorf("Expected scanned values: %d got: %d", 10, stats.ScannedValues)
|
|
}
|
|
}
|
|
|
|
// A mean window aggregate is supported
|
|
func TestNewWindowAggregateResultSet_Mean(t *testing.T) {
|
|
|
|
newCursor := newMockReadCursor(
|
|
"clicks click=1 1",
|
|
)
|
|
|
|
request := datatypes.ReadWindowAggregateRequest{
|
|
Aggregate: []*datatypes.Aggregate{
|
|
&datatypes.Aggregate{Type: datatypes.Aggregate_AggregateTypeMean},
|
|
},
|
|
WindowEvery: 10,
|
|
}
|
|
resultSet, err := reads.NewWindowAggregateResultSet(context.Background(), &request, &newCursor)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error creating WindowAggregateResultSet: %s", err)
|
|
}
|
|
|
|
if !resultSet.Next() {
|
|
t.Fatalf("unexpected: resultSet could not advance")
|
|
}
|
|
cursor := resultSet.Cursor()
|
|
if cursor == nil {
|
|
t.Fatalf("unexpected: cursor was nil")
|
|
}
|
|
floatArrayCursor := cursor.(cursors.FloatArrayCursor)
|
|
floatArray := floatArrayCursor.Next()
|
|
|
|
if !reflect.DeepEqual(floatArray.Timestamps, []int64{1000000010, 1000000020, 1000000030, 2678400000000010, 5000000000000010, 5097600000000010}) {
|
|
t.Log(time.Unix(0, floatArray.Timestamps[0]))
|
|
t.Errorf("unexpected mean timestamps: %v", floatArray.Timestamps)
|
|
}
|
|
if !reflect.DeepEqual(floatArray.Values, []float64{77.5, 508.2, 4, 67, 49929, 51000}) {
|
|
t.Errorf("unexpected mean values: %v", floatArray.Values)
|
|
}
|
|
}
|
|
|
|
func TestNewWindowAggregateResultSet_Months(t *testing.T) {
|
|
|
|
newCursor := newMockReadCursor(
|
|
"clicks click=1 1",
|
|
)
|
|
request := datatypes.ReadWindowAggregateRequest{
|
|
Aggregate: []*datatypes.Aggregate{
|
|
&datatypes.Aggregate{Type: datatypes.Aggregate_AggregateTypeMean},
|
|
},
|
|
Window: &datatypes.Window{
|
|
Every: &datatypes.Duration{
|
|
Nsecs: 0,
|
|
Months: 1,
|
|
Negative: false,
|
|
},
|
|
},
|
|
}
|
|
resultSet, err := reads.NewWindowAggregateResultSet(context.Background(), &request, &newCursor)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error creating WindowAggregateResultSet: %s", err)
|
|
}
|
|
|
|
if !resultSet.Next() {
|
|
t.Fatalf("unexpected: resultSet could not advance")
|
|
}
|
|
cursor := resultSet.Cursor()
|
|
if cursor == nil {
|
|
t.Fatalf("unexpected: cursor was nil")
|
|
}
|
|
floatArrayCursor := cursor.(cursors.FloatArrayCursor)
|
|
floatArray := floatArrayCursor.Next()
|
|
|
|
if !reflect.DeepEqual(floatArray.Timestamps, []int64{2678400000000000, 5097600000000000, 7776000000000000}) {
|
|
t.Log(time.Unix(0, floatArray.Timestamps[0]))
|
|
t.Errorf("unexpected month timestamps: %v", floatArray.Timestamps)
|
|
}
|
|
if !reflect.DeepEqual(floatArray.Values, []float64{337.5, 24998, 51000}) {
|
|
t.Errorf("unexpected month values: %v", floatArray.Values)
|
|
}
|
|
}
|
|
|
|
func TestNewWindowAggregateResultSet_UnsupportedTyped(t *testing.T) {
|
|
newCursor := newMockReadCursor(
|
|
"clicks click=1 1",
|
|
)
|
|
for i := range newCursor.rows[0].Query {
|
|
newCursor.rows[0].Query[i] = &mockCursorIterator{
|
|
newCursorFn: func(req *cursors.CursorRequest) cursors.Cursor {
|
|
return &mockStringArrayCursor{}
|
|
},
|
|
}
|
|
}
|
|
|
|
request := datatypes.ReadWindowAggregateRequest{
|
|
Aggregate: []*datatypes.Aggregate{
|
|
{Type: datatypes.Aggregate_AggregateTypeMean},
|
|
},
|
|
WindowEvery: 10,
|
|
}
|
|
resultSet, err := reads.NewWindowAggregateResultSet(context.Background(), &request, &newCursor)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error creating WindowAggregateResultSet: %s", err)
|
|
}
|
|
|
|
if resultSet.Next() {
|
|
t.Fatal("unexpected: resultSet should not have advanced")
|
|
}
|
|
err = resultSet.Err()
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if want, got := "unsupported input type for mean aggregate: string", err.Error(); want != got {
|
|
t.Fatalf("unexpected error:\n\t- %q\n\t+ %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestNewWindowAggregateResultSet_TimeRange(t *testing.T) {
|
|
newCursor := newMockReadCursor(
|
|
"clicks click=1 1",
|
|
)
|
|
for i := range newCursor.rows[0].Query {
|
|
newCursor.rows[0].Query[i] = &mockCursorIterator{
|
|
newCursorFn: func(req *cursors.CursorRequest) cursors.Cursor {
|
|
if want, got := int64(0), req.StartTime; want != got {
|
|
t.Errorf("unexpected start time -want/+got:\n\t- %d\n\t+ %d", want, got)
|
|
}
|
|
if want, got := int64(29), req.EndTime; want != got {
|
|
t.Errorf("unexpected end time -want/+got:\n\t- %d\n\t+ %d", want, got)
|
|
}
|
|
return &mockIntegerArrayCursor{}
|
|
},
|
|
}
|
|
}
|
|
|
|
ctx := context.Background()
|
|
req := datatypes.ReadWindowAggregateRequest{
|
|
Range: &datatypes.TimestampRange{
|
|
Start: 0,
|
|
End: 30,
|
|
},
|
|
Aggregate: []*datatypes.Aggregate{
|
|
{
|
|
Type: datatypes.Aggregate_AggregateTypeCount,
|
|
},
|
|
},
|
|
Window: &datatypes.Window{
|
|
Every: &datatypes.Duration{Nsecs: int64(time.Minute)},
|
|
},
|
|
}
|
|
|
|
resultSet, err := reads.NewWindowAggregateResultSet(ctx, &req, &newCursor)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !resultSet.Next() {
|
|
t.Fatal("expected result")
|
|
}
|
|
}
|