mirror of
https://github.com/lobaro/restic-backup-docker.git
synced 2026-11-13 15:45:22 +00:00
fix: sabotage check treats failed scenario steps as INVALID
StepFailed is no AssertionError any more; sabotage.py counts a JUnit failure named StepFailed as error, so an expected-red test only counts when its own assertion fails. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9be33e3c26
commit
cb35fb296d
2 files changed
+24
-5
No files matched your search
+6
-2
@@ -41,8 +41,12 @@ def raise_interrupt(signum: int, _frame: object) -> None:
|
||||
raise KeyboardInterrupt(f"received {signal.Signals(signum).name}")
|
||||
|
||||
|
||||
class StepFailed(AssertionError):
|
||||
pass
|
||||
class StepFailed(Exception):
|
||||
"""A scenario step could not run; not an assertion about the behaviour under test.
|
||||
|
||||
Deliberately no AssertionError: sabotage.py tells it apart from a real assertion
|
||||
failure by the exception name in the JUnit report.
|
||||
"""
|
||||
|
||||
|
||||
class Scenario:
|
||||
|
||||
+18
-3
@@ -35,6 +35,9 @@ EXPECTED_RED = (
|
||||
)
|
||||
|
||||
|
||||
STEP_FAILED = "StepFailed" # name of helpers.StepFailed in the JUnit report
|
||||
|
||||
|
||||
class Invalid(Exception):
|
||||
"""The check itself is broken; the suite result means nothing."""
|
||||
|
||||
@@ -58,13 +61,25 @@ def sabotage_tree(tmp: Path) -> None:
|
||||
|
||||
|
||||
def red_tests(report: Path) -> tuple[set[str], set[str]]:
|
||||
"""Names of tests with an assertion failure, and of tests with an error (setup etc.)."""
|
||||
"""Names of tests with an assertion failure, and of tests with an error.
|
||||
|
||||
Errors are setup errors and failed scenario steps (StepFailed): neither is an assertion
|
||||
about the behaviour under test.
|
||||
"""
|
||||
try:
|
||||
cases = list(ET.parse(report).getroot().iter("testcase"))
|
||||
except ET.ParseError as exc:
|
||||
raise Invalid(f"pytest report is malformed or truncated: {exc}") from exc
|
||||
failures = {c.get("name", "") for c in cases if c.find("failure") is not None}
|
||||
errors = {c.get("name", "") for c in cases if c.find("error") is not None}
|
||||
failures = set()
|
||||
for case in cases:
|
||||
failure = case.find("failure")
|
||||
if failure is None:
|
||||
continue
|
||||
if STEP_FAILED in failure.get("message") or "":
|
||||
errors.add(case.get("name", ""))
|
||||
else:
|
||||
failures.add(case.get("name", ""))
|
||||
return failures, errors
|
||||
|
||||
|
||||
@@ -108,7 +123,7 @@ def main() -> int:
|
||||
failures, errors = red_tests(report)
|
||||
broken = [name for name in EXPECTED_RED if name in errors]
|
||||
if broken:
|
||||
raise Invalid(f"expected tests errored instead of failing an assertion: {broken}")
|
||||
raise Invalid(f"expected tests errored or hit a failed scenario step instead of failing an assertion: {broken}")
|
||||
missing = [name for name in EXPECTED_RED if name not in failures]
|
||||
for name in missing:
|
||||
print(
|
||||
|
||||
Reference in new issue
Block a user