chronograf/influx/annotations_test.go

646 lines
14 KiB
Go

package influx
import (
"context"
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/chronograf"
"github.com/influxdata/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",
},
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",
},
},
},
1: {
name: "convert annotation to point with start/end time",
anno: &chronograf.Annotation{
ID: "1",
Text: "mytext",
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",
},
},
},
}
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": "",
},
},
},
}
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",
],
"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",
"id"
],
"values": [
[
1516920177345000000,
0,
1516989242129417403,
"mytext",
"ecf3a75d-f1c0-40e8-9790-902701467e92"
],
[
1516920177345000000,
0,
1517425914433539296,
"mytext2",
"ea0aa94b-969a-4cd5-912a-5db61d502268"
]
]
}
]
}
]`, nil), nil
},
},
want: []chronograf.Annotation{
{
EndTime: time.Unix(0, 1516920177345000000),
StartTime: time.Unix(0, 0),
Text: "mytext2",
ID: "ea0aa94b-969a-4cd5-912a-5db61d502268",
Tags: chronograf.AnnotationTags{},
},
{
EndTime: time.Unix(0, 1516920177345000000),
StartTime: time.Unix(0, 0),
Text: "mytext",
ID: "ecf3a75d-f1c0-40e8-9790-902701467e92",
Tags: chronograf.AnnotationTags{},
},
},
},
{
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",
"id"
],
"values": [
[
1516920177345000000,
0,
1516989242129417403,
"mytext",
"ea0aa94b-969a-4cd5-912a-5db61d502268"
],
[
1516920177345000000,
0,
1517425914433539296,
"mytext2",
"ea0aa94b-969a-4cd5-912a-5db61d502268"
]
]
}
]
}
]`, nil), nil
},
},
want: []chronograf.Annotation{
{
EndTime: time.Unix(0, 1516920177345000000),
StartTime: time.Unix(0, 0),
Text: "mytext2",
ID: "ea0aa94b-969a-4cd5-912a-5db61d502268",
Tags: chronograf.AnnotationTags{},
},
},
},
{
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",
"id"
],
"values": [
[
1516920177345000000,
0,
1516989242129417403,
"mytext",
"ecf3a75d-f1c0-40e8-9790-902701467e92"
],
[
1516920177345000000,
0,
1517425914433539296,
"mytext2",
"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",
"id"
],
"values": [
[
1516920177345000000,
0,
1516989242129417403,
"mytext",
"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",
"id"
],
"values": [
[
1516920177345000000,
0,
1516989242129417403,
"mytext",
"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)
}
})
}
}