influxdb/chronograf/server/cells_test.go

993 lines
26 KiB
Go

package server
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/httprouter"
"github.com/influxdata/influxdb/v2/chronograf"
"github.com/influxdata/influxdb/v2/chronograf/mocks"
)
func Test_Cells_CorrectAxis(t *testing.T) {
t.Parallel()
axisTests := []struct {
name string
cell *chronograf.DashboardCell
shouldFail bool
}{
{
name: "correct axes",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Bounds: []string{"0", "100"},
},
"y": chronograf.Axis{
Bounds: []string{"0", "100"},
},
"y2": chronograf.Axis{
Bounds: []string{"0", "100"},
},
},
},
},
{
name: "invalid axes present",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"axis of evil": chronograf.Axis{
Bounds: []string{"666", "666"},
},
"axis of awesome": chronograf.Axis{
Bounds: []string{"1337", "31337"},
},
},
},
shouldFail: true,
},
{
name: "linear scale value",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Scale: "linear",
Bounds: []string{"0", "100"},
},
},
},
},
{
name: "log scale value",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Scale: "log",
Bounds: []string{"0", "100"},
},
},
},
},
{
name: "invalid scale value",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Scale: "potatoes",
Bounds: []string{"0", "100"},
},
},
},
shouldFail: true,
},
{
name: "base 10 axis",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Base: "10",
Bounds: []string{"0", "100"},
},
},
},
},
{
name: "base 2 axis",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Base: "2",
Bounds: []string{"0", "100"},
},
},
},
},
{
name: "invalid base",
cell: &chronograf.DashboardCell{
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Base: "all your base are belong to us",
Bounds: []string{"0", "100"},
},
},
},
shouldFail: true,
},
}
for _, test := range axisTests {
t.Run(test.name, func(tt *testing.T) {
if err := HasCorrectAxes(test.cell); err != nil && !test.shouldFail {
t.Errorf("%q: Unexpected error: err: %s", test.name, err)
} else if err == nil && test.shouldFail {
t.Errorf("%q: Expected error and received none", test.name)
}
})
}
}
func Test_Service_DashboardCells(t *testing.T) {
cellsTests := []struct {
name string
reqURL *url.URL
ctxParams map[string]string
mockResponse []chronograf.DashboardCell
expected []chronograf.DashboardCell
expectedCode int
}{
{
name: "happy path",
reqURL: &url.URL{
Path: "/chronograf/v1/dashboards/1/cells",
},
ctxParams: map[string]string{
"id": "1",
},
mockResponse: []chronograf.DashboardCell{},
expected: []chronograf.DashboardCell{},
expectedCode: http.StatusOK,
},
{
name: "cell axes should always be \"x\", \"y\", and \"y2\"",
reqURL: &url.URL{
Path: "/chronograf/v1/dashboards/1/cells",
},
ctxParams: map[string]string{
"id": "1",
},
mockResponse: []chronograf.DashboardCell{
{
ID: "3899be5a-f6eb-4347-b949-de2f4fbea859",
X: 0,
Y: 0,
W: 4,
H: 4,
Name: "CPU",
Type: "bar",
Queries: []chronograf.DashboardQuery{},
Axes: map[string]chronograf.Axis{},
},
},
expected: []chronograf.DashboardCell{
{
ID: "3899be5a-f6eb-4347-b949-de2f4fbea859",
X: 0,
Y: 0,
W: 4,
H: 4,
Name: "CPU",
Type: "bar",
Queries: []chronograf.DashboardQuery{},
CellColors: []chronograf.CellColor{},
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Bounds: []string{"", ""},
},
"y": chronograf.Axis{
Bounds: []string{"", ""},
},
"y2": chronograf.Axis{
Bounds: []string{"", ""},
},
},
},
},
expectedCode: http.StatusOK,
},
}
for _, test := range cellsTests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
// setup context with params
params := httprouter.Params{}
for k, v := range test.ctxParams {
params = append(params, httprouter.Param{
Key: k,
Value: v,
})
}
ctx := context.WithValue(
context.Background(),
httprouter.ParamsKey,
params,
)
// setup response recorder and request
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", test.reqURL.RequestURI(), strings.NewReader("")).WithContext(ctx)
// setup mock DashboardCells store and logger
tlog := &mocks.TestLogger{}
svc := &Service{
Store: &mocks.Store{
DashboardsStore: &mocks.DashboardsStore{
GetF: func(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{
ID: chronograf.DashboardID(1),
Cells: test.mockResponse,
Templates: []chronograf.Template{},
Name: "empty dashboard",
}, nil
},
},
},
Logger: tlog,
}
// invoke DashboardCell handler
svc.DashboardCells(rr, req)
// setup frame to decode response into
respFrame := []struct {
chronograf.DashboardCell
Links json.RawMessage `json:"links"` // ignore links
}{}
// decode response
resp := rr.Result()
if resp.StatusCode != test.expectedCode {
tlog.Dump(t)
t.Fatalf("%q - Status codes do not match. Want %d (%s), Got %d (%s)", test.name, test.expectedCode, http.StatusText(test.expectedCode), resp.StatusCode, http.StatusText(resp.StatusCode))
}
if err := json.NewDecoder(resp.Body).Decode(&respFrame); err != nil {
t.Fatalf("%q - Error unmarshalling response body: err: %s", test.name, err)
}
// extract actual
actual := []chronograf.DashboardCell{}
for _, rsp := range respFrame {
actual = append(actual, rsp.DashboardCell)
}
// compare actual and expected
if !cmp.Equal(actual, test.expected) {
t.Fatalf("%q - Dashboard Cells do not match: diff: %s", test.name, cmp.Diff(actual, test.expected))
}
})
}
}
func TestHasCorrectColors(t *testing.T) {
tests := []struct {
name string
c *chronograf.DashboardCell
wantErr bool
}{
{
name: "min type is valid",
c: &chronograf.DashboardCell{
CellColors: []chronograf.CellColor{
{
Type: "min",
Hex: "#FFFFFF",
},
},
},
},
{
name: "max type is valid",
c: &chronograf.DashboardCell{
CellColors: []chronograf.CellColor{
{
Type: "max",
Hex: "#FFFFFF",
},
},
},
},
{
name: "threshold type is valid",
c: &chronograf.DashboardCell{
CellColors: []chronograf.CellColor{
{
Type: "threshold",
Hex: "#FFFFFF",
},
},
},
},
{
name: "invalid color type",
c: &chronograf.DashboardCell{
CellColors: []chronograf.CellColor{
{
Type: "unknown",
Hex: "#FFFFFF",
},
},
},
wantErr: true,
},
{
name: "invalid color hex",
c: &chronograf.DashboardCell{
CellColors: []chronograf.CellColor{
{
Type: "min",
Hex: "bad",
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := HasCorrectColors(tt.c); (err != nil) != tt.wantErr {
t.Errorf("HasCorrectColors() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestService_ReplaceDashboardCell(t *testing.T) {
tests := []struct {
name string
DashboardsStore chronograf.DashboardsStore
ID string
CID string
w *httptest.ResponseRecorder
r *http.Request
want string
}{
{
name: "update cell retains query config",
ID: "1",
CID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
DashboardsStore: &mocks.DashboardsStore{
UpdateF: func(ctx context.Context, target chronograf.Dashboard) error {
return nil
},
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{
ID: ID,
Cells: []chronograf.DashboardCell{
{
ID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
W: 4,
H: 4,
Name: "Untitled Cell",
Queries: []chronograf.DashboardQuery{
{
Command: "SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)",
QueryConfig: chronograf.QueryConfig{
ID: "3cd3eaa4-a4b8-44b3-b69e-0c7bf6b91d9e",
Database: "telegraf",
Measurement: "cpu",
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Value: "mean",
Type: "func",
Alias: "mean_usage_user",
Args: []chronograf.Field{
{
Value: "usage_user",
Type: "field",
},
},
},
},
Tags: map[string][]string{
"cpu": {
"ChristohersMBP2.lan",
},
},
GroupBy: chronograf.GroupBy{
Time: "2s",
Tags: []string{},
},
AreTagsAccepted: true,
Fill: "null",
RawText: strPtr("SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)"),
Range: &chronograf.DurationRange{
Lower: "now() - 15m"},
Shifts: []chronograf.TimeShift{},
},
},
},
Axes: map[string]chronograf.Axis{
"x": {
Bounds: []string{"", ""},
},
"y": {
Bounds: []string{"", ""},
},
"y2": {
Bounds: []string{"", ""},
},
},
Type: "line",
CellColors: []chronograf.CellColor{
{
ID: "0",
Type: "min",
Hex: "#00C9FF",
Name: "laser",
Value: "0",
},
{
ID: "1",
Type: "max",
Hex: "#9394FF",
Name: "comet",
Value: "100",
},
},
},
},
}, nil
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`
{
"i": "3c5c4102-fa40-4585-a8f9-917c77e37192",
"x": 0,
"y": 0,
"w": 4,
"h": 4,
"name": "Untitled Cell",
"queries": [
{
"queryConfig": {
"id": "3cd3eaa4-a4b8-44b3-b69e-0c7bf6b91d9e",
"database": "telegraf",
"measurement": "cpu",
"retentionPolicy": "autogen",
"fields": [
{
"value": "mean",
"type": "func",
"alias": "mean_usage_user",
"args": [{"value": "usage_user", "type": "field", "alias": ""}]
}
],
"tags": {"cpu": ["ChristohersMBP2.lan"]},
"groupBy": {"time": "2s", "tags": []},
"areTagsAccepted": true,
"fill": "null",
"rawText":
"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)",
"range": {"upper": "", "lower": "now() - 15m"},
"shifts": []
},
"query":
"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)",
"source": null
}
],
"axes": {
"x": {
"bounds": ["",""],
"label": "",
"prefix": "",
"suffix": "",
"base": "",
"scale": ""
},
"y": {
"bounds": ["",""],
"label": "",
"prefix": "",
"suffix": "",
"base": "",
"scale": ""
},
"y2": {
"bounds": ["",""],
"label": "",
"prefix": "",
"suffix": "",
"base": "",
"scale": ""
}
},
"type": "line",
"colors": [
{"type": "min", "hex": "#00C9FF", "id": "0", "name": "laser", "value": "0"},
{
"type": "max",
"hex": "#9394FF",
"id": "1",
"name": "comet",
"value": "100"
}
],
"links": {
"self":
"/chronograf/v1/dashboards/6/cells/3c5c4102-fa40-4585-a8f9-917c77e37192"
}
}
`))),
want: `{"i":"3c5c4102-fa40-4585-a8f9-917c77e37192","x":0,"y":0,"w":4,"h":4,"name":"Untitled Cell","queries":[{"query":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","queryConfig":{"id":"3cd3eaa4-a4b8-44b3-b69e-0c7bf6b91d9e","database":"telegraf","measurement":"cpu","retentionPolicy":"autogen","fields":[{"value":"mean","type":"func","alias":"mean_usage_user","args":[{"value":"usage_user","type":"field","alias":""}]}],"tags":{"cpu":["ChristohersMBP2.lan"]},"groupBy":{"time":"2s","tags":[]},"areTagsAccepted":true,"fill":"null","rawText":"SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time \u003e :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)","range":{"upper":"","lower":"now() - 15m"},"shifts":[]},"source":""}],"axes":{"x":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""},"y2":{"bounds":["",""],"label":"","prefix":"","suffix":"","base":"","scale":""}},"type":"line","colors":[{"id":"0","type":"min","hex":"#00C9FF","name":"laser","value":"0"},{"id":"1","type":"max","hex":"#9394FF","name":"comet","value":"100"}],"legend":{},"tableOptions":{"verticalTimeAxis":false,"sortBy":{"internalName":"","displayName":"","visible":false},"wrapping":"","fixFirstColumn":false},"fieldOptions":null,"timeFormat":"","decimalPlaces":{"isEnforced":false,"digits":0},"links":{"self":"/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192"}}
`,
},
{
name: "dashboard doesn't exist",
ID: "1",
DashboardsStore: &mocks.DashboardsStore{
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{}, fmt.Errorf("doesn't exist")
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("PUT", "/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192", nil),
want: `{"code":404,"message":"ID 1 not found"}`,
},
{
name: "cell doesn't exist",
ID: "1",
CID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
DashboardsStore: &mocks.DashboardsStore{
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{}, nil
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("PUT", "/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192", nil),
want: `{"code":404,"message":"ID 3c5c4102-fa40-4585-a8f9-917c77e37192 not found"}`,
},
{
name: "invalid query config",
ID: "1",
CID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
DashboardsStore: &mocks.DashboardsStore{
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{
ID: ID,
Cells: []chronograf.DashboardCell{
{
ID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
},
},
}, nil
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("PUT", "/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192", bytes.NewReader([]byte(`{
"i": "3c5c4102-fa40-4585-a8f9-917c77e37192",
"x": 0,
"y": 0,
"w": 4,
"h": 4,
"name": "Untitled Cell",
"queries": [
{
"queryConfig": {
"fields": [
{
"value": "invalid",
"type": "invalidType"
}
]
}
}
]
}`))),
want: `{"code":422,"message":"invalid field type \"invalidType\" ; expect func, field, integer, number, regex, wildcard"}`,
},
{
name: "JSON is not parsable",
ID: "1",
CID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
DashboardsStore: &mocks.DashboardsStore{
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{
ID: ID,
Cells: []chronograf.DashboardCell{
{
ID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
},
},
}, nil
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("PUT", "/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192", nil),
want: `{"code":400,"message":"unparsable JSON"}`,
},
{
name: "not able to update store returns error message",
ID: "1",
CID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
DashboardsStore: &mocks.DashboardsStore{
UpdateF: func(ctx context.Context, target chronograf.Dashboard) error {
return fmt.Errorf("error")
},
GetF: func(ctx context.Context, ID chronograf.DashboardID) (chronograf.Dashboard, error) {
return chronograf.Dashboard{
ID: ID,
Cells: []chronograf.DashboardCell{
{
ID: "3c5c4102-fa40-4585-a8f9-917c77e37192",
},
},
}, nil
},
},
w: httptest.NewRecorder(),
r: httptest.NewRequest("PUT", "/chronograf/v1/dashboards/1/cells/3c5c4102-fa40-4585-a8f9-917c77e37192", bytes.NewReader([]byte(`{
"i": "3c5c4102-fa40-4585-a8f9-917c77e37192",
"x": 0,
"y": 0,
"w": 4,
"h": 4,
"name": "Untitled Cell",
"queries": [
{
"queryConfig": {
"fields": [
{
"value": "usage_user",
"type": "field"
}
]
}
}
]
}`))),
want: `{"code":500,"message":"Error updating cell 3c5c4102-fa40-4585-a8f9-917c77e37192 in dashboard 1: error"}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Service{
Store: &mocks.Store{
DashboardsStore: tt.DashboardsStore,
},
Logger: &mocks.TestLogger{},
}
tt.r = WithContext(tt.r.Context(), tt.r, map[string]string{
"id": tt.ID,
"cid": tt.CID,
})
tt.r = tt.r.WithContext(context.WithValue(
context.TODO(),
httprouter.ParamsKey,
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
{
Key: "cid",
Value: tt.CID,
},
}))
s.ReplaceDashboardCell(tt.w, tt.r)
got := tt.w.Body.String()
if got != tt.want {
t.Errorf("ReplaceDashboardCell() = got/want\n%s\n%s\n", got, tt.want)
}
})
}
}
func strPtr(s string) *string {
return &s
}
func Test_newCellResponses(t *testing.T) {
tests := []struct {
name string
dID chronograf.DashboardID
dcells []chronograf.DashboardCell
want []dashboardCellResponse
}{
{
name: "all fields set",
dID: chronograf.DashboardID(1),
dcells: []chronograf.DashboardCell{
chronograf.DashboardCell{
ID: "445f8dc0-4d73-4168-8477-f628690d18a3",
X: 0,
Y: 0,
W: 4,
H: 4,
Name: "Untitled Cell",
Queries: []chronograf.DashboardQuery{
{
Command: "SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)",
Label: "",
QueryConfig: chronograf.QueryConfig{
ID: "8d5ec6da-13a5-423e-9026-7bc45649766c",
Database: "telegraf",
Measurement: "cpu",
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Value: "mean",
Type: "func",
Alias: "mean_usage_user",
Args: []chronograf.Field{
{
Value: "usage_user",
Type: "field",
Alias: "",
},
},
},
},
Tags: map[string][]string{"cpu": []string{"ChristohersMBP2.lan"}},
GroupBy: chronograf.GroupBy{
Time: "2s",
},
AreTagsAccepted: true,
Fill: "null",
RawText: strPtr("SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)"),
Range: &chronograf.DurationRange{
Lower: "now() - 15m",
},
},
Source: "",
},
},
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{},
"y": chronograf.Axis{},
"y2": chronograf.Axis{},
},
Type: "line",
CellColors: []chronograf.CellColor{
chronograf.CellColor{ID: "0", Type: "min", Hex: "#00C9FF", Name: "laser", Value: "0"},
chronograf.CellColor{ID: "1", Type: "max", Hex: "#9394FF", Name: "comet", Value: "100"},
},
Legend: chronograf.Legend{
Type: "static",
Orientation: "bottom",
},
},
},
want: []dashboardCellResponse{
{
DashboardCell: chronograf.DashboardCell{
ID: "445f8dc0-4d73-4168-8477-f628690d18a3",
W: 4,
H: 4,
Name: "Untitled Cell",
Queries: []chronograf.DashboardQuery{
{
Command: "SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)",
QueryConfig: chronograf.QueryConfig{
ID: "8d5ec6da-13a5-423e-9026-7bc45649766c",
Database: "telegraf",
Measurement: "cpu",
RetentionPolicy: "autogen",
Fields: []chronograf.Field{
{
Value: "mean",
Type: "func",
Alias: "mean_usage_user",
Args: []chronograf.Field{
{
Value: "usage_user",
Type: "field",
},
},
},
},
Tags: map[string][]string{"cpu": {"ChristohersMBP2.lan"}},
GroupBy: chronograf.GroupBy{
Time: "2s",
},
AreTagsAccepted: true,
Fill: "null",
RawText: strPtr("SELECT mean(\"usage_user\") AS \"mean_usage_user\" FROM \"telegraf\".\"autogen\".\"cpu\" WHERE time > :dashboardTime: AND \"cpu\"=:cpu: GROUP BY :interval: FILL(null)"),
Range: &chronograf.DurationRange{
Lower: "now() - 15m",
},
},
},
},
Axes: map[string]chronograf.Axis{
"x": {},
"y": {},
"y2": {},
},
Type: "line",
CellColors: []chronograf.CellColor{
{
ID: "0",
Type: "min",
Hex: "#00C9FF",
Name: "laser",
Value: "0",
},
{
ID: "1",
Type: "max",
Hex: "#9394FF",
Name: "comet",
Value: "100",
},
},
Legend: chronograf.Legend{
Type: "static",
Orientation: "bottom",
},
},
Links: dashboardCellLinks{
Self: "/chronograf/v1/dashboards/1/cells/445f8dc0-4d73-4168-8477-f628690d18a3"},
},
},
},
{
name: "nothing set",
dID: chronograf.DashboardID(1),
dcells: []chronograf.DashboardCell{
chronograf.DashboardCell{
ID: "445f8dc0-4d73-4168-8477-f628690d18a3",
X: 0,
Y: 0,
W: 4,
H: 4,
Name: "Untitled Cell",
},
},
want: []dashboardCellResponse{
{
DashboardCell: chronograf.DashboardCell{
ID: "445f8dc0-4d73-4168-8477-f628690d18a3",
W: 4,
H: 4,
Name: "Untitled Cell",
Queries: []chronograf.DashboardQuery{},
Axes: map[string]chronograf.Axis{
"x": chronograf.Axis{
Bounds: []string{"", ""},
},
"y": chronograf.Axis{
Bounds: []string{"", ""},
},
"y2": chronograf.Axis{
Bounds: []string{"", ""},
},
},
CellColors: []chronograf.CellColor{},
Legend: chronograf.Legend{},
},
Links: dashboardCellLinks{
Self: "/chronograf/v1/dashboards/1/cells/445f8dc0-4d73-4168-8477-f628690d18a3"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := newCellResponses(tt.dID, tt.dcells); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newCellResponses() = got-/want+ %s", cmp.Diff(got, tt.want))
}
})
}
}
func TestHasCorrectLegend(t *testing.T) {
tests := []struct {
name string
c *chronograf.DashboardCell
wantErr bool
}{
{
name: "empty legend is ok",
c: &chronograf.DashboardCell{},
},
{
name: "must have both an orientation and type",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
},
},
wantErr: true,
},
{
name: "must have both a type and orientation",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Orientation: "bottom",
},
},
wantErr: true,
},
{
name: "invalid types",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "no such type",
Orientation: "bottom",
},
},
wantErr: true,
},
{
name: "invalid orientation",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
Orientation: "no such orientation",
},
},
wantErr: true,
},
{
name: "orientation bottom valid",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
Orientation: "bottom",
},
},
},
{
name: "orientation top valid",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
Orientation: "top",
},
},
},
{
name: "orientation right valid",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
Orientation: "right",
},
},
},
{
name: "orientation left valid",
c: &chronograf.DashboardCell{
Legend: chronograf.Legend{
Type: "static",
Orientation: "left",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := HasCorrectLegend(tt.c); (err != nil) != tt.wantErr {
t.Errorf("HasCorrectLegend() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}