Skip to content

Commit 3641c97

Browse files
committed
tests(health api): extract specific assertions from list validator
1 parent 0f5362d commit 3641c97

File tree

9 files changed

+42
-30
lines changed

9 files changed

+42
-30
lines changed

.buildkite/scripts/health-report-tests/bootstrap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self) -> None:
2424
logstash_branch = os.environ.get("LS_BRANCH")
2525
if logstash_branch is None:
2626
# version is not specified, use the main branch, no need to git checkout
27-
print(f"LS_BRANCH is not specified, using main branch.")
27+
print(f"LS_BRANCH is not specified, using HEAD.")
2828
else:
2929
# LS_BRANCH accepts major latest as a major.x or specific branch as X.Y
3030
if logstash_branch.find(".x") == -1:

.buildkite/scripts/health-report-tests/scenario_executor.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
A class to execute the given scenario for Logstash Health Report integration test
33
"""
44
import time
5+
import re
56
from logstash_health_report import LogstashHealthReport
67

78

@@ -11,39 +12,38 @@ class ScenarioExecutor:
1112
def __init__(self):
1213
pass
1314

14-
def __has_intersection(self, expects, results):
15-
# TODO: this logic is aligned on current Health API response
16-
# there is no guarantee that method correctly runs if provided multi expects and results
17-
# we expect expects to be existing in results
18-
for expect in expects:
19-
for result in results:
20-
if result.get('help_url') and "health-report-pipeline-" not in result.get('help_url'):
21-
return False
22-
if not all(key in result and result[key] == value for key, value in expect.items()):
23-
return False
24-
return True
15+
def __get_difference(self, expect: any(), actual: any(), path: str | None = None) -> list:
2516

26-
def __get_difference(self, differences: list, expectations: dict, reports: dict) -> dict:
27-
for key in expectations.keys():
17+
path = path or ""
18+
differences = []
2819

29-
if type(expectations.get(key)) != type(reports.get(key)):
30-
differences.append(f"Scenario expectation and Health API report structure differs for {key}.")
31-
return differences
20+
match expect:
21+
case {"$include": inclusion} if isinstance(expect, dict) and len(expect) == 1 and isinstance(actual, str):
22+
if inclusion not in actual:
23+
differences.append(f"Value at path `{path}` does not include:`{inclusion}`; got:`{actual}`")
24+
case dict():
25+
if not isinstance(actual, dict):
26+
differences.append(f"Structure differs at `{path}`, expected:`{expect}` got:`{actual}`")
27+
else:
28+
for key in expect.keys():
29+
differences.extend(self.__get_difference(expect.get(key), actual.get(key), f"{path}.{key}"))
30+
case list():
31+
if not isinstance(actual, list):
32+
differences.append(f"Structure differs at `{path}`, expected:`{expect}` got:`{actual}`")
33+
else:
34+
for index, (expectEntry, actualEntry) in enumerate(zip(expect, actual)):
35+
differences.extend(self.__get_difference(expectEntry, actualEntry, f"{path}[{index}]"))
36+
if len(actual) < len(expect):
37+
differences.append(f"Missing entries at path `{path}`, expected:`{len(expect)}`, got:`{len(actual)}`")
38+
case _:
39+
if expect != actual:
40+
differences.append(f"Value not match at path `{path}`; expected:`{expect}`, got:`{actual}`")
3241

33-
if isinstance(expectations.get(key), str):
34-
if expectations.get(key) != reports.get(key):
35-
differences.append({key: {"expected": expectations.get(key), "got": reports.get(key)}})
36-
continue
37-
elif isinstance(expectations.get(key), dict):
38-
self.__get_difference(differences, expectations.get(key), reports.get(key))
39-
elif isinstance(expectations.get(key), list):
40-
if not self.__has_intersection(expectations.get(key), reports.get(key)):
41-
differences.append({key: {"expected": expectations.get(key), "got": reports.get(key)}})
4242
return differences
4343

4444
def __is_expected(self, expectations: dict) -> None:
4545
reports = self.logstash_health_report_api.get()
46-
differences = self.__get_difference([], expectations, reports)
46+
differences = self.__get_difference(expect=expectations, actual=reports)
4747
if differences:
4848
print("Differences found in 'expectation' section between YAML content and stats:")
4949
for diff in differences:

.buildkite/scripts/health-report-tests/tests/abnormal-termination.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ expectation:
2424
diagnosis:
2525
- cause: "pipeline is not running, likely because it has encountered an error"
2626
action: "view logs to determine the cause of abnormal pipeline shutdown"
27+
help_url: { $include: "health-report-pipeline-" }
2728
impacts:
2829
- description: "the pipeline is not currently processing"
2930
impact_areas: ["pipeline_execution"]

.buildkite/scripts/health-report-tests/tests/backpressure-1m.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ expectation:
2525
- id: "logstash:health:pipeline:flow:worker_utilization:diagnosis:1m-blocked"
2626
cause: "pipeline workers have been completely blocked for at least one minute"
2727
action: "address bottleneck or add resources"
28+
help_url: { $include: "health-report-pipeline-" }
2829
impacts:
2930
- id: "logstash:health:pipeline:flow:impact:blocked_processing"
3031
severity: 2

.buildkite/scripts/health-report-tests/tests/backpressure-5m.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ config:
99
pipeline.batch.size: 1
1010
conditions:
1111
full_start_required: true
12-
wait_seconds: 310 # give more seconds to make sure time is over the threshold, 1m in this case
12+
wait_seconds: 315 # give more seconds to make sure time is over the threshold, 5m in this case
1313
expectation:
1414
status: "red"
1515
symptom: "1 indicator is unhealthy (`pipelines`)"
@@ -25,6 +25,7 @@ expectation:
2525
- id: "logstash:health:pipeline:flow:worker_utilization:diagnosis:5m-blocked"
2626
cause: "pipeline workers have been completely blocked for at least five minutes"
2727
action: "address bottleneck or add resources"
28+
help_url: { $include: "health-report-pipeline-" }
2829
impacts:
2930
- id: "logstash:health:pipeline:flow:impact:blocked_processing"
3031
severity: 1

.buildkite/scripts/health-report-tests/tests/multipipeline.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ expectation:
3737
diagnosis:
3838
- cause: "pipeline is loading"
3939
action: "if pipeline does not come up quickly, you may need to check the logs to see if it is stalled"
40+
help_url: { $include: "health-report-pipeline-" }
4041
impacts:
4142
- impact_areas: ["pipeline_execution"]
4243
details:
@@ -48,6 +49,7 @@ expectation:
4849
diagnosis:
4950
- cause: "pipeline has finished running because its inputs have been closed and events have been processed"
5051
action: "if you expect this pipeline to run indefinitely, you will need to configure its inputs to continue receiving or fetching events"
52+
help_url: { $include: "health-report-pipeline-" }
5153
impacts:
5254
- impact_areas: [ "pipeline_execution" ]
5355
details:
@@ -59,6 +61,7 @@ expectation:
5961
diagnosis:
6062
- cause: "pipeline is not running, likely because it has encountered an error"
6163
action: "view logs to determine the cause of abnormal pipeline shutdown"
64+
help_url: { $include: "health-report-pipeline-" }
6265
impacts:
6366
- description: "the pipeline is not currently processing"
6467
impact_areas: [ "pipeline_execution" ]

.buildkite/scripts/health-report-tests/tests/normal-termination.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ expectation:
2323
diagnosis:
2424
- cause: "pipeline has finished running because its inputs have been closed and events have been processed"
2525
action: "if you expect this pipeline to run indefinitely, you will need to configure its inputs to continue receiving or fetching events"
26+
help_url: { $include: "health-report-pipeline-" }
2627
impacts:
2728
- impact_areas: ["pipeline_execution"]
2829
details:

.buildkite/scripts/health-report-tests/tests/slow-start.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ expectation:
2424
diagnosis:
2525
- cause: "pipeline is loading"
2626
action: "if pipeline does not come up quickly, you may need to check the logs to see if it is stalled"
27+
help_url: { $include: "health-report-pipeline-" }
2728
impacts:
2829
- impact_areas: ["pipeline_execution"]
2930
details:

.buildkite/scripts/health-report-tests/util.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ def git_check_out_branch(branch_name: str) -> None:
2323
run_or_raise_error(["git", "checkout", branch_name],
2424
"Error occurred while checking out the " + branch_name + " branch")
2525

26-
2726
def run_or_raise_error(commands: list, error_message):
2827
f"""
2928
Executes the {list} commands and raises an {Exception} if operation fails.
3029
"""
31-
result = subprocess.run(commands, env=os.environ.copy(), universal_newlines=True, stdout=subprocess.PIPE)
30+
myEnv = os.environ.copy()
31+
result = subprocess.run(commands, env=myEnv, universal_newlines=True, stdout=subprocess.PIPE)
3232
if result.returncode != 0:
33+
print("ENV")
34+
for k, v in myEnv.items():
35+
print(f" {k}={v}")
36+
print("---")
3337
full_error_message = (error_message + ", output: " + result.stdout) \
3438
if result.stdout else error_message
3539
raise Exception(f"{full_error_message}")

0 commit comments

Comments
 (0)