Skip to content

Commit fe736e5

Browse files
committed
Tests: Improve test quality, specifically for explore dashboards
Grafana 6 vs. Grafana >= 7 have a different behaviour here because Grafana 6 did not know about data source UIDs yet.
1 parent 29d9a48 commit fe736e5

File tree

7 files changed

+245
-66
lines changed

7 files changed

+245
-66
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ in progress
1313
- Tests: Run Grafana on non-standard port 33333
1414
- Tests: Add flag ``CLEANUP_RESOURCES`` to determine whether to clean up
1515
all resources provisioned to Grafana.
16+
- Tests: Improve test quality, specifically for ``explore dashboards`` on
17+
Grafana 6 vs. Grafana >= 7
1618

1719

1820
2022-02-03 0.13.1

doc/backlog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ grafana-wtf backlog
77
Prio 1
88
******
99
- [o] With Grafana >8.3, resolve datasource name and add to ``{'type': 'influxdb', 'uid': 'PDF2762CDFF14A314'}``
10+
- [o] Add "folder name/uid" to "explore dashboards" response.
11+
- [o] Check if "datasources" is always present in responses to "explore dashboards".
1012

1113

1214
*********
@@ -27,6 +29,7 @@ Prio 1.5
2729
- [o] Show dependencies
2830
- [o] Optionally apply "replace" to data sources also
2931
- [o] Add software tests for authenticated access to Grafana (--grafana-token)
32+
- [o] Add output format RSS
3033

3134

3235
******

grafana_wtf/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def run():
148148
setup_logging(log_level)
149149

150150
# Debugging
151-
log.debug("Options: {}".format(json.dumps(options, indent=4)))
151+
# log.debug("Options: {}".format(json.dumps(options, indent=4)))
152152

153153
configure_http_logging(options)
154154

grafana_wtf/core.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,17 @@ def info(self):
295295

296296
return response
297297

