From cd88b5cecdac026b42e1947e3d35ba1e7ecad0dd Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Sat, 17 Feb 2018 14:01:18 -0600 Subject: [PATCH] Add tests for writing to influxdb --- influx/annotations.go | 5 + influx/annotations_test.go | 209 +++++++++++++++++++++++++++++++++++-- influx/influx_test.go | 151 +++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 6 deletions(-) diff --git a/influx/annotations.go b/influx/annotations.go index cbfc3cf1b..fa4cfccb3 100644 --- a/influx/annotations.go +++ b/influx/annotations.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "sort" "time" "github.com/influxdata/chronograf/id" @@ -256,5 +257,9 @@ func (r *influxResults) Annotations() (res []chronograf.Annotation, err error) { res = append(res, a.Annotation) } + sort.Slice(res, func(i int, j int) bool { + return res[i].StartTime.Before(res[j].StartTime) || res[i].ID < res[j].ID + }) + return res, err } diff --git a/influx/annotations_test.go b/influx/annotations_test.go index 814bd9322..1d6b41081 100644 --- a/influx/annotations_test.go +++ b/influx/annotations_test.go @@ -376,16 +376,16 @@ func TestAnnotationStore_queryAnnotations(t *testing.T) { { EndTime: time.Unix(0, 1516920177345000000), StartTime: time.Unix(0, 0), - Text: "mytext", - Type: "mytype", - ID: "ecf3a75d-f1c0-40e8-9790-902701467e92", + Text: "mytext2", + Type: "mytype2", + ID: "ea0aa94b-969a-4cd5-912a-5db61d502268", }, { EndTime: time.Unix(0, 1516920177345000000), StartTime: time.Unix(0, 0), - Text: "mytext2", - Type: "mytype2", - ID: "ea0aa94b-969a-4cd5-912a-5db61d502268", + Text: "mytext", + Type: "mytype", + ID: "ecf3a75d-f1c0-40e8-9790-902701467e92", }, }, }, @@ -466,3 +466,200 @@ func TestAnnotationStore_queryAnnotations(t *testing.T) { }) } } + +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) + } + }) + } +} diff --git a/influx/influx_test.go b/influx/influx_test.go index d165ccb86..d4814bf6c 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -14,6 +14,7 @@ import ( "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/influx" "github.com/influxdata/chronograf/log" + "github.com/influxdata/chronograf/mocks" ) // NewClient initializes an HTTP Client for InfluxDB. @@ -395,3 +396,153 @@ func TestClient_Roles(t *testing.T) { t.Errorf("Client.Roles() want error") } } + +func TestClient_write(t *testing.T) { + type fields struct { + Authorizer influx.Authorizer + InsecureSkipVerify bool + Logger chronograf.Logger + } + type args struct { + ctx context.Context + point *chronograf.Point + } + tests := []struct { + name string + fields fields + args args + body string + wantErr bool + }{ + { + name: "write point to influxdb", + fields: fields{ + Logger: mocks.NewLogger(), + }, + args: args{ + ctx: context.Background(), + point: &chronograf.Point{ + Database: "mydb", + RetentionPolicy: "myrp", + Measurement: "mymeas", + Time: 10, + Tags: map[string]string{ + "tag1": "value1", + "tag2": "value2", + }, + Fields: map[string]interface{}{ + "field1": "value1", + }, + }, + }, + }, + { + name: "point without fields", + args: args{ + ctx: context.Background(), + point: &chronograf.Point{}, + }, + wantErr: true, + }, + { + name: "hinted handoff errors are not errors really.", + fields: fields{ + Logger: mocks.NewLogger(), + }, + args: args{ + ctx: context.Background(), + point: &chronograf.Point{ + Database: "mydb", + RetentionPolicy: "myrp", + Measurement: "mymeas", + Time: 10, + Tags: map[string]string{ + "tag1": "value1", + "tag2": "value2", + }, + Fields: map[string]interface{}{ + "field1": "value1", + }, + }, + }, + body: `{"error":"hinted handoff queue not empty"}`, + }, + { + name: "database not found creates a new db", + fields: fields{ + Logger: mocks.NewLogger(), + }, + args: args{ + ctx: context.Background(), + point: &chronograf.Point{ + Database: "mydb", + RetentionPolicy: "myrp", + Measurement: "mymeas", + Time: 10, + Tags: map[string]string{ + "tag1": "value1", + "tag2": "value2", + }, + Fields: map[string]interface{}{ + "field1": "value1", + }, + }, + }, + body: `{"error":"database not found"}`, + }, + { + name: "error from database reported", + fields: fields{ + Logger: mocks.NewLogger(), + }, + args: args{ + ctx: context.Background(), + point: &chronograf.Point{ + Database: "mydb", + RetentionPolicy: "myrp", + Measurement: "mymeas", + Time: 10, + Tags: map[string]string{ + "tag1": "value1", + "tag2": "value2", + }, + Fields: map[string]interface{}{ + "field1": "value1", + }, + }, + }, + body: `{"error":"oh no!"}`, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + retry := 0 // if the retry is > 0 then we don't error + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.RequestURI, "/write") { + if tt.body == "" || retry > 0 { + rw.WriteHeader(http.StatusNoContent) + return + } + retry++ + rw.WriteHeader(http.StatusBadRequest) + rw.Write([]byte(tt.body)) + return + } + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{"results":[{}]}`)) + })) + defer ts.Close() + u, _ := url.Parse(ts.URL) + c := &influx.Client{ + URL: u, + Authorizer: tt.fields.Authorizer, + InsecureSkipVerify: tt.fields.InsecureSkipVerify, + Logger: tt.fields.Logger, + } + if err := c.Write(tt.args.ctx, tt.args.point); (err != nil) != tt.wantErr { + t.Errorf("Client.write() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}