diff --git a/mocks/dashboards.go b/mocks/dashboards.go new file mode 100644 index 0000000000..a038f468ba --- /dev/null +++ b/mocks/dashboards.go @@ -0,0 +1,37 @@ +package mocks + +import ( + "context" + + "github.com/influxdata/chronograf" +) + +var _ chronograf.DashboardsStore = &DashboardsStore{} + +type DashboardsStore struct { + AddF func(ctx context.Context, newDashboard chronograf.Dashboard) (chronograf.Dashboard, error) + AllF func(ctx context.Context) ([]chronograf.Dashboard, error) + DeleteF func(ctx context.Context, target chronograf.Dashboard) error + GetF func(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) + UpdateF func(ctx context.Context, target chronograf.Dashboard) error +} + +func (d *DashboardsStore) Add(ctx context.Context, newDashboard chronograf.Dashboard) (chronograf.Dashboard, error) { + return d.AddF(ctx, newDashboard) +} + +func (d *DashboardsStore) All(ctx context.Context) ([]chronograf.Dashboard, error) { + return d.AllF(ctx) +} + +func (d *DashboardsStore) Delete(ctx context.Context, target chronograf.Dashboard) error { + return d.DeleteF(ctx, target) +} + +func (d *DashboardsStore) Get(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) { + return d.GetF(ctx, id) +} + +func (d *DashboardsStore) Update(ctx context.Context, target chronograf.Dashboard) error { + return d.UpdateF(ctx, target) +} diff --git a/mocks/logger.go b/mocks/logger.go index 46c7bae9f6..f2926e09a3 100644 --- a/mocks/logger.go +++ b/mocks/logger.go @@ -3,6 +3,7 @@ package mocks import ( "fmt" "io" + "testing" "github.com/influxdata/chronograf" ) @@ -72,3 +73,11 @@ func (tl *TestLogger) stringifyArg(arg interface{}) []byte { return []byte("UNKNOWN") } } + +// Dump dumps out logs into a given testing.T's logs +func (tl *TestLogger) Dump(t *testing.T) { + t.Log("== Dumping Test Logs ==") + for _, msg := range tl.Messages { + t.Logf("lvl: %s, msg: %s", msg.Level, msg.Body) + } +} diff --git a/server/cells.go b/server/cells.go index f60437c910..a16cb702d5 100644 --- a/server/cells.go +++ b/server/cells.go @@ -33,6 +33,20 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC if len(cell.Queries) == 0 { cell.Queries = make([]chronograf.DashboardQuery, 0) } + + // ensure x, y, and y2 axes always returned + labels := []string{"x", "y", "y2"} + if cell.Axes == nil { + cell.Axes = make(map[string]chronograf.Axis, len(labels)) + } + + for _, lbl := range labels { + _, found := cell.Axes[lbl] + if !found { + cell.Axes[lbl] = chronograf.Axis{} + } + } + cells[i] = dashboardCellResponse{ DashboardCell: cell, Links: dashboardCellLinks{ diff --git a/server/cells_test.go b/server/cells_test.go index a3ae00e9c1..1be5981f9f 100644 --- a/server/cells_test.go +++ b/server/cells_test.go @@ -1,9 +1,18 @@ package server_test import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" + "github.com/bouk/httprouter" + "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/mocks" "github.com/influxdata/chronograf/server" ) @@ -58,3 +67,131 @@ func Test_Cells_CorrectAxis(t *testing.T) { }) } } + +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 + }{ + { + "happy path", + &url.URL{ + Path: "/chronograf/v1/dashboards/1/cells", + }, + map[string]string{ + "id": "1", + }, + []chronograf.DashboardCell{}, + []chronograf.DashboardCell{}, + http.StatusOK, + }, + { + "cell axes should always be \"x\", \"y\", and \"y2\"", + &url.URL{ + Path: "/chronograf/v1/dashboards/1/cells", + }, + map[string]string{ + "id": "1", + }, + []chronograf.DashboardCell{ + { + ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "CPU", + Queries: []chronograf.DashboardQuery{}, + Axes: map[string]chronograf.Axis{}, + }, + }, + []chronograf.DashboardCell{ + { + ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "CPU", + Queries: []chronograf.DashboardQuery{}, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{}, + "y": chronograf.Axis{}, + "y2": chronograf.Axis{}, + }, + }, + }, + http.StatusOK, + }, + } + + for _, test := range cellsTests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + // setup context with params + ctx := context.Background() + params := httprouter.Params{} + for k, v := range test.ctxParams { + params = append(params, httprouter.Param{k, v}) + } + ctx = httprouter.WithParams(ctx, 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 := &server.Service{ + 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 unmarshaling 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)) + } + }) + } +} diff --git a/server/dashboards_test.go b/server/dashboards_test.go index 5c92083451..d328addad5 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -273,6 +273,7 @@ func Test_newDashboardResponse(t *testing.T) { "y": chronograf.Axis{ Bounds: []string{"2", "95"}, }, + "y2": chronograf.Axis{}, }, }, }, @@ -284,6 +285,11 @@ func Test_newDashboardResponse(t *testing.T) { ID: "b", W: 4, H: 4, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{}, + "y": chronograf.Axis{}, + "y2": chronograf.Axis{}, + }, Queries: []chronograf.DashboardQuery{ { Command: "SELECT winning_horses from grays_sports_alamanc where time > now() - 15m",