diff --git a/requirements-strix-ci-hashes.txt b/requirements-strix-ci-hashes.txt index 9e705850b5..eb83beda17 100644 --- a/requirements-strix-ci-hashes.txt +++ b/requirements-strix-ci-hashes.txt @@ -140,9 +140,9 @@ annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 # via pydantic -anyio==4.14.0 \ - --hash=sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89 \ - --hash=sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9 +anyio==4.14.2 \ + --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 \ + --hash=sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f # via # google-genai # gql diff --git a/scripts/ci/actions_queue_health.py b/scripts/ci/actions_queue_health.py index bb73698551..e01145b8e0 100644 --- a/scripts/ci/actions_queue_health.py +++ b/scripts/ci/actions_queue_health.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """Queue-health CLI with stable identity and audit-provenance guarantees. -The shared collector implementation lives in ``actions_queue_health_core.py``. -This entrypoint owns the consistency boundary that binds active-run evidence to +Shared parsing and reporting primitives live in ``actions_queue_health_core.py``. +This entrypoint owns collection and the consistency boundary that binds active-run evidence to a stable pull-request view, carries stable workflow identity, and exports the exact timestamp used for queue-age calculations. """ @@ -163,16 +163,7 @@ def collect_snapshot( ), ) for workflow_run in workflow_runs: - workflow_run_id = workflow_run.get("id") - if ( - isinstance(workflow_run_id, bool) - or not isinstance(workflow_run_id, int) - or workflow_run_id <= 0 - ): - raise QueueHealthError( - "workflow run id must be a positive integer" - ) - active_snapshot[workflow_run_id] = workflow_run + active_snapshot[workflow_run["id"]] = workflow_run active_snapshots.append(active_snapshot) first_snapshot, second_snapshot = active_snapshots @@ -242,16 +233,7 @@ def collect_snapshot( TERMINAL_DIAGNOSTIC_STATUSES ): continue - workflow_run_id = workflow_run.get("id") - if ( - isinstance(workflow_run_id, bool) - or not isinstance(workflow_run_id, int) - or workflow_run_id <= 0 - ): - raise QueueHealthError( - "workflow run id must be a positive integer" - ) - terminal_diagnostic_snapshot[workflow_run_id] = workflow_run + terminal_diagnostic_snapshot[workflow_run["id"]] = workflow_run for terminal_status in TARGET_TERMINAL_DIAGNOSTIC_STATUSES: target_workflow_runs = _list_payload( diff --git a/scripts/ci/actions_queue_health_core.py b/scripts/ci/actions_queue_health_core.py index db3e5570ba..50cb3e0793 100644 --- a/scripts/ci/actions_queue_health_core.py +++ b/scripts/ci/actions_queue_health_core.py @@ -112,6 +112,13 @@ def _list_payload( declared_total_counts.append(payload["total_count"]) if not isinstance(values, list) or not all(isinstance(value, dict) for value in values): raise QueueHealthError(f"GitHub response field {key!r} must be an array of objects") + if key == "workflow_runs" and any( + isinstance(value.get("id"), bool) + or not isinstance(value.get("id"), int) + or value["id"] <= 0 + for value in values + ): + raise QueueHealthError("workflow run id must be a positive integer") if isinstance(payload, dict) and PAGINATED_PAGES_KEY in payload: record_identities: list[tuple[str, int]] = [] for value in values: @@ -360,133 +367,6 @@ def _normalise_run(repository: str, run: dict[str, Any], jobs: list[dict[str, An } -def collect_snapshot( - repositories: Sequence[str], - *, - runner: Runner = subprocess.run, - generated_at: str | None = None, -) -> dict[str, Any]: - """Collect bounded queued/in-progress run and job data using read-only API calls.""" - validated = sorted({_repository_name(repository) for repository in repositories}) - if len(validated) != len(repositories): - raise QueueHealthError("collection repository list contains duplicates") - collected_repositories: list[dict[str, Any]] = [] - collection_errors: list[dict[str, str]] = [] - for repository in validated: - try: - metadata = github_json(f"repos/{repository}", runner=runner) - if not isinstance(metadata, dict): - raise QueueHealthError(f"repository metadata for {repository} is not an object") - pulls_endpoint = f"repos/{repository}/pulls?state=open&per_page={MAX_API_PAGE_SIZE}" - pull_requests = _list_payload( - github_json(pulls_endpoint, paginate=True, runner=runner), - "pulls", - max_items=MAX_API_PAGE_SIZE * MAX_API_PAGES, - ) - normalized_pull_requests = sorted( - (_normalise_pull_request(item) for item in pull_requests), - key=lambda item: item["number"], - ) - except IncompletePullRequestIdentity: - time.sleep(PULL_REQUEST_RETRY_DELAY_SECONDS) - try: - retry_pull_requests = _list_payload( - github_json(pulls_endpoint, paginate=True, runner=runner), - "pulls", - max_items=MAX_API_PAGE_SIZE * MAX_API_PAGES, - ) - normalized_pull_requests = sorted( - (_normalise_pull_request(item) for item in retry_pull_requests), - key=lambda item: item["number"], - ) - except QueueHealthError as retry_exc: - collection_errors.append( - { - "repository": repository, - "error": f"pull-request identity validation failed: {retry_exc}", - } - ) - continue - except QueueHealthError as exc: - collection_errors.append({"repository": repository, "error": str(exc)}) - continue - pull_requests_by_number = {item["number"]: item for item in normalized_pull_requests} - runs_by_id: dict[int, dict[str, Any]] = {} - try: - active_statuses = ("in_progress", "pending", "queued", "requested", "waiting") - snapshots: list[dict[int, dict[str, Any]]] = [] - for status_order in (active_statuses, tuple(reversed(active_statuses))): - snapshot: dict[int, dict[str, Any]] = {} - for status in status_order: - runs = _list_payload( - github_json( - f"repos/{repository}/actions/runs?status={status}" - f"&per_page={WORKFLOW_RUN_PAGE_SIZE}", - paginate=True, - max_pages=ACTIVE_RUN_MAX_API_PAGES, - runner=runner, - ), - "workflow_runs", - max_items=WORKFLOW_RUN_PAGE_SIZE * ACTIVE_RUN_MAX_API_PAGES, - ) - for run in runs: - run_id = run.get("id") - if isinstance(run_id, bool) or not isinstance(run_id, int) or run_id <= 0: - raise QueueHealthError("workflow run id must be a positive integer") - snapshot[run_id] = run - snapshots.append(snapshot) - first_snapshot, second_snapshot = snapshots - first_states = { - run_id: str(run.get("status") or "").upper() - for run_id, run in first_snapshot.items() - } - second_states = { - run_id: str(run.get("status") or "").upper() - for run_id, run in second_snapshot.items() - } - if first_states != second_states: - raise QueueHealthError("active workflow run snapshot changed during collection") - for run_id, run in second_snapshot.items(): - run_id = run.get("id") - candidate = _normalise_run(repository, run, []) - identity, _ = _run_identity(candidate, pull_requests_by_number) - if identity != "current_head" or candidate["status"] not in { - "IN_PROGRESS", - "WAITING", - }: - runs_by_id[run_id] = candidate - continue - jobs_payload = github_json( - f"repos/{repository}/actions/runs/{run_id}/jobs?per_page={MAX_API_PAGE_SIZE}", - paginate=True, - runner=runner, - ) - jobs = _list_payload( - jobs_payload, - "jobs", - max_items=MAX_API_PAGE_SIZE * MAX_API_PAGES, - ) - runs_by_id[run_id] = _normalise_run(repository, run, jobs) - except QueueHealthError as exc: - collection_errors.append({"repository": repository, "error": str(exc)}) - continue - collected_repositories.append( - { - "full_name": repository, - "default_branch": str(metadata.get("default_branch") or ""), - "pull_requests": normalized_pull_requests, - "runs": sorted(runs_by_id.values(), key=lambda item: item["id"]), - } - ) - timestamp = generated_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") - parse_timestamp(timestamp) - return { - "generated_at": timestamp, - "repositories": collected_repositories, - "collection_errors": collection_errors, - } - - def load_snapshot(path: Path) -> dict[str, Any]: """Load a JSON snapshot for offline, deterministic report generation.""" try: @@ -822,34 +702,3 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: parser.add_argument("--queue-age-slo-seconds", type=int, default=DEFAULT_QUEUE_AGE_SLO_SECONDS) parser.add_argument("--now", help="Explicit timezone-aware evaluation time for deterministic reports") return parser.parse_args(argv) - - -def main(argv: Sequence[str] | None = None, *, stderr: TextIO = sys.stderr) -> int: - """Collect or load a snapshot, write reports, and return a stable CLI status.""" - args = parse_args(argv) - try: - snapshot = load_snapshot(args.snapshot) if args.snapshot else collect_snapshot(load_allowlist(args.allowlist)) - now = parse_timestamp(args.now) if args.now else datetime.now(timezone.utc) - report = build_report( - snapshot, - now=now, - queue_age_slo_seconds=args.queue_age_slo_seconds, - ) - write_reports(report, args.output_json, args.output_html) - except (OSError, QueueHealthError, ValueError) as exc: - print(f"ERROR: queue-health report failed: {exc}", file=stderr) - return 2 - breaches = report["summary"]["unassigned_slo_breached_count"] - if breaches: - print(f"::warning::Actions queue-health found {breaches} unassigned current-head SLO breach(es).") - print( - "QUEUE_HEALTH_RESULT=" - f"observed={report['summary']['observed_job_count']} " - f"pending={report['summary']['pending_job_count']} " - f"slo_breaches={breaches}" - ) - return 0 - - -if __name__ == "__main__": # pragma: no cover - exercised through the CLI tests. - raise SystemExit(main()) diff --git a/tests/test_actions_queue_health_cancelled_before_runner.py b/tests/test_actions_queue_health_cancelled_before_runner.py index 1827275f68..850f3bd866 100644 --- a/tests/test_actions_queue_health_cancelled_before_runner.py +++ b/tests/test_actions_queue_health_cancelled_before_runner.py @@ -17,7 +17,7 @@ SPEC.loader.exec_module(queue_health) -def test_collect_snapshot_classifies_cancelled_job_before_runner_assignment() -> None: +def test_collect_snapshot_classifies_cancelled_job_before_runner_assignment(monkeypatch) -> None: """A cancelled current-head job with no runner or steps stays explicit evidence.""" repository_name = "owner/repo" pull_request = { @@ -144,6 +144,9 @@ def runner(args: list[str], **_: object) -> CompletedProcess[str]: "cancelled_before_runner_assignment" ) assert report["summary"]["cancelled_before_runner_assignment_count"] == 1 + actions = list(report["summary"]["external_actions"]) + monkeypatch.setattr(queue_health, "_CORE_BUILD_REPORT", lambda *_args, **_kwargs: report) + assert queue_health.build_report(snapshot)["summary"]["external_actions"] == actions def test_collect_snapshot_retains_cancelled_pull_request_target_current_head() -> None: diff --git a/tests/test_actions_queue_health_post_evidence_retry.py b/tests/test_actions_queue_health_post_evidence_retry.py index bc266fa96f..ae1c619cd0 100644 --- a/tests/test_actions_queue_health_post_evidence_retry.py +++ b/tests/test_actions_queue_health_post_evidence_retry.py @@ -5,6 +5,8 @@ from pathlib import Path from subprocess import CompletedProcess +import pytest + ROOT = Path(__file__).resolve().parents[1] MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" @@ -82,3 +84,88 @@ def test_post_evidence_identity_read_fails_closed_after_retry_remains_incomplete assert len(snapshot["collection_errors"]) == 1 assert snapshot["collection_errors"][0]["repository"] == "owner/repo" assert "pull-request identity validation failed" in snapshot["collection_errors"][0]["error"] + +def _run(run_id: int, workflow_id: int) -> dict: + """Return one linked run for terminal-filter boundary tests.""" + return { + "id": run_id, + "workflow_id": workflow_id, + "name": "required-check", + "event": "pull_request", + "status": "queued", + "head_sha": "head", + "pull_requests": [{"number": 1, "head": {"sha": "head"}}], + } + + +@pytest.mark.parametrize( + ("active_runs", "completed_runs", "target_runs", "expected_error"), + [ + ([{"id": 0, "status": "queued"}], [], [], "workflow run id"), + ([], [{**_run(8, 501), "status": "completed", "conclusion": "failure", "id": 0}], [], "workflow run id"), + ([], [{**_run(8, 501), "status": "completed", "conclusion": "success"}], [], None), + ([], [], [{**_run(8, 501), "status": "completed", "conclusion": "cancelled", "pull_requests": []}], None), + ], +) +def test_collector_rejects_invalid_run_ids_and_ignores_unrelated_terminal_runs( + active_runs: list[dict], completed_runs: list[dict], target_runs: list[dict], expected_error: str | None, +) -> None: + """Bad identities fail closed; unrelated terminal runs do not become current-head evidence.""" + def runner(args: list[str], **_kwargs: object) -> CompletedProcess[str]: + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + payload = [_pull()] + elif "status=completed&head_sha=" in path: + payload = completed_runs + elif "status=cancelled&event=pull_request_target" in path: + payload = target_runs + elif "status=queued" in path: + payload = active_runs + elif "/actions/runs?status=" in path: + payload = [] + else: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + ["owner/repo"], runner=runner, generated_at="2026-09-02T00:00:00Z" + ) + if expected_error: + assert snapshot["repositories"] == [] + assert expected_error in snapshot["collection_errors"][0]["error"] + else: + assert snapshot["collection_errors"] == [] + assert snapshot["repositories"][0]["runs"] == [] + + +def test_final_pull_identity_retry_failure_is_bounded(monkeypatch: pytest.MonkeyPatch) -> None: + """A repeatedly incomplete final PR view cannot certify current-head evidence.""" + monkeypatch.setattr(queue_health.time, "sleep", lambda _seconds: None) + pull_reads = 0 + + def runner(args: list[str], **_kwargs: object) -> CompletedProcess[str]: + nonlocal pull_reads + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + pull_reads += 1 + payload = [_pull()] if pull_reads == 1 else [{**_pull(), "head": {"sha": ""}}] + elif "/actions/runs?status=" in path: + payload = [] + else: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot(["owner/repo"], runner=runner) + assert pull_reads == 3 + assert snapshot["repositories"] == [] + assert "pull-request identity validation failed" in snapshot["collection_errors"][0]["error"] + + +def test_core_run_normalization_rejects_non_object() -> None: + """A malformed workflow-run payload cannot be classified as a real run.""" + with pytest.raises(queue_health.QueueHealthError, match="workflow run entry must be an object"): + queue_health._CORE_NORMALISE_RUN("owner/repo", None, []) diff --git a/tests/test_actions_queue_health_snapshot_consistency.py b/tests/test_actions_queue_health_snapshot_consistency.py index b9711a09dd..8e939e2fc9 100644 --- a/tests/test_actions_queue_health_snapshot_consistency.py +++ b/tests/test_actions_queue_health_snapshot_consistency.py @@ -192,4 +192,5 @@ def test_invalid_present_workflow_id_fails_closed() -> None: def test_queue_health_workflow_does_not_grant_unused_pull_request_permission() -> None: """The scheduler token keeps only permissions used outside the cross-repository token.""" workflow = (ROOT / ".github/workflows/actions-queue-health.yml").read_text(encoding="utf-8") - assert "pull-requests: read" not in workflow + assert "\n pull-requests: read\n" not in workflow + assert "\n pull-requests: read\n" not in workflow diff --git a/tests/test_actions_queue_health_terminal_preexecution.py b/tests/test_actions_queue_health_terminal_preexecution.py index 05237ba340..c86c435e30 100644 --- a/tests/test_actions_queue_health_terminal_preexecution.py +++ b/tests/test_actions_queue_health_terminal_preexecution.py @@ -57,7 +57,7 @@ def _terminal_failure_job() -> dict: } -def test_terminal_preexecution_failure_survives_collection_and_is_not_product_failure() -> None: +def test_terminal_preexecution_failure_survives_collection_and_is_not_product_failure(monkeypatch) -> None: """Keep failed zero-step jobs as explicit non-passing admission evidence.""" failed_run = _terminal_failure_run() failed_job = _terminal_failure_job() @@ -106,3 +106,6 @@ def runner(args: list[str], **_: object) -> CompletedProcess[str]: assert row["recommended_action"] == "inspect_actions_control_plane_without_leaf_bypass" assert report["summary"]["terminal_pre_execution_failure_count"] == 1 assert report["summary"]["terminal_job_count"] == 1 + actions = list(report["summary"]["external_actions"]) + monkeypatch.setattr(queue_health, "_CORE_BUILD_REPORT", lambda *_args, **_kwargs: report) + assert queue_health.build_report(snapshot)["summary"]["external_actions"] == actions diff --git a/tests/test_noema_document_review_context.py b/tests/test_noema_document_review_context.py index e6ec2e6d70..ffed1c8961 100644 --- a/tests/test_noema_document_review_context.py +++ b/tests/test_noema_document_review_context.py @@ -176,6 +176,13 @@ def test_forbidden_docx_entities_are_explicitly_rejected(): document.extract_review_document("docs/entity.docx", _docx_entity_bytes()) +def test_invalid_github_base64_content_fails_closed(monkeypatch): + """Malformed GitHub file data must not reach the document reader.""" + monkeypatch.setattr(noema, "run", lambda _args, stdin=None: "not/base64!") + with pytest.raises(RuntimeError, match="malformed base64"): + noema.fetch_file_content_at_ref("owner/repo", "docs/review.docx", "head") + + def test_hwp_reader_contract_is_local_and_fail_closed(monkeypatch): """HWP/HWPX use the configured local adapter and reject failed readers.""" monkeypatch.setenv(document.HWP_READER_ENV, "/trusted/hwp-mcp-source") diff --git a/tests/test_noema_review_document_boundaries.py b/tests/test_noema_review_document_boundaries.py new file mode 100644 index 0000000000..5680252b3f --- /dev/null +++ b/tests/test_noema_review_document_boundaries.py @@ -0,0 +1,128 @@ +"""Exercise document-reader failure boundaries used by protected review.""" + +from __future__ import annotations + +import io +import runpy +import sys +import zipfile +from pathlib import Path +from subprocess import CompletedProcess + +import pytest + +from scripts.ci import noema_review_document as document + + +def _docx(xml: str | None, *, extra_entries: int = 0) -> bytes: + """Build a small DOCX archive with optional missing document XML.""" + output = io.BytesIO() + with zipfile.ZipFile(output, "w") as archive: + if xml is not None: + archive.writestr("word/document.xml", xml) + for index in range(extra_entries): + archive.writestr(f"extra-{index}", "x") + return output.getvalue() + + +def _body(content: str) -> str: + """Wrap Word body content in the namespace expected by the reader.""" + return ( + f'' + f"{content}" + ) + + +def test_document_input_limits_and_unsupported_formats(monkeypatch: pytest.MonkeyPatch) -> None: + """Reject oversized, unsupported, and unconfigured reader inputs.""" + monkeypatch.setattr(document, "MAX_DOCUMENT_BYTES", 2) + with pytest.raises(document.DocumentReadError, match="8 MiB"): + document.extract_review_document("a.docx", b"long") + with pytest.raises(document.DocumentReadError, match="unsupported"): + document.extract_review_document("a.pdf", b"ok") + monkeypatch.delenv(document.HWP_READER_ENV, raising=False) + with pytest.raises(document.DocumentReadError, match="not configured"): + document.extract_review_document("a.hwp", b"ok") + + +def test_docx_archive_and_xml_boundaries(monkeypatch: pytest.MonkeyPatch) -> None: + """Reject partial archives and XML without visible document content.""" + valid = _docx(_body("ok")) + monkeypatch.setattr(document, "MAX_DOCUMENT_ZIP_ENTRIES", 0) + with pytest.raises(document.DocumentReadError, match="too many entries"): + document.extract_review_document("a.docx", valid) + monkeypatch.setattr(document, "MAX_DOCUMENT_ZIP_ENTRIES", 2048) + monkeypatch.setattr(document, "MAX_DOCUMENT_ZIP_UNCOMPRESSED_BYTES", 1) + with pytest.raises(document.DocumentReadError, match="unpacked size"): + document.extract_review_document("a.docx", valid) + monkeypatch.setattr(document, "MAX_DOCUMENT_ZIP_UNCOMPRESSED_BYTES", 64 * 1024 * 1024) + cases = ( + (_docx(None, extra_entries=1), "no word/document.xml"), + (_docx("'), "no document body"), + (_docx(_body("")), "no readable text"), + ) + for payload, message in cases: + with pytest.raises(document.DocumentReadError, match=message): + document.extract_review_document("a.docx", payload) + + +def test_docx_visible_controls_and_uneven_table() -> None: + """Keep tabs, line breaks, and uneven table cells in reviewer text.""" + xml = _body( + "ABC" + "X|Y" + "Z" + "" + "" + ) + text = document.extract_review_document("a.docx", _docx(xml)) + assert "A\tB\nC" in text + assert "X\\|Y" in text + assert "| Z | |" in text + assert "### Table 1 (2 rows x 2 columns)" in text + assert "Table 2" not in text + + +def test_hwp_reader_rejects_process_and_output_failures(monkeypatch: pytest.MonkeyPatch) -> None: + """Keep parser failures and untrusted output out of the review prompt.""" + monkeypatch.setenv(document.HWP_READER_ENV, "/reviewed/source") + + def unavailable(*_args: object, **_kwargs: object) -> None: + raise OSError("private process detail") + + monkeypatch.setattr(document.subprocess, "run", unavailable) + with pytest.raises(document.DocumentReadError, match="could not start"): + document.extract_review_document("a.hwpx", b"data") + + for stdout, message in ((b"abcd", "bounded output"), (b"\xff", "non-UTF-8"), (b" ", "empty text")): + monkeypatch.setattr(document, "MAX_DOCUMENT_TEXT_BYTES", 3) + completed = CompletedProcess(["node"], 0, stdout, b"") + monkeypatch.setattr( + document.subprocess, + "run", + lambda *_args, **_kwargs: completed, + ) + with pytest.raises(document.DocumentReadError, match=message): + document.extract_review_document("a.hwpx", b"data") + + +def test_document_text_truncation_and_cli(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + """Bound UTF-8 output and preserve a useful local CLI failure exit.""" + monkeypatch.setattr(document, "MAX_DOCUMENT_TEXT_BYTES", 4) + assert document._bounded_text("ééé").startswith("éé\n[document text truncated;") + assert document._bounded_text("ok") == "ok" + + path = tmp_path / "review.docx" + path.write_bytes(_docx(_body("ok"))) + monkeypatch.setattr(sys, "argv", ["noema_review_document.py", str(path)]) + assert document._main() == 0 + assert "ok" in capsys.readouterr().out + monkeypatch.setattr(sys, "argv", ["noema_review_document.py", str(path.with_name("missing.docx"))]) + assert document._main() == 1 + assert capsys.readouterr().err + + monkeypatch.setattr(sys, "argv", ["noema_review_document.py", str(path)]) + with pytest.raises(SystemExit) as exit_status: + runpy.run_path(str(Path(document.__file__)), run_name="__main__") + assert exit_status.value.code == 0 diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index 4b8715d361..b8afdfcd92 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -2363,19 +2363,19 @@ def fake_active_workflow_runs(repo, statuses, *, event=None, created=None, head_ assert created == ">=2026-09-17T11:50:00Z" return [ { - "path": ".github/workflows/opencode-review-coalesce-tick.yml", + "path": ".github/workflows/other.yml", "conclusion": "success", - "updated_at": "2026-09-17T11:55:00Z", + "updated_at": "2026-09-17T11:59:00Z", }, { "path": ".github/workflows/opencode-review-coalesce-tick.yml", "conclusion": "success", - "updated_at": "2026-09-17T11:40:00Z", + "updated_at": "2026-09-17T11:55:00Z", }, { - "path": ".github/workflows/other.yml", + "path": ".github/workflows/opencode-review-coalesce-tick.yml", "conclusion": "success", - "updated_at": "2026-09-17T11:59:00Z", + "updated_at": "2026-09-17T11:40:00Z", }, ]