fix(ci): scope OpenAPI evidence to required tests
CI / Rust Checks (push) Successful in 12m40s
CI / UI Checks (push) Successful in 16m21s
CI / Community Image Smoke (push) Successful in 1m15s
CI / Frontend E2E (push) Successful in 5m8s
CI / Deploy (push) Failing after 46s

This commit is contained in:
2026-09-01 12:59:04 +03:00
parent 6c5d0c62d0
commit 66b9bfd875
2 changed files with 107 additions and 11 deletions
+37 -11
View File
@@ -90,26 +90,52 @@ def playwright_verdict(report: dict[str, Any], required_titles: list[str]) -> tu
return "fail", counts return "fail", counts
tests = list(iter_tests(report)) tests = list(iter_tests(report))
if not tests: if not tests:
if required_titles:
counts["failed"] = len(set(required_titles))
return "fail", counts
counts["not_run"] = 1 counts["not_run"] = 1
return "not_run", counts return "not_run", counts
required = {title: False for title in required_titles} required = {title: False for title in required_titles}
for test, title in tests: for test, title in tests:
if required and title not in required:
continue
status = test.get("status") status = test.get("status")
results = test.get("results") if isinstance(test.get("results"), list) else [] results = test.get("results")
result_statuses = [result.get("status") for result in results if isinstance(result, dict)] if not isinstance(results, list):
retries = [result.get("retry", 0) for result in results if isinstance(result, dict)]
if len(result_statuses) != len(results):
counts["not_run"] += 1 counts["not_run"] += 1
continue continue
if status == "flaky" or any(isinstance(retry, int) and retry > 0 for retry in retries): result_statuses: list[Any] = []
counts["flaky"] += 1 retries: list[int] = []
malformed = False
for result in results:
if not isinstance(result, dict):
malformed = True
break
result_status = result.get("status")
retry = result.get("retry", 0)
if result_status not in ("passed", "failed", "timedOut", "skipped", "interrupted") \
or type(retry) is not int or retry < 0:
malformed = True
break
result_statuses.append(result_status)
retries.append(retry)
if malformed:
counts["not_run"] += 1
continue
final_status = result_statuses[-1] if result_statuses else None
if status in ("unexpected", "failed", "timedOut", "interrupted") \
or final_status in ("failed", "timedOut", "interrupted"):
counts["failed"] += 1
elif status == "flaky":
if final_status == "passed" and any(retry > 0 for retry in retries):
counts["flaky"] += 1
else:
counts["not_run"] += 1
elif status == "skipped" or (not results and status in ("skipped", "expected")): elif status == "skipped" or (not results and status in ("skipped", "expected")):
counts["skipped"] += 1 counts["skipped"] += 1
elif status in ("unexpected", "failed", "timedOut", "interrupted") or any( elif any(retry > 0 for retry in retries):
result_status in ("failed", "timedOut", "interrupted") for result_status in result_statuses counts["flaky"] += 1
): elif status == "expected" and results and all(result_status == "passed" for result_status in result_statuses):
counts["failed"] += 1
elif results and all(result_status == "passed" for result_status in result_statuses):
counts["passed"] += 1 counts["passed"] += 1
if title in required: if title in required:
required[title] = True required[title] = True
@@ -57,6 +57,7 @@ class CapabilityBaselineCollectorTests(unittest.TestCase):
def test_required_openapi_tests_fail_closed_when_missing_skipped_or_flaky(self) -> None: def test_required_openapi_tests_fail_closed_when_missing_skipped_or_flaky(self) -> None:
required = ["OpenAPI required scenario"] required = ["OpenAPI required scenario"]
reports = [ reports = [
{"suites": []},
{"suites": [{"specs": [{"title": "another scenario", "tests": [{"status": "expected", "results": [{"status": "passed", "retry": 0}]}]}]}]}, {"suites": [{"specs": [{"title": "another scenario", "tests": [{"status": "expected", "results": [{"status": "passed", "retry": 0}]}]}]}]},
{"suites": [{"specs": [{"title": required[0], "tests": [{"status": "skipped", "results": []}]}]}]}, {"suites": [{"specs": [{"title": required[0], "tests": [{"status": "skipped", "results": []}]}]}]},
{"suites": [{"specs": [{"title": required[0], "tests": [{"status": "flaky", "results": [{"status": "failed", "retry": 0}, {"status": "passed", "retry": 1}]}]}]}]}, {"suites": [{"specs": [{"title": required[0], "tests": [{"status": "flaky", "results": [{"status": "failed", "retry": 0}, {"status": "passed", "retry": 1}]}]}]}]},
@@ -70,6 +71,73 @@ class CapabilityBaselineCollectorTests(unittest.TestCase):
self.assertEqual(candidate["execution_verdict"], "fail") self.assertEqual(candidate["execution_verdict"], "fail")
self.assertFalse(candidate["accepted"]) self.assertFalse(candidate["accepted"])
def test_required_scope_ignores_unrelated_flaky_tests(self) -> None:
required = "OpenAPI required scenario"
report = {
"suites": [
{
"specs": [
{
"title": required,
"tests": [{"status": "expected", "results": [{"status": "passed", "retry": 0}]}],
},
{
"title": "Unrelated wizard scenario",
"tests": [
{
"status": "flaky",
"results": [
{"status": "failed", "retry": 0},
{"status": "passed", "retry": 1},
],
}
],
},
{"title": "Unrelated failure", "tests": [{"status": "unexpected", "results": [{"status": "failed", "retry": 0}]}]},
{"title": "Unrelated skip", "tests": [{"status": "skipped", "results": []}]},
{"title": "Unrelated malformed", "tests": [{"status": "expected", "results": None}]},
]
}
]
}
result, output, temporary = self.run_playwright(report, [required])
self.addCleanup(temporary.cleanup)
self.assertEqual(result.returncode, 0, result.stderr)
candidate = json.loads(output.read_text(encoding="utf-8"))
self.assertEqual(candidate["execution_verdict"], "pass")
self.assertTrue(candidate["accepted"])
self.assertEqual(candidate["summary"], {"passed": 1, "failed": 0, "flaky": 0, "skipped": 0, "not_run": 0})
def test_final_failure_after_retry_is_failed_not_flaky(self) -> None:
for status in ("unexpected", "flaky"):
with self.subTest(status=status):
report = {
"suites": [
{
"specs": [
{
"tests": [
{
"status": status,
"results": [
{"status": "failed", "retry": 0},
{"status": "failed", "retry": 1},
],
}
]
}
]
}
]
}
result, output, temporary = self.run_playwright(report)
self.addCleanup(temporary.cleanup)
self.assertEqual(result.returncode, 0, result.stderr)
candidate = json.loads(output.read_text(encoding="utf-8"))
self.assertEqual(candidate["execution_verdict"], "fail")
self.assertEqual(candidate["summary"]["failed"], 1)
self.assertEqual(candidate["summary"]["flaky"], 0)
def test_raw_report_content_never_reaches_candidate_or_error(self) -> None: def test_raw_report_content_never_reaches_candidate_or_error(self) -> None:
canary = "Bearer secret-canary /home/private/workspace https://private.invalid?q=secret" canary = "Bearer secret-canary /home/private/workspace https://private.invalid?q=secret"
report = {"suites": [], "errors": [{"message": canary}], "stdout": [canary]} report = {"suites": [], "errors": [{"message": canary}], "stdout": [canary]}
@@ -84,6 +152,8 @@ class CapabilityBaselineCollectorTests(unittest.TestCase):
reports = [ reports = [
{"errors": [{"message": "fatal"}], "suites": [{"specs": [{"tests": [{"status": "expected", "results": [{"status": "passed"}]}]}]}]}, {"errors": [{"message": "fatal"}], "suites": [{"specs": [{"tests": [{"status": "expected", "results": [{"status": "passed"}]}]}]}]},
{"suites": [{"specs": [{"tests": [{"status": "expected", "results": ["not-an-object"]}]}]}]}, {"suites": [{"specs": [{"tests": [{"status": "expected", "results": ["not-an-object"]}]}]}]},
{"suites": [{"specs": [{"tests": [{"status": "expected", "results": None}]}]}]},
{"suites": [{"specs": [{"tests": [{"status": "expected", "results": [{"status": "passed", "retry": True}]}]}]}]},
] ]
for report in reports: for report in reports:
with self.subTest(report=report): with self.subTest(report=report):