Skip to content

Commit 0259f2d

Browse files
Merge pull request #1 from NeedleInAJayStack/test/mocks
Adds test mocks
2 parents c5cc3a5 + 2fcef12 commit 0259f2d

File tree

6 files changed

+149
-43
lines changed

6 files changed

+149
-43
lines changed

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require github.com/grafana/grafana-plugin-sdk-go v0.149.1
66

77
require (
88
github.com/NeedleInAJayStack/haystack v0.1.6
9-
github.com/joho/godotenv v1.5.1
9+
github.com/google/go-cmp v0.5.9
1010
)
1111

1212
require (
@@ -26,7 +26,6 @@ require (
2626
github.com/golang/protobuf v1.5.2 // indirect
2727
github.com/golang/snappy v0.0.3 // indirect
2828
github.com/google/flatbuffers v2.0.0+incompatible // indirect
29-
github.com/google/go-cmp v0.5.9 // indirect
3029
github.com/google/uuid v1.3.0 // indirect
3130
github.com/gorilla/mux v1.8.0 // indirect
3231
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,6 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe
204204
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
205205
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
206206
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
207-
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
208-
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
209207
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
210208
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
211209
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=

pkg/plugin/datasource.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func NewDatasource(settings backend.DataSourceInstanceSettings) (instancemgmt.In
5656
// Datasource is an example datasource which can respond to data queries, reports
5757
// its health and has streaming skills.
5858
type Datasource struct {
59-
client *client.Client
59+
client HaystackClient
6060
}
6161

6262
type Options struct {

pkg/plugin/datasource_test.go

Lines changed: 130 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,120 @@ package plugin
33
import (
44
"context"
55
"encoding/json"
6-
"fmt"
7-
"os"
86
"testing"
97
"time"
108

11-
"github.com/NeedleInAJayStack/haystack/client"
9+
"github.com/NeedleInAJayStack/haystack"
10+
"github.com/google/go-cmp/cmp"
1211
"github.com/grafana/grafana-plugin-sdk-go/backend"
13-
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
1412
"github.com/grafana/grafana-plugin-sdk-go/data"
15-
"github.com/joho/godotenv"
1613
)
1714

18-
// To run these tests, do the following:
19-
// 1. Start a Haystack server you can access
20-
// 1. Set up a `.env` file in the `/pkg` directory with these env vars: `TEST_URL`, `TEST_USERNAME`, `TEST_PASSWORD`
21-
2215
func TestQueryData_Eval(t *testing.T) {
23-
data := getResponse(
16+
response := haystack.NewGridBuilder()
17+
response.AddCol("a", map[string]haystack.Val{})
18+
response.AddCol("b", map[string]haystack.Val{})
19+
response.AddRow([]haystack.Val{haystack.NewStr("a"), haystack.NewStr("b")})
20+
21+
client := &testHaystackClient{
22+
evalResponse: response.ToGrid(),
23+
}
24+
25+
actual := getResponse(
26+
client,
2427
&QueryModel{
2528
Type: "Eval",
26-
Eval: "[{ts: now()-1hr, v0: 0}, {ts: now(), v0: 10}].toGrid",
29+
Eval: "{a: \"a\", b: \"b\"}",
2730
},
28-
backend.TimeRange{},
2931
t,
3032
)
31-
table, _ := data.StringTable(10, 100)
32-
fmt.Printf("frame: %v\n", table)
33+
34+
aVal := "a"
35+
bVal := "b"
36+
expected := data.NewFrame("",
37+
data.NewField("a", nil, []*string{&aVal}).SetConfig(&data.FieldConfig{DisplayName: "a"}),
38+
data.NewField("b", nil, []*string{&bVal}).SetConfig(&data.FieldConfig{DisplayName: "b"}),
39+
)
40+
41+
if !cmp.Equal(actual, expected, data.FrameTestCompareOptions()...) {
42+
t.Error(cmp.Diff(actual, expected, data.FrameTestCompareOptions()...))
43+
}
3344
}
3445

35-
func TestQueryData_Eval_Variables(t *testing.T) {
36-
data := getResponse(
46+
func TestQueryData_HisRead(t *testing.T) {
47+
response := haystack.NewGridBuilder()
48+
response.AddCol("ts", map[string]haystack.Val{})
49+
response.AddCol("v0", map[string]haystack.Val{})
50+
response.AddRow([]haystack.Val{haystack.NewDateTimeFromGo(time.Unix(0, 0)), haystack.NewNumber(5, "kWh")})
51+
52+
client := &testHaystackClient{
53+
hisReadResponse: response.ToGrid(),
54+
}
55+
56+
actual := getResponse(
57+
client,
3758
&QueryModel{
38-
Type: "Eval",
39-
Eval: "[{ts: $__timeRange_start, v0: 0}, {ts: $__timeRange_end, v0: 10}].toGrid",
40-
},
41-
backend.TimeRange{
42-
From: time.Now().Add(-1 * time.Hour),
43-
To: time.Now(),
59+
Type: "HisRead",
60+
HisRead: "abcdefg-12345678",
4461
},
4562
t,
4663
)
47-
table, _ := data.StringTable(10, 100)
48-
fmt.Printf("frame: %v\n", table)
64+
65+
tsVal := time.Unix(0, 0)
66+
v0Val := 5.0
67+
expected := data.NewFrame("",
68+
data.NewField("ts", nil, []*time.Time{&tsVal}).SetConfig(&data.FieldConfig{DisplayName: "ts"}),
69+
data.NewField("v0", nil, []*float64{&v0Val}).SetConfig(&data.FieldConfig{DisplayName: "v0"}),
70+
)
71+
72+
if !cmp.Equal(actual, expected, data.FrameTestCompareOptions()...) {
73+
t.Error(cmp.Diff(actual, expected, data.FrameTestCompareOptions()...))
74+
}
4975
}
5076

51-
func getResponse(queryModel *QueryModel, timeRange backend.TimeRange, t *testing.T) data.Frame {
52-
err := godotenv.Load("../.env")
53-
if err != nil {
54-
log.DefaultLogger.Warn(".env file not found, falling back to local environment")
77+
func TestQueryData_Read(t *testing.T) {
78+
response := haystack.NewGridBuilder()
79+
response.AddCol("id", map[string]haystack.Val{})
80+
response.AddCol("dis", map[string]haystack.Val{})
81+
response.AddCol("ahu", map[string]haystack.Val{})
82+
response.AddRow([]haystack.Val{
83+
haystack.NewRef("abcdefg-12345678", "AHU-1"),
84+
haystack.NewStr("AHU-1"),
85+
haystack.NewMarker(),
86+
})
87+
88+
client := &testHaystackClient{
89+
readResponse: response.ToGrid(),
5590
}
5691

57-
client := client.NewClient(
58-
os.Getenv("TEST_URL"),
59-
os.Getenv("TEST_USERNAME"),
60-
os.Getenv("TEST_PASSWORD"),
92+
actual := getResponse(
93+
client,
94+
&QueryModel{
95+
Type: "Read",
96+
Read: "ahu",
97+
},
98+
t,
99+
)
100+
101+
idVal := "@abcdefg-12345678 \"AHU-1\""
102+
disVal := "AHU-1"
103+
ahuVal := "M"
104+
expected := data.NewFrame("",
105+
data.NewField("id", nil, []*string{&idVal}).SetConfig(&data.FieldConfig{DisplayName: "id"}),
106+
data.NewField("dis", nil, []*string{&disVal}).SetConfig(&data.FieldConfig{DisplayName: "dis"}),
107+
data.NewField("ahu", nil, []*string{&ahuVal}).SetConfig(&data.FieldConfig{DisplayName: "ahu"}),
61108
)
109+
110+
if !cmp.Equal(actual, expected, data.FrameTestCompareOptions()...) {
111+
t.Error(cmp.Diff(actual, expected, data.FrameTestCompareOptions()...))
112+
}
113+
}
114+
115+
func getResponse(
116+
client HaystackClient,
117+
queryModel *QueryModel,
118+
t *testing.T,
119+
) *data.Frame {
62120
if client.Open() != nil {
63121
t.Fatal("Failed to open connection. Is a local Haxall server running?")
64122
}
@@ -79,9 +137,8 @@ func getResponse(queryModel *QueryModel, timeRange backend.TimeRange, t *testing
79137
&backend.QueryDataRequest{
80138
Queries: []backend.DataQuery{
81139
{
82-
RefID: refID,
83-
JSON: rawJson,
84-
TimeRange: timeRange,
140+
RefID: refID,
141+
JSON: rawJson,
85142
},
86143
},
87144
},
@@ -103,5 +160,42 @@ func getResponse(queryModel *QueryModel, timeRange backend.TimeRange, t *testing
103160
if len(queryResponse.Frames) != 1 {
104161
t.Fatal("Currently only support single-frame results")
105162
}
106-
return *queryResponse.Frames[0]
163+
return queryResponse.Frames[0]
164+
}
165+
166+
// TestHaystackClient is a mock of the HaystackClient interface
167+
type testHaystackClient struct {
168+
evalResponse haystack.Grid
169+
hisReadResponse haystack.Grid
170+
readResponse haystack.Grid
171+
}
172+
173+
// Open is a no-op
174+
func (c *testHaystackClient) Open() error {
175+
return nil
176+
}
177+
178+
// Close is a no-op
179+
func (c *testHaystackClient) Close() error {
180+
return nil
181+
}
182+
183+
// About returns an empty dict
184+
func (c *testHaystackClient) About() (haystack.Dict, error) {
185+
return haystack.Dict{}, nil
186+
}
187+
188+
// Eval returns the EvalResponse
189+
func (c *testHaystackClient) Eval(query string) (haystack.Grid, error) {
190+
return c.evalResponse, nil
191+
}
192+
193+
// HisRead returns the HisReadResponse
194+
func (c *testHaystackClient) HisReadAbsDateTime(ref haystack.Ref, start haystack.DateTime, end haystack.DateTime) (haystack.Grid, error) {
195+
return c.hisReadResponse, nil
196+
}
197+
198+
// Read returns the ReadResponse
199+
func (c *testHaystackClient) Read(query string) (haystack.Grid, error) {
200+
return c.readResponse, nil
107201
}

pkg/plugin/haystackClient.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package plugin
2+
3+
import (
4+
"github.com/NeedleInAJayStack/haystack"
5+
)
6+
7+
// HaystackClient is an interface used to enable mocking of the haystack client in tests
8+
type HaystackClient interface {
9+
Open() error
10+
Close() error
11+
About() (haystack.Dict, error)
12+
Eval(string) (haystack.Grid, error)
13+
HisReadAbsDateTime(haystack.Ref, haystack.DateTime, haystack.DateTime) (haystack.Grid, error)
14+
Read(string) (haystack.Grid, error)
15+
}

src/components/QueryEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { ChangeEvent, ReactNode } from 'react';
2-
import { Button, Field, Form, Icon, InlineField, Input, Select } from '@grafana/ui';
2+
import { Button, Field, Form, Icon, Input, Select } from '@grafana/ui';
33
import { QueryEditorProps, SelectableValue } from '@grafana/data';
44
import { DataSource } from '../datasource';
55
import { DEFAULT_QUERY, HaystackDataSourceOptions, HaystackQuery } from '../types';
@@ -28,7 +28,7 @@ export function QueryEditor({ query, onChange, onRunQuery }: Props) {
2828
];
2929
const queryTypeDefault = queryTypes[0];
3030
function queryTypeFromLabel(label: string) {
31-
return queryTypes.find(queryType => queryType.label == label) ?? queryTypeDefault
31+
return queryTypes.find(queryType => queryType.label === label) ?? queryTypeDefault
3232
}
3333

3434
const SelectComponent = () => {

0 commit comments

Comments
 (0)