Enforce presence of "x", "y", and "y2" axes
Certain aspects of the frontend requires the presence of these three axes, so part of the contract established is that the backend will always provide them. Since we centralize creation of dashboardCellResponses, this is where these axes are added to all cell responses. Additionally, because there was previously no coverage over the dashboard cells endpoints, a test has been added to cover the DashboardCells method of Service.pull/10616/head
parent
2ff3e27e1f
commit
4391004e7f
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue