self.promql_expr_test = None
self.data = get_dashboards_data()
self.query_map = self.data['queries']
+ self.dashboard = None
def reset_promql_test(self):
self.promql_expr_test = PromqlTest()
def before_scenario(context, scenario):
global_context.reset_promql_test()
+ global_context.dashboard = None
def after_scenario(context, scenario):
global_context.print_query_stats()
+@given('the dashboard is `{dashboard}`')
+def step_impl(context, dashboard):
+ if dashboard not in global_context.query_map:
+ raise KeyError(f'Unknown dashboard "{dashboard}". Known: '
+ f'{sorted(global_context.query_map)}')
+ global_context.dashboard = dashboard
+
+
@given("the following series")
def step_impl(context):
for row in context.table:
"""
if legend == "EMPTY":
legend = ''
+ if global_context.dashboard is None:
+ raise KeyError('No dashboard selected; add "Given the dashboard is '
+ '`<name>`" to the feature Background')
query_id = panel_name + '-' + legend
- if query_id not in global_context.query_map:
- print(f"QueryMap: {global_context.query_map}")
- raise KeyError((f'Query with legend {legend} in panel "{panel_name}"'
- 'couldn\'t be found'))
+ dashboard_queries = global_context.query_map[global_context.dashboard]
+ if query_id not in dashboard_queries:
+ raise KeyError(f'Query with legend {legend} in panel "{panel_name}" '
+ f'couldn\'t be found in dashboard '
+ f'"{global_context.dashboard}"')
- expr = global_context.query_map[query_id]['query']
+ expr = dashboard_queries[query_id]['query']
global_context.promql_expr_test.set_expression(expr)
for row in context.table:
metric = row['metrics']
value = row['values']
global_context.promql_expr_test.add_exp_samples(metric, float(value))
- path = global_context.query_map[query_id]['path']
+ path = dashboard_queries[query_id]['path']
global_context.data['stats'][path]['tested'] += 1
def get_dashboards_data() -> Dict[str, Any]:
data: Dict[str, Any] = {'queries': {}, 'variables': {}, 'stats': {}}
- for file in Path(__file__).parent.parent \
- .joinpath('dashboards_out').glob('*.json'):
+ for file in sorted(Path(__file__).parent.parent
+ .joinpath('dashboards_out').glob('*.json')):
with open(file, 'r') as f:
dashboard_data = json.load(f)
data['stats'][str(file)] = {'total': 0, 'tested': 0}
Grafana panels can have more than one target/query, in order to identify each
query in the panel we append the "legendFormat" of the target to the panel name.
format: panel_name-legendFormat
+
+ Queries are grouped per dashboard (data['queries'][<dashboard>]) so that
+ different dashboards reusing a panel title/legend, e.g. "IOPS-Read", stay in
+ separate namespaces. Each .feature picks its dashboard so the lookup is
+ unambiguous.
"""
if 'panels' not in dashboard_data:
return
+ dashboard = Path(path).stem
+ queries = data['queries'].setdefault(dashboard, {})
error = 0
panel_ids_in_file = set()
title = panel['title']
legend_format = target['legendFormat'] if 'legendFormat' in target else ""
query_id = f'{title}-{legend_format}'
- # Skip duplicates within collapsed rows
- # but error on duplicates at top-level or across different contexts
+ # a repeated id within one dashboard is expected for a collapsed
+ # row; at top level it means a missing legendFormat
if query_id in panel_ids_in_file:
if legend_format != '__auto' and not in_row:
cprint((f'ERROR: Query in panel "{title}" with legend "{legend_format}"'
f' already exists in the same file: "{path}"'), 'red')
error = 1
- # Skip adding duplicate
- continue
- # Also check for duplicates across different files
- # NOTE: This silently skips duplicate panel+legend combinations across dashboards,
- # which means some queries never get tested. A better approach would be to include
- # dashboard name in query_id (e.g., "dashboard:panel-legend"), but that requires
- # updating all test .feature files to specify which dashboard they're testing.
- if query_id in data['queries']:
- # Skip silently for duplicates across files (first one wins)
continue
- data['queries'][query_id] = {'query': target['expr'], 'path': path}
+ queries[query_id] = {'query': target['expr'], 'path': path}
data['stats'][path]['total'] += 1
panel_ids_in_file.add(query_id)
# Recurse into row panels