298+
def version(self):
299+
300+
try:
301+
health = self.grafana.client.GET("/health")
302+
except Exception as ex:
303+
log.error(f"Request to /health endpoint failed: {ex}")
304+
health = {}
305+
306+
version = health.get("version")
307+
return version
308+
298309
def dashboard_details(self):
299310
for dashboard in self.data.dashboards:
300311
yield DashboardDetails(dashboard=dashboard)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
extras = {
3030
"test": [
31-
"pytest>=5,<7",
31+
"pytest>=5,<8",
3232
"lovely-pytest-docker>=0.2.1,<3",
3333
"grafanalib>=0.6,<0.7",
3434
]

tests/conftest.py

Lines changed: 141 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import re
44
from io import StringIO
55
from pathlib import Path
6+
from typing import List, Union
67

78
import grafanalib.core
89
import pytest
@@ -12,6 +13,9 @@
1213
from grafana_wtf.core import GrafanaWtf
1314

1415
# Whether to clean up all resources provisioned to Grafana.
16+
# Note that the test suite will not complete successfully when toggling this
17+
# setting. It can be used when running individual test cases in order to
18+
# investigate the resources provisioned to Grafana.
1519
CLEANUP_RESOURCES = True
1620

1721

@@ -56,6 +60,38 @@ def create_datasource(docker_grafana):
5660
5761
- https://grafana.com/docs/grafana/latest/http_api/data_source/
5862
- https://docs.pytest.org/en/4.6.x/fixture.html#factories-as-fixtures
63+
64+
The JSON response to `create_datasource` looks like this::
65+
66+
{
67+
"datasource": {
68+
"id": 3,
69+
"uid": "PDF2762CDFF14A314",
70+
"orgId": 1,
71+
"name": "ldi_v2",
72+
"type": "influxdb",
73+
"typeLogoUrl": "",
74+
"access": "proxy",
75+
"url": "http://localhost:8086/",
76+
"password": "root",
77+
"user": "root",
78+
"database": "ldi_v2",
79+
"basicAuth": false,
80+
"basicAuthUser": "",
81+
"basicAuthPassword": "",
82+
"withCredentials": false,
83+
"isDefault": false,
84+
"jsonData": {},
85+
"secureJsonFields": {
86+
"password": true
87+
},
88+
"version": 1,
89+
"readOnly": false
90+
},
91+
"id": 3,
92+
"message": "Datasource added",
93+
"name": "ldi_v2"
94+
}
5995
"""
6096

6197
# Reference to `grafana-client`.
@@ -64,7 +100,19 @@ def create_datasource(docker_grafana):
64100
# Keep track of the datasource ids in order to delete them afterwards.
65101
datasource_ids = []
66102

67-
def _create_datasource(name: str, type: str, access: str, **kwargs):
103+
def _create_datasource(name: str, type: str = "testdata", access: str = "proxy", **kwargs):
104+
105+
# Reuse existing datasource.
106+
try:
107+
response = grafana.datasource.get_datasource_by_name(name)
108+
datasource_id = response["id"]
109+
datasource_ids.append(datasource_id)
110+
return
111+
except GrafanaClientError as ex:
112+
if ex.status_code != 404:
113+
raise
114+
115+
# Create new datasource.
68116
datasource = dict(name=name, type=type, access=access)
69117
datasource.update(kwargs)
70118
try:
@@ -91,60 +139,74 @@ def _create_datasource(name: str, type: str, access: str, **kwargs):
91139
def create_folder(docker_grafana):
92140
"""
93141
Create a Grafana folder from a test case.
94-
After the test case finished, it will remove the dashboard again.
142+
After the test case finished, it will remove the folder again.
95143
96144
- https://grafana.com/docs/grafana/latest/http_api/folder/
97145
- https://docs.pytest.org/en/4.6.x/fixture.html#factories-as-fixtures
146+
147+
The JSON response to `create_folder` looks like this::
148+
149+
{
150+
"id": 44,
151+
"uid": "iga1UrEnz",
152+
"title": "Testdrive",
153+
"url": "/dashboards/f/iga1UrEnz/testdrive",
154+
"hasAcl": false,
155+
"canSave": true,
156+
"canEdit": true,
157+
"canAdmin": true,
158+
"createdBy": "admin",
159+
"created": "2022-03-22T23:44:38Z",
160+
"updatedBy": "admin",
161+
"updated": "2022-03-22T23:44:38Z",
162+
"version": 1
163+
}
98164
"""
99165

100166
# Reference to `grafana-client`.
101167
grafana = GrafanaWtf.grafana_client_factory(docker_grafana)
102168

103-
# Keep track of the dashboard uids in order to delete them afterwards.
169+
# Keep track of the folder uids in order to delete them afterwards.
104170
folder_uids = []
105171

106-
# https://docs.pytest.org/en/4.6.x/fixture.html#factories-as-fixtures
107172
def _create_folder(title: str, uid: str = None):
108173

109-
# Create dashboard in Grafana.
174+
# Reuse folder when it already exists.
110175
try:
111-
response = grafana.folder.create_folder(title=title, uid=uid)
176+
response = grafana.folder.get_folder(uid=uid)
112177
folder_id = response["id"]
113178
folder_uid = response["uid"]
179+
folder_uids.append(folder_uid)
180+
return folder_id
181+
except GrafanaClientError as ex:
182+
if ex.status_code != 404:
183+
raise
114184

115-
# Response is like:
116-
"""
117-
{
118-
"id": 44,
119-
"uid": "iga1UrEnz",
120-
"title": "Testdrive",
121-
"url": "/dashboards/f/iga1UrEnz/testdrive",
122-
"hasAcl": false,
123-
"canSave": true,
124-
"canEdit": true,
125-
"canAdmin": true,
126-
"createdBy": "admin",
127-
"created": "2022-03-22T23:44:38Z",
128-
"updatedBy": "admin",
129-
"updated": "2022-03-22T23:44:38Z",
130-
"version": 1
131-
}
132-
"""
133-
185+
# Create folder.
186+
try:
187+
response = grafana.folder.create_folder(title=title, uid=uid)
188+
folder_id = response["id"]
189+
folder_uid = response["uid"]
134190
folder_uids.append(folder_uid)
135191
return folder_id
136192
except GrafanaClientError as ex:
137193
# TODO: Mimic the original response in order to make the removal work.
138-
if not re.match(
194+
error_exists = re.match(
139195
"Client Error 409: a folder or dashboard in the general folder with the same name already exists",
140196
str(ex),
141197
re.IGNORECASE,
142-
):
198+
)
199+
error_modified = re.match(
200+
"Client Error 412: The folder has been changed by someone else",
201+
str(ex),
202+
re.IGNORECASE,
203+
)
204+
if not (error_exists or error_modified):
143205
raise
144206

145207
yield _create_folder
146208

147-
# Delete dashboard again.
209+
# Delete folder again.
148210
if CLEANUP_RESOURCES:
149211
if folder_uids:
150212
for folder_uid in folder_uids:
@@ -159,6 +221,17 @@ def create_dashboard(docker_grafana):
159221
160222
- https://grafana.com/docs/grafana/latest/http_api/dashboard/
161223
- https://docs.pytest.org/en/4.6.x/fixture.html#factories-as-fixtures
224+
225+
The JSON response to `update_dashboard` looks like this::
226+
227+
{
228+
"id": 2,
229+
"slug": "luftdaten-info-generic-trend-v33",
230+
"status": "success",
231+
"uid": "jpVsQxRja",
232+
"url": "/d/jpVsQxRja/luftdaten-info-generic-trend-v33",
233+
"version": 1
234+
}
162235
"""
163236

164237
# Reference to `grafana-client`.
@@ -177,10 +250,6 @@ def _create_dashboard(dashboard: dict = None, folder_id: str = None, folder_uid:
177250
if folder_uid:
178251
payload["folderUid"] = folder_uid
179252
response = grafana.dashboard.update_dashboard(dashboard=payload)
180-
181-
# Response is like:
182-
# {'id': 3, 'slug': 'foo', 'status': 'success', 'uid': 'iO0xgE2nk', 'url': '/d/iO0xgE2nk/foo', 'version': 1}
183-
184253
dashboard_uid = response["uid"]
185254
dashboard_uids.append(dashboard_uid)
186255

@@ -202,26 +271,46 @@ def ldi_resources(create_datasource, create_folder, create_dashboard):
202271
https://docs.pytest.org/en/4.6.x/fixture.html#factories-as-fixtures
203272
"""
204273

205-
# Create LDI datasource.
206-
create_datasource(
207-
name="ldi_v2",
208-
type="influxdb",
209-
access="proxy",
210-
url="http://localhost:8086/",
211-
user="root",
212-
password="root",
213-
database="ldi_v2",
214-
secureJsonData={"password": "root"},
215-
)
216-
217-
# Create folder.
218-
folder_id = create_folder(title="Testdrive", uid="testdrive")
219-
220-
# Create LDI dashboards.
221-
for file in Path("tests/grafana/dashboards").glob("*.json"):
222-
with open(file, "r") as f:
223-
dashboard = json.load(f)
224-
create_dashboard(dashboard=dashboard, folder_id=folder_id)
274+
def _ldi_resources(dashboards: List[Union[Path, str]] = None):
275+
# Create LDI datasource.
276+
create_datasource(
277+
name="ldi_v2",
278+
type="influxdb",
279+
access="proxy",
280+
uid="PDF2762CDFF14A314",
281+
url="http://localhost:8086/",
282+
user="root",
283+
password="root",
284+
database="ldi_v2",
285+
secureJsonData={"password": "root"},
286+
)
287+
288+
# Create folder.
289+
folder_id = create_folder(title="Testdrive", uid="testdrive")
290+
291+
# Create LDI dashboards.
292+
if dashboards:
293+
dashboard_files = dashboards
294+
else:
295+
dashboard_files = Path("tests/grafana/dashboards").glob("*.json")
296+
297+
for file in dashboard_files:
298+
with open(file, "r") as f:
299+
dashboard = json.load(f)
300+
create_dashboard(dashboard=dashboard, folder_id=folder_id)
301+
302+
return _ldi_resources
303+
304+
305+
@pytest.fixture
306+
def grafana_version(docker_grafana):
307+
"""
308+
Return Grafana version number.
309+
"""
310+
engine = GrafanaWtf(grafana_url=docker_grafana, grafana_token=None)
311+
engine.setup()
312+
grafana_version = engine.version()
313+
return grafana_version
225314

226315

227316
def mkdashboard(title: str, datasource: str):

0 commit comments

Comments
 (0)