666 lines
14 KiB
Go
666 lines
14 KiB
Go
package influx
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/influxdata/platform/chronograf"
|
|
"github.com/influxdata/platform/chronograf/mocks"
|
|
)
|
|
|
|
func Test_toPoint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
anno *chronograf.Annotation
|
|
now time.Time
|
|
want chronograf.Point
|
|
}{
|
|
0: {
|
|
name: "convert annotation to point w/o start and end times",
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
Text: "mytext",
|
|
Type: "mytype",
|
|
},
|
|
now: time.Unix(0, 0),
|
|
want: chronograf.Point{
|
|
Database: AnnotationsDB,
|
|
RetentionPolicy: DefaultRP,
|
|
Measurement: DefaultMeasurement,
|
|
Time: time.Time{}.UnixNano(),
|
|
Tags: map[string]string{
|
|
"id": "1",
|
|
},
|
|
Fields: map[string]interface{}{
|
|
"deleted": false,
|
|
"start_time": time.Time{}.UnixNano(),
|
|
"modified_time_ns": int64(time.Unix(0, 0).UnixNano()),
|
|
"text": "mytext",
|
|
"type": "mytype",
|
|
},
|
|
},
|
|
},
|
|
1: {
|
|
name: "convert annotation to point with start/end time",
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
Text: "mytext",
|
|
Type: "mytype",
|
|
StartTime: time.Unix(100, 0),
|
|
EndTime: time.Unix(200, 0),
|
|
},
|
|
now: time.Unix(0, 0),
|
|
want: chronograf.Point{
|
|
Database: AnnotationsDB,
|
|
RetentionPolicy: DefaultRP,
|
|
Measurement: DefaultMeasurement,
|
|
Time: time.Unix(200, 0).UnixNano(),
|
|
Tags: map[string]string{
|
|
"id": "1",
|
|
},
|
|
Fields: map[string]interface{}{
|
|
"deleted": false,
|
|
"start_time": time.Unix(100, 0).UnixNano(),
|
|
"modified_time_ns": int64(time.Unix(0, 0).UnixNano()),
|
|
"text": "mytext",
|
|
"type": "mytype",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := toPoint(tt.anno, tt.now); !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("toPoint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_toDeletedPoint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
anno *chronograf.Annotation
|
|
now time.Time
|
|
want chronograf.Point
|
|
}{
|
|
0: {
|
|
name: "convert annotation to point w/o start and end times",
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
EndTime: time.Unix(0, 0),
|
|
},
|
|
now: time.Unix(0, 0),
|
|
want: chronograf.Point{
|
|
Database: AnnotationsDB,
|
|
RetentionPolicy: DefaultRP,
|
|
Measurement: DefaultMeasurement,
|
|
Time: 0,
|
|
Tags: map[string]string{
|
|
"id": "1",
|
|
},
|
|
Fields: map[string]interface{}{
|
|
"deleted": true,
|
|
"start_time": int64(0),
|
|
"modified_time_ns": int64(0),
|
|
"text": "",
|
|
"type": "",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := toDeletedPoint(tt.anno, tt.now); !cmp.Equal(got, tt.want) {
|
|
t.Errorf("toDeletedPoint() = %s", cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_value_Int64(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
v value
|
|
idx int
|
|
want int64
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "index out of range returns error",
|
|
idx: 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "converts a string to int64",
|
|
v: value{
|
|
json.Number("1"),
|
|
},
|
|
idx: 0,
|
|
want: int64(1),
|
|
},
|
|
{
|
|
name: "when not a json.Number, return error",
|
|
v: value{
|
|
"howdy",
|
|
},
|
|
idx: 0,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.v.Int64(tt.idx)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("value.Int64() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("value.Int64() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_value_Time(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
v value
|
|
idx int
|
|
want time.Time
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "index out of range returns error",
|
|
idx: 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "converts a string to int64",
|
|
v: value{
|
|
json.Number("1"),
|
|
},
|
|
idx: 0,
|
|
want: time.Unix(0, 1),
|
|
},
|
|
{
|
|
name: "when not a json.Number, return error",
|
|
v: value{
|
|
"howdy",
|
|
},
|
|
idx: 0,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.v.Time(tt.idx)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("value.Time() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("value.Time() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_value_String(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
v value
|
|
idx int
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "index out of range returns error",
|
|
idx: 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "converts a string",
|
|
v: value{
|
|
"howdy",
|
|
},
|
|
idx: 0,
|
|
want: "howdy",
|
|
},
|
|
{
|
|
name: "when not a string, return error",
|
|
v: value{
|
|
0,
|
|
},
|
|
idx: 0,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := tt.v.String(tt.idx)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("value.String() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("value.String() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnnotationStore_queryAnnotations(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
query string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
client chronograf.TimeSeries
|
|
args args
|
|
want []chronograf.Annotation
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "query error returns an error",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return nil, fmt.Errorf("error")
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "response marshal error returns an error",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse("", fmt.Errorf("")), nil
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Bad JSON returns an error",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`{}`, nil), nil
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
|
|
{
|
|
name: "Incorrect fields returns error",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"deleted",
|
|
"id",
|
|
"modified_time_ns",
|
|
"start_time",
|
|
"text",
|
|
"type"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920117000000000,
|
|
true,
|
|
"4ba9f836-20e8-4b8e-af51-e1363edd7b6d",
|
|
1517425994487495051,
|
|
0,
|
|
"",
|
|
""
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]}]`, nil), nil
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "two annotation response",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"start_time",
|
|
"modified_time_ns",
|
|
"text",
|
|
"type",
|
|
"id"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1516989242129417403,
|
|
"mytext",
|
|
"mytype",
|
|
"ecf3a75d-f1c0-40e8-9790-902701467e92"
|
|
],
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1517425914433539296,
|
|
"mytext2",
|
|
"mytype2",
|
|
"ea0aa94b-969a-4cd5-912a-5db61d502268"
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]`, nil), nil
|
|
},
|
|
},
|
|
want: []chronograf.Annotation{
|
|
{
|
|
EndTime: time.Unix(0, 1516920177345000000),
|
|
StartTime: time.Unix(0, 0),
|
|
Text: "mytext2",
|
|
Type: "mytype2",
|
|
ID: "ea0aa94b-969a-4cd5-912a-5db61d502268",
|
|
},
|
|
{
|
|
EndTime: time.Unix(0, 1516920177345000000),
|
|
StartTime: time.Unix(0, 0),
|
|
Text: "mytext",
|
|
Type: "mytype",
|
|
ID: "ecf3a75d-f1c0-40e8-9790-902701467e92",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "same id returns one",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"start_time",
|
|
"modified_time_ns",
|
|
"text",
|
|
"type",
|
|
"id"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1516989242129417403,
|
|
"mytext",
|
|
"mytype",
|
|
"ea0aa94b-969a-4cd5-912a-5db61d502268"
|
|
],
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1517425914433539296,
|
|
"mytext2",
|
|
"mytype2",
|
|
"ea0aa94b-969a-4cd5-912a-5db61d502268"
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]`, nil), nil
|
|
},
|
|
},
|
|
want: []chronograf.Annotation{
|
|
{
|
|
EndTime: time.Unix(0, 1516920177345000000),
|
|
StartTime: time.Unix(0, 0),
|
|
Text: "mytext2",
|
|
Type: "mytype2",
|
|
ID: "ea0aa94b-969a-4cd5-912a-5db61d502268",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no responses returns empty array",
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[ { } ]`, nil), nil
|
|
},
|
|
},
|
|
want: []chronograf.Annotation{},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := &AnnotationStore{
|
|
client: tt.client,
|
|
}
|
|
got, err := a.queryAnnotations(tt.args.ctx, tt.args.query)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("AnnotationStore.queryAnnotations() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("AnnotationStore.queryAnnotations() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnnotationStore_Update(t *testing.T) {
|
|
type fields struct {
|
|
client chronograf.TimeSeries
|
|
now Now
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
anno *chronograf.Annotation
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "no responses returns error",
|
|
fields: fields{
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[ { } ]`, nil), nil
|
|
},
|
|
WriteF: func(context.Context, []chronograf.Point) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "error writing returns error",
|
|
fields: fields{
|
|
now: func() time.Time { return time.Time{} },
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"start_time",
|
|
"modified_time_ns",
|
|
"text",
|
|
"type",
|
|
"id"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1516989242129417403,
|
|
"mytext",
|
|
"mytype",
|
|
"ecf3a75d-f1c0-40e8-9790-902701467e92"
|
|
],
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1517425914433539296,
|
|
"mytext2",
|
|
"mytype2",
|
|
"ea0aa94b-969a-4cd5-912a-5db61d502268"
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]`, nil), nil
|
|
},
|
|
WriteF: func(context.Context, []chronograf.Point) error {
|
|
return fmt.Errorf("error")
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Update with delete",
|
|
fields: fields{
|
|
now: func() time.Time { return time.Time{} },
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"start_time",
|
|
"modified_time_ns",
|
|
"text",
|
|
"type",
|
|
"id"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1516989242129417403,
|
|
"mytext",
|
|
"mytype",
|
|
"ecf3a75d-f1c0-40e8-9790-902701467e92"
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]`, nil), nil
|
|
},
|
|
WriteF: func(context.Context, []chronograf.Point) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
anno: &chronograf.Annotation{
|
|
ID: "1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Update with delete no delete",
|
|
fields: fields{
|
|
now: func() time.Time { return time.Time{} },
|
|
client: &mocks.TimeSeries{
|
|
QueryF: func(context.Context, chronograf.Query) (chronograf.Response, error) {
|
|
return mocks.NewResponse(`[
|
|
{
|
|
"series": [
|
|
{
|
|
"name": "annotations",
|
|
"columns": [
|
|
"time",
|
|
"start_time",
|
|
"modified_time_ns",
|
|
"text",
|
|
"type",
|
|
"id"
|
|
],
|
|
"values": [
|
|
[
|
|
1516920177345000000,
|
|
0,
|
|
1516989242129417403,
|
|
"mytext",
|
|
"mytype",
|
|
"ecf3a75d-f1c0-40e8-9790-902701467e92"
|
|
]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]`, nil), nil
|
|
},
|
|
WriteF: func(context.Context, []chronograf.Point) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
anno: &chronograf.Annotation{
|
|
ID: "ecf3a75d-f1c0-40e8-9790-902701467e92",
|
|
EndTime: time.Unix(0, 1516920177345000000),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := &AnnotationStore{
|
|
client: tt.fields.client,
|
|
now: tt.fields.now,
|
|
}
|
|
if err := a.Update(tt.args.ctx, tt.args.anno); (err != nil) != tt.wantErr {
|
|
t.Errorf("AnnotationStore.Update() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|