monitoring(testing): use lane timelines for test health
This commit is contained in:
parent
792ac2b946
commit
a2d5c9c83e
@ -1169,7 +1169,7 @@ def testing_case_variable():
|
|||||||
"name": "test",
|
"name": "test",
|
||||||
"label": "Test Case",
|
"label": "Test Case",
|
||||||
"type": "query",
|
"type": "query",
|
||||||
"query": f'label_values(platform_quality_gate_test_case_result{{suite=~"${{suite:regex}}",branch=~"${{branch:regex}}",test!="__no_test_cases__",{PLATFORM_TEST_EXPORT_FILTER}}}, test)',
|
"query": f'label_values(platform_quality_gate_test_case_result{{suite=~"${{suite:regex}}",branch!="",branch=~"${{branch:regex}}",test!="",test!="__no_test_cases__",{PLATFORM_TEST_EXPORT_FILTER}}}, test)',
|
||||||
"current": {"text": "All", "value": "$__all", "selected": True},
|
"current": {"text": "All", "value": "$__all", "selected": True},
|
||||||
"options": [],
|
"options": [],
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
@ -1187,7 +1187,7 @@ def testing_branch_variable():
|
|||||||
"name": "branch",
|
"name": "branch",
|
||||||
"label": "Branch",
|
"label": "Branch",
|
||||||
"type": "query",
|
"type": "query",
|
||||||
"query": f'label_values(platform_quality_gate_build_info{{suite=~"${{suite:regex}}",{PLATFORM_TEST_EXPORT_FILTER}}}, branch)',
|
"query": f'label_values(platform_quality_gate_build_info{{suite=~"${{suite:regex}}",branch!="",{PLATFORM_TEST_EXPORT_FILTER}}}, branch)',
|
||||||
"current": {"text": "All", "value": "$__all", "selected": True},
|
"current": {"text": "All", "value": "$__all", "selected": True},
|
||||||
"options": [],
|
"options": [],
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
@ -3385,9 +3385,7 @@ def build_jobs_dashboard():
|
|||||||
coverage_metric_selector = f'__name__=~".*_quality_gate_coverage_percent",suite=~"{suite_var}",{exported}'
|
coverage_metric_selector = f'__name__=~".*_quality_gate_coverage_percent",suite=~"{suite_var}",{exported}'
|
||||||
workspace_coverage_selector = f'suite=~"{suite_var}",{exported}'
|
workspace_coverage_selector = f'suite=~"{suite_var}",{exported}'
|
||||||
smell_selector = f'suite=~"{suite_var}",{exported}'
|
smell_selector = f'suite=~"{suite_var}",{exported}'
|
||||||
test_case_selector = f'suite=~"{suite_var}",branch=~"{branch_var}",test=~"{test_var}",test!="__no_test_cases__",{exported}'
|
build_info_selector = f'suite=~"{suite_var}",branch!="",branch=~"{branch_var}",{exported}'
|
||||||
all_test_case_selector = f'suite=~"{suite_var}",branch=~"{branch_var}",test!="__no_test_cases__",{exported}'
|
|
||||||
build_info_selector = f'suite=~"{suite_var}",branch=~"{branch_var}",{exported}'
|
|
||||||
selected_suite_universe = (
|
selected_suite_universe = (
|
||||||
f'(sum by (suite) (increase(platform_quality_gate_runs_total{{{runs_selector}}}[30d])) >= bool 0)'
|
f'(sum by (suite) (increase(platform_quality_gate_runs_total{{{runs_selector}}}[30d])) >= bool 0)'
|
||||||
)
|
)
|
||||||
@ -3488,59 +3486,51 @@ def build_jobs_dashboard():
|
|||||||
return f"(100 * ({state_checks}) / clamp_min(({total_checks}), 1)) and on(suite) (({total_checks}) > 0)"
|
return f"(100 * ({state_checks}) / clamp_min(({total_checks}), 1)) and on(suite) (({total_checks}) > 0)"
|
||||||
|
|
||||||
rollup_failed_tests = (
|
rollup_failed_tests = (
|
||||||
f'sum by (suite, test) (platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch=~"{branch_var}",test!="__no_test_cases__",status="failed"}})'
|
f'sum by (suite, test) (platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch!="",branch=~"{branch_var}",test!="",test!="__no_test_cases__",status="failed"}})'
|
||||||
)
|
)
|
||||||
raw_failed_tests = (
|
raw_failed_tests = (
|
||||||
f'sum by (suite, test) (max_over_time(platform_quality_gate_test_case_result{{{all_test_case_selector},status="failed"}}[$__interval]))'
|
f'sum by (suite, test) (max_over_time(platform_quality_gate_test_case_result{{suite=~"{suite_var}",branch!="",branch=~"{branch_var}",test!="",test!="__no_test_cases__",{exported},status="failed"}}[$__interval]))'
|
||||||
)
|
)
|
||||||
problematic_tests_history_core = f"topk(12, (({rollup_failed_tests}) or on(suite, test) ({raw_failed_tests})))"
|
problematic_tests_history_core = f"topk(12, (({rollup_failed_tests}) or on(suite, test) ({raw_failed_tests})))"
|
||||||
problematic_tests_history = f"({problematic_tests_history_core}) or on() vector(0)"
|
problematic_tests_history = problematic_tests_history_core
|
||||||
rollup_failed_tests_30d = (
|
rollup_failed_tests_30d = (
|
||||||
f'sum by (suite, test) (sum_over_time(platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch=~"{branch_var}",test!="__no_test_cases__",status="failed"}}[30d:1h]))'
|
f'sum by (suite, test) (sum_over_time(platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch!="",branch=~"{branch_var}",test!="",test!="__no_test_cases__",status="failed"}}[30d:1h]))'
|
||||||
)
|
)
|
||||||
raw_failed_tests_30d = (
|
raw_failed_tests_30d = (
|
||||||
f'sum by (suite, test) (increase(platform_quality_gate_test_case_result{{{all_test_case_selector},status="failed"}}[30d]))'
|
f'sum by (suite, test) (increase(platform_quality_gate_test_case_result{{suite=~"{suite_var}",branch!="",branch=~"{branch_var}",test!="",test!="__no_test_cases__",{exported},status="failed"}}[30d]))'
|
||||||
)
|
)
|
||||||
worst_test_per_suite_core = (
|
worst_test_per_suite_core = (
|
||||||
f"topk by (suite) (1, (({rollup_failed_tests_30d}) or on(suite, test) ({raw_failed_tests_30d})))"
|
f"topk by (suite) (1, (({rollup_failed_tests_30d}) or on(suite, test) ({raw_failed_tests_30d})))"
|
||||||
)
|
)
|
||||||
worst_test_per_suite = f"({worst_test_per_suite_core}) or on() vector(0)"
|
worst_test_per_suite = worst_test_per_suite_core
|
||||||
|
|
||||||
def _selected_status_history(status: str) -> str:
|
def _selected_status_volume(status: str) -> str:
|
||||||
rollup = (
|
return (
|
||||||
f'sum by (suite) (platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch=~"{branch_var}",test=~"{test_var}",test!="__no_test_cases__",status="{status}"}})'
|
f'(sum(platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch!="",'
|
||||||
|
f'branch=~"{branch_var}",test!="",test=~"{test_var}",test!="__no_test_cases__",'
|
||||||
|
f'status="{status}"}}) or on() vector(0))'
|
||||||
)
|
)
|
||||||
raw = (
|
|
||||||
f'sum by (suite) (max_over_time(platform_quality_gate_test_case_result{{{test_case_selector},status="{status}"}}[$__interval]))'
|
|
||||||
)
|
|
||||||
return f"(({rollup}) or on(suite) ({raw}) or on(suite) ({selected_suite_zero}))"
|
|
||||||
|
|
||||||
selected_passed_history = _selected_status_history("passed")
|
|
||||||
selected_failed_history = _selected_status_history("failed")
|
|
||||||
selected_skipped_history = _selected_status_history("skipped")
|
|
||||||
selected_total_history = (
|
|
||||||
f'(sum by (suite) (platform_quality:test_case_status:count_1h{{suite=~"{suite_var}",branch=~"{branch_var}",test=~"{test_var}",test!="__no_test_cases__",status=~"passed|failed|error|skipped"}}) '
|
|
||||||
f'or on(suite) sum by (suite) (max_over_time(platform_quality_gate_test_case_result{{{test_case_selector},status=~"passed|failed|error|skipped"}}[$__interval])))'
|
|
||||||
)
|
|
||||||
selected_test_pass_fail = [
|
selected_test_pass_fail = [
|
||||||
{
|
{
|
||||||
"refId": "A",
|
"refId": "A",
|
||||||
"expr": selected_passed_history,
|
"expr": _selected_status_volume("passed"),
|
||||||
"legendFormat": "{{suite}} passed",
|
"legendFormat": "Passed",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"refId": "B",
|
"refId": "B",
|
||||||
"expr": selected_failed_history,
|
"expr": _selected_status_volume("failed"),
|
||||||
"legendFormat": "{{suite}} failed",
|
"legendFormat": "Failed",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"refId": "C",
|
"refId": "C",
|
||||||
"expr": selected_skipped_history,
|
"expr": _selected_status_volume("skipped"),
|
||||||
"legendFormat": "{{suite}} skipped",
|
"legendFormat": "Skipped",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
selected_test_pass_rate = (
|
selected_test_pass_rate = (
|
||||||
f"((100 * ({selected_passed_history}) / clamp_min(({selected_total_history}), 1)) or on(suite) ({selected_suite_zero}))"
|
f'avg by (suite) (platform_quality:test_case_pass_rate:percent_1h{{suite=~"{suite_var}",'
|
||||||
|
f'branch!="",branch=~"{branch_var}",test!="",test=~"{test_var}",test!="__no_test_cases__"}})'
|
||||||
)
|
)
|
||||||
recent_branch_evidence = (
|
recent_branch_evidence = (
|
||||||
f'sort_desc(count by (suite, branch) (max_over_time(platform_quality_gate_build_info{{{build_info_selector}}}[30d])))'
|
f'sort_desc(count by (suite, branch) (max_over_time(platform_quality_gate_build_info{{{build_info_selector}}}[30d])))'
|
||||||
@ -3628,7 +3618,25 @@ def build_jobs_dashboard():
|
|||||||
*,
|
*,
|
||||||
description: str,
|
description: str,
|
||||||
thresholds: dict,
|
thresholds: dict,
|
||||||
|
unit: str = "percent",
|
||||||
|
min_value: int | float | None = 0,
|
||||||
|
max_value: int | float | None = 100,
|
||||||
|
legend: str = "{{suite}}",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
defaults = {
|
||||||
|
"color": {"mode": "thresholds"},
|
||||||
|
"unit": unit,
|
||||||
|
"thresholds": thresholds,
|
||||||
|
"custom": {
|
||||||
|
"fillOpacity": 70,
|
||||||
|
"lineWidth": 0,
|
||||||
|
"spanNulls": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if min_value is not None:
|
||||||
|
defaults["min"] = min_value
|
||||||
|
if max_value is not None:
|
||||||
|
defaults["max"] = max_value
|
||||||
panel = {
|
panel = {
|
||||||
"id": panel_id,
|
"id": panel_id,
|
||||||
"type": "state-timeline",
|
"type": "state-timeline",
|
||||||
@ -3636,20 +3644,9 @@ def build_jobs_dashboard():
|
|||||||
"description": description,
|
"description": description,
|
||||||
"datasource": PROM_DS,
|
"datasource": PROM_DS,
|
||||||
"gridPos": grid,
|
"gridPos": grid,
|
||||||
"targets": [{"expr": expr, "refId": "A", "legendFormat": "{{suite}}"}],
|
"targets": [{"expr": expr, "refId": "A", "legendFormat": legend}],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": defaults,
|
||||||
"color": {"mode": "thresholds"},
|
|
||||||
"unit": "percent",
|
|
||||||
"min": 0,
|
|
||||||
"max": 100,
|
|
||||||
"thresholds": thresholds,
|
|
||||||
"custom": {
|
|
||||||
"fillOpacity": 70,
|
|
||||||
"lineWidth": 0,
|
|
||||||
"spanNulls": True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"overrides": [],
|
"overrides": [],
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@ -3860,63 +3857,47 @@ def build_jobs_dashboard():
|
|||||||
"Higher means more of the selected suites/checks are healthy right now; gaps mean there was no check evidence."
|
"Higher means more of the selected suites/checks are healthy right now; gaps mean there was no check evidence."
|
||||||
)
|
)
|
||||||
for index, (label, regex) in enumerate(check_dimensions[:4]):
|
for index, (label, regex) in enumerate(check_dimensions[:4]):
|
||||||
panel = timeseries_panel(
|
panel = _state_timeline_panel(
|
||||||
start_id + index,
|
start_id + index,
|
||||||
f"{label} {title_prefix}",
|
f"{label} {title_prefix}",
|
||||||
_check_state_percent_series(regex, failed),
|
_check_state_percent_series(regex, failed),
|
||||||
{"h": 7, "w": 6, "x": index * 6, "y": y},
|
{"h": 7, "w": 6, "x": index * 6, "y": y},
|
||||||
unit="percent",
|
thresholds=trend_thresholds,
|
||||||
legend="{{suite}}",
|
description=trend_description,
|
||||||
legend_display="list",
|
|
||||||
legend_placement="bottom",
|
|
||||||
legend_calcs=[],
|
|
||||||
)
|
)
|
||||||
panel["description"] = trend_description
|
|
||||||
panel["fieldConfig"]["defaults"]["thresholds"] = trend_thresholds
|
|
||||||
panel["fieldConfig"]["defaults"]["min"] = 0
|
|
||||||
panel["fieldConfig"]["defaults"]["max"] = 100
|
|
||||||
panel["fieldConfig"]["defaults"].setdefault("custom", {})["spanNulls"] = True
|
|
||||||
panel["fieldConfig"]["defaults"]["custom"]["showPoints"] = "never"
|
|
||||||
panel["fieldConfig"]["defaults"]["custom"]["lineWidth"] = 2
|
|
||||||
panels.append(panel)
|
panels.append(panel)
|
||||||
for index, (label, regex) in enumerate(check_dimensions[4:]):
|
for index, (label, regex) in enumerate(check_dimensions[4:]):
|
||||||
panel = timeseries_panel(
|
panel = _state_timeline_panel(
|
||||||
start_id + 4 + index,
|
start_id + 4 + index,
|
||||||
f"{label} {title_prefix}",
|
f"{label} {title_prefix}",
|
||||||
_check_state_percent_series(regex, failed),
|
_check_state_percent_series(regex, failed),
|
||||||
{"h": 7, "w": 8, "x": index * 8, "y": y + 7},
|
{"h": 7, "w": 8, "x": index * 8, "y": y + 7},
|
||||||
unit="percent",
|
thresholds=trend_thresholds,
|
||||||
legend="{{suite}}",
|
description=trend_description,
|
||||||
legend_display="list",
|
|
||||||
legend_placement="bottom",
|
|
||||||
legend_calcs=[],
|
|
||||||
)
|
)
|
||||||
panel["description"] = trend_description
|
|
||||||
panel["fieldConfig"]["defaults"]["thresholds"] = trend_thresholds
|
|
||||||
panel["fieldConfig"]["defaults"]["min"] = 0
|
|
||||||
panel["fieldConfig"]["defaults"]["max"] = 100
|
|
||||||
panel["fieldConfig"]["defaults"].setdefault("custom", {})["spanNulls"] = True
|
|
||||||
panel["fieldConfig"]["defaults"]["custom"]["showPoints"] = "never"
|
|
||||||
panel["fieldConfig"]["defaults"]["custom"]["lineWidth"] = 2
|
|
||||||
panels.append(panel)
|
panels.append(panel)
|
||||||
|
|
||||||
_append_check_trends(130, "Failure Rate", True, 29)
|
_append_check_trends(130, "Failure Rate", True, 29)
|
||||||
_append_check_trends(138, "Healthy Rate", False, 43)
|
_append_check_trends(138, "Healthy Rate", False, 43)
|
||||||
panels.append(
|
panels.append(
|
||||||
timeseries_panel(
|
_state_timeline_panel(
|
||||||
145,
|
145,
|
||||||
"Problematic Tests Over Time (Top failures)",
|
"Problematic Tests Over Time (Top failures)",
|
||||||
problematic_tests_history,
|
problematic_tests_history,
|
||||||
{"h": 8, "w": 12, "x": 0, "y": 57},
|
{"h": 8, "w": 12, "x": 0, "y": 57},
|
||||||
|
thresholds=failures_thresholds,
|
||||||
unit="none",
|
unit="none",
|
||||||
|
min_value=0,
|
||||||
|
max_value=None,
|
||||||
legend="{{suite}} - {{test}}",
|
legend="{{suite}} - {{test}}",
|
||||||
legend_display="list",
|
description=(
|
||||||
legend_placement="right",
|
"Top failing test cases over time, using memoized hourly rollups. "
|
||||||
legend_calcs=[],
|
"Blank branch/test labels and placeholder no-test-case rows are excluded."
|
||||||
links=jenkins_suite_links(),
|
),
|
||||||
data_links=jenkins_latest_artifact_data_links(),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
panels[-1]["links"] = jenkins_suite_links()
|
||||||
|
panels[-1]["fieldConfig"]["defaults"]["links"] = jenkins_latest_artifact_data_links()
|
||||||
panels.append(
|
panels.append(
|
||||||
bargauge_panel(
|
bargauge_panel(
|
||||||
147,
|
147,
|
||||||
@ -3948,22 +3929,32 @@ def build_jobs_dashboard():
|
|||||||
data_links=jenkins_artifact_data_links(),
|
data_links=jenkins_artifact_data_links(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected_pass_rate_panel = timeseries_panel(
|
panels[-1]["description"] = (
|
||||||
|
"Stacked hourly outcome volume for the selected suite/branch/test scope. "
|
||||||
|
"This uses vmalert rollups only, avoiding expensive raw 30-day per-test scans."
|
||||||
|
)
|
||||||
|
panels[-1]["fieldConfig"]["defaults"]["min"] = 0
|
||||||
|
panels[-1]["fieldConfig"]["defaults"]["custom"] = {
|
||||||
|
"drawStyle": "bars",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"lineWidth": 0,
|
||||||
|
"fillOpacity": 70,
|
||||||
|
"stacking": {"mode": "normal", "group": "A"},
|
||||||
|
}
|
||||||
|
selected_pass_rate_panel = _state_timeline_panel(
|
||||||
152,
|
152,
|
||||||
"Selected Test Pass Rate History",
|
"Selected Test Pass Rate History",
|
||||||
selected_test_pass_rate,
|
selected_test_pass_rate,
|
||||||
{"h": 8, "w": 12, "x": 12, "y": 65},
|
{"h": 8, "w": 12, "x": 12, "y": 65},
|
||||||
unit="percent",
|
thresholds=success_thresholds,
|
||||||
legend="{{suite}}",
|
legend="{{suite}}",
|
||||||
legend_display="list",
|
description=(
|
||||||
legend_placement="bottom",
|
"Average pass rate per suite for the selected test filter, using memoized hourly "
|
||||||
legend_calcs=[],
|
"test-case pass-rate rollups instead of raw historical scans."
|
||||||
links=jenkins_suite_links(),
|
),
|
||||||
data_links=jenkins_artifact_data_links(),
|
|
||||||
)
|
)
|
||||||
selected_pass_rate_panel["fieldConfig"]["defaults"]["min"] = 0
|
selected_pass_rate_panel["links"] = jenkins_suite_links()
|
||||||
selected_pass_rate_panel["fieldConfig"]["defaults"]["max"] = 100
|
selected_pass_rate_panel["fieldConfig"]["defaults"]["links"] = jenkins_artifact_data_links()
|
||||||
selected_pass_rate_panel["fieldConfig"]["defaults"]["thresholds"] = success_thresholds
|
|
||||||
panels.append(selected_pass_rate_panel)
|
panels.append(selected_pass_rate_panel)
|
||||||
|
|
||||||
coverage_panel = bargauge_panel(
|
coverage_panel = bargauge_panel(
|
||||||
@ -4078,7 +4069,7 @@ def build_jobs_dashboard():
|
|||||||
stat_panel(
|
stat_panel(
|
||||||
32,
|
32,
|
||||||
"Sonar Projects (Selected)",
|
"Sonar Projects (Selected)",
|
||||||
f'(count(sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}"}}) or on() vector(0))',
|
f'(count(max by (project_key) (sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}"}})) or on() vector(0))',
|
||||||
{"h": 6, "w": 4, "x": 4, "y": 88},
|
{"h": 6, "w": 4, "x": 4, "y": 88},
|
||||||
unit="none",
|
unit="none",
|
||||||
instant=True,
|
instant=True,
|
||||||
@ -4099,23 +4090,32 @@ def build_jobs_dashboard():
|
|||||||
sonar_status_mix_panel = pie_panel(
|
sonar_status_mix_panel = pie_panel(
|
||||||
34,
|
34,
|
||||||
"Sonar Gate Status Mix (Selected)",
|
"Sonar Gate Status Mix (Selected)",
|
||||||
f'count by (status) (sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}"}})',
|
f'count by (status) (max by (project_key, status) (sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}"}}))',
|
||||||
{"h": 6, "w": 6, "x": 12, "y": 88},
|
{"h": 6, "w": 4, "x": 12, "y": 88},
|
||||||
)
|
)
|
||||||
sonar_status_mix_panel["targets"][0]["legendFormat"] = "{{status}}"
|
sonar_status_mix_panel["targets"][0]["legendFormat"] = "{{status}}"
|
||||||
panels.append(sonar_status_mix_panel)
|
panels.append(sonar_status_mix_panel)
|
||||||
panels.append(
|
panels.append(
|
||||||
bargauge_panel(
|
_state_timeline_panel(
|
||||||
35,
|
35,
|
||||||
"Projects Failing Sonar Gate",
|
"Projects Failing Sonar Gate",
|
||||||
f'(sort_desc(count by (project_key) (sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}",status!~"OK|ok"}})) '
|
f'max by (project_key) ((max by (project_key, status) (sonarqube_project_quality_gate_pass{{project_key=~"{suite_var}",status!~"OK|ok"}})) * 0 + 1)',
|
||||||
f'or on() label_replace(vector(0), "project_key", "none", "__name__", ".*"))',
|
{"h": 6, "w": 8, "x": 16, "y": 88},
|
||||||
{"h": 6, "w": 6, "x": 18, "y": 88},
|
thresholds={
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{"color": dark_green, "value": None},
|
||||||
|
{"color": dark_red, "value": 1},
|
||||||
|
],
|
||||||
|
},
|
||||||
unit="none",
|
unit="none",
|
||||||
instant=True,
|
min_value=0,
|
||||||
|
max_value=1,
|
||||||
legend="{{project_key}}",
|
legend="{{project_key}}",
|
||||||
sort_order="desc",
|
description=(
|
||||||
thresholds=failures_thresholds,
|
"Projects observed with a non-OK SonarQube gate status over time. "
|
||||||
|
"The query deduplicates pod/service endpoint scrapes before rendering."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
panels.append(
|
panels.append(
|
||||||
@ -4222,8 +4222,8 @@ def build_jobs_dashboard():
|
|||||||
31: {"h": 6, "w": 4, "x": 0, "y": 111},
|
31: {"h": 6, "w": 4, "x": 0, "y": 111},
|
||||||
32: {"h": 6, "w": 4, "x": 4, "y": 111},
|
32: {"h": 6, "w": 4, "x": 4, "y": 111},
|
||||||
33: {"h": 6, "w": 4, "x": 8, "y": 111},
|
33: {"h": 6, "w": 4, "x": 8, "y": 111},
|
||||||
34: {"h": 6, "w": 6, "x": 12, "y": 111},
|
34: {"h": 6, "w": 4, "x": 12, "y": 111},
|
||||||
35: {"h": 6, "w": 6, "x": 18, "y": 111},
|
35: {"h": 6, "w": 8, "x": 16, "y": 111},
|
||||||
}
|
}
|
||||||
for panel_id, grid in row_layout.items():
|
for panel_id, grid in row_layout.items():
|
||||||
panel_by_id[panel_id]["gridPos"] = grid
|
panel_by_id[panel_id]["gridPos"] = grid
|
||||||
|
|||||||
@ -193,10 +193,30 @@ def test_jobs_dashboard_collapses_heavy_drilldowns_for_light_first_paint():
|
|||||||
assert "SonarQube API Up" in nested_panels_by_title
|
assert "SonarQube API Up" in nested_panels_by_title
|
||||||
|
|
||||||
failure_rate_panel = nested_panels_by_title["Coverage Failure Rate"]
|
failure_rate_panel = nested_panels_by_title["Coverage Failure Rate"]
|
||||||
|
assert failure_rate_panel["type"] == "state-timeline"
|
||||||
assert failure_rate_panel["fieldConfig"]["defaults"]["unit"] == "percent"
|
assert failure_rate_panel["fieldConfig"]["defaults"]["unit"] == "percent"
|
||||||
assert failure_rate_panel["fieldConfig"]["defaults"]["max"] == 100
|
assert failure_rate_panel["fieldConfig"]["defaults"]["max"] == 100
|
||||||
assert "increase(" not in failure_rate_panel["targets"][0]["expr"]
|
assert "increase(" not in failure_rate_panel["targets"][0]["expr"]
|
||||||
|
|
||||||
pass_rate_panel = nested_panels_by_title["Selected Test Pass Rate History"]
|
pass_rate_panel = nested_panels_by_title["Selected Test Pass Rate History"]
|
||||||
assert "platform_quality_gate_test_case_result" in pass_rate_panel["targets"][0]["expr"]
|
assert pass_rate_panel["type"] == "state-timeline"
|
||||||
assert "platform_quality:test_case_pass_rate:percent_1h" not in pass_rate_panel["targets"][0]["expr"]
|
assert "platform_quality:test_case_pass_rate:percent_1h" in pass_rate_panel["targets"][0]["expr"]
|
||||||
|
assert "platform_quality_gate_test_case_result" not in pass_rate_panel["targets"][0]["expr"]
|
||||||
|
|
||||||
|
pass_fail_panel = nested_panels_by_title["Selected Test Pass/Fail History"]
|
||||||
|
assert pass_fail_panel["fieldConfig"]["defaults"]["custom"]["drawStyle"] == "bars"
|
||||||
|
assert all(
|
||||||
|
"platform_quality:test_case_status:count_1h" in target["expr"]
|
||||||
|
for target in pass_fail_panel["targets"]
|
||||||
|
)
|
||||||
|
|
||||||
|
problematic_panel = nested_panels_by_title["Problematic Tests Over Time (Top failures)"]
|
||||||
|
assert problematic_panel["type"] == "state-timeline"
|
||||||
|
assert 'test!=""' in problematic_panel["targets"][0]["expr"]
|
||||||
|
assert "vector(0)" not in problematic_panel["targets"][0]["expr"]
|
||||||
|
|
||||||
|
sonar_mix_panel = nested_panels_by_title["Sonar Gate Status Mix (Selected)"]
|
||||||
|
sonar_failing_panel = nested_panels_by_title["Projects Failing Sonar Gate"]
|
||||||
|
assert sonar_mix_panel["gridPos"]["w"] == 4
|
||||||
|
assert sonar_failing_panel["gridPos"]["w"] == 8
|
||||||
|
assert sonar_failing_panel["type"] == "state-timeline"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -147,7 +147,7 @@ data:
|
|||||||
- record: platform_quality:test_case_status:count_1h
|
- record: platform_quality:test_case_status:count_1h
|
||||||
expr: |
|
expr: |
|
||||||
sum by (suite, branch, test, status) (
|
sum by (suite, branch, test, status) (
|
||||||
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",test!="__no_test_cases__"}[1h])
|
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",branch!="",test!="",test!="__no_test_cases__"}[1h])
|
||||||
)
|
)
|
||||||
labels:
|
labels:
|
||||||
rollup: hourly
|
rollup: hourly
|
||||||
@ -155,13 +155,13 @@ data:
|
|||||||
expr: |
|
expr: |
|
||||||
100 * (
|
100 * (
|
||||||
sum by (suite, branch, test) (
|
sum by (suite, branch, test) (
|
||||||
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",test!="__no_test_cases__",status="passed"}[1h])
|
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",branch!="",test!="",test!="__no_test_cases__",status="passed"}[1h])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
/
|
/
|
||||||
clamp_min(
|
clamp_min(
|
||||||
sum by (suite, branch, test) (
|
sum by (suite, branch, test) (
|
||||||
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",test!="__no_test_cases__",status=~"passed|failed|error|skipped"}[1h])
|
max_over_time(platform_quality_gate_test_case_result{exported_job="platform-quality-ci",branch!="",test!="",test!="__no_test_cases__",status=~"passed|failed|error|skipped"}[1h])
|
||||||
),
|
),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
@ -196,7 +196,7 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: vmalert-atlas-availability
|
app: vmalert-atlas-availability
|
||||||
annotations:
|
annotations:
|
||||||
bstein.dev/rules-revision: "2026-05-15-platform-quality-rollups-v2"
|
bstein.dev/rules-revision: "2026-05-15-platform-quality-rollups-v3"
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: vmalert-atlas-availability
|
serviceAccountName: vmalert-atlas-availability
|
||||||
affinity:
|
affinity:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user