|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +import json |
5 | 6 | from pathlib import Path |
6 | 7 | from types import SimpleNamespace |
7 | 8 |
|
@@ -579,3 +580,114 @@ def test_version_above_minimum_returns_ok(self, monkeypatch: pytest.MonkeyPatch) |
579 | 580 | result = _check_codex_version(backend=CodexBackend()) |
580 | 581 | assert result.severity == Severity.OK |
581 | 582 | assert "0.131.0" in result.message |
| 583 | + |
| 584 | + |
| 585 | +class TestCheckCodexGraduation: |
| 586 | + """Tests for _check_codex_graduation doctor check (Check 33).""" |
| 587 | + |
| 588 | + def _codex_result(self, stdout: str, returncode: int = 0, stderr: str = ""): |
| 589 | + return type( |
| 590 | + "CompletedProcess", |
| 591 | + (), |
| 592 | + {"returncode": returncode, "stdout": stdout, "stderr": stderr}, |
| 593 | + )() |
| 594 | + |
| 595 | + def test_skip_for_none_backend(self) -> None: |
| 596 | + from autoskillit.cli.doctor._doctor_runtime import _check_codex_graduation |
| 597 | + from autoskillit.core import Severity |
| 598 | + |
| 599 | + result = _check_codex_graduation(backend=None) |
| 600 | + assert result.severity == Severity.OK |
| 601 | + assert result.check == "codex_graduation" |
| 602 | + assert "skipped" in result.message.lower() |
| 603 | + |
| 604 | + def test_skip_for_non_codex_backend(self) -> None: |
| 605 | + from types import SimpleNamespace |
| 606 | + |
| 607 | + from autoskillit.cli.doctor._doctor_runtime import _check_codex_graduation |
| 608 | + from autoskillit.core import Severity |
| 609 | + |
| 610 | + stub = SimpleNamespace(capabilities=SimpleNamespace(version_check_command="")) |
| 611 | + result = _check_codex_graduation(backend=stub) |
| 612 | + assert result.severity == Severity.OK |
| 613 | + assert "skipped" in result.message.lower() |
| 614 | + |
| 615 | + def test_all_green_omits_hold_note( |
| 616 | + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch |
| 617 | + ) -> None: |
| 618 | + import subprocess |
| 619 | + from datetime import UTC, datetime |
| 620 | + |
| 621 | + from autoskillit.cli.doctor._doctor_runtime import _check_codex_graduation |
| 622 | + from autoskillit.core import Severity |
| 623 | + from autoskillit.execution.backends.codex import CodexBackend |
| 624 | + |
| 625 | + monkeypatch.setattr( |
| 626 | + subprocess, |
| 627 | + "run", |
| 628 | + lambda *a, **kw: self._codex_result("Codex 0.130.0\n"), |
| 629 | + ) |
| 630 | + ts = datetime.now(UTC).isoformat() |
| 631 | + (tmp_path / "codex-probe-cache.json").write_text( |
| 632 | + json.dumps( |
| 633 | + { |
| 634 | + "schema_version": 1, |
| 635 | + "entries": {"0.130.0": {"passed": True, "probe_timestamp": ts}}, |
| 636 | + } |
| 637 | + ) |
| 638 | + ) |
| 639 | + (tmp_path / "codex-matrix-result.json").write_text(json.dumps({"passed": True})) |
| 640 | + (tmp_path / "sessions.jsonl").write_text( |
| 641 | + json.dumps({"backend": "codex", "success": True}) + "\n" |
| 642 | + ) |
| 643 | + |
| 644 | + result = _check_codex_graduation(backend=CodexBackend(), log_dir=tmp_path) |
| 645 | + assert result.severity == Severity.INFO |
| 646 | + assert "version=pass" in result.message |
| 647 | + assert "probe=pass" in result.message |
| 648 | + assert "matrix=pass" in result.message |
| 649 | + assert "smoke=pass" in result.message |
| 650 | + assert "EXPERIMENTAL" not in result.message |
| 651 | + |
| 652 | + def test_non_green_includes_hold_note( |
| 653 | + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch |
| 654 | + ) -> None: |
| 655 | + import subprocess |
| 656 | + |
| 657 | + from autoskillit.cli.doctor._doctor_runtime import _check_codex_graduation |
| 658 | + from autoskillit.core import Severity |
| 659 | + from autoskillit.execution.backends.codex import CodexBackend |
| 660 | + |
| 661 | + monkeypatch.setattr( |
| 662 | + subprocess, |
| 663 | + "run", |
| 664 | + lambda *a, **kw: self._codex_result("Codex 0.130.0\n"), |
| 665 | + ) |
| 666 | + (tmp_path / "sessions.jsonl").write_text( |
| 667 | + json.dumps({"backend": "codex", "success": True}) + "\n" |
| 668 | + ) |
| 669 | + |
| 670 | + result = _check_codex_graduation(backend=CodexBackend(), log_dir=tmp_path) |
| 671 | + assert result.severity == Severity.INFO |
| 672 | + assert "not-yet-run" in result.message |
| 673 | + assert "EXPERIMENTAL" in result.message |
| 674 | + |
| 675 | + def test_absent_files_yield_not_yet_run( |
| 676 | + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch |
| 677 | + ) -> None: |
| 678 | + import subprocess |
| 679 | + |
| 680 | + from autoskillit.cli.doctor._doctor_runtime import _check_codex_graduation |
| 681 | + from autoskillit.core import Severity |
| 682 | + from autoskillit.execution.backends.codex import CodexBackend |
| 683 | + |
| 684 | + monkeypatch.setattr( |
| 685 | + subprocess, |
| 686 | + "run", |
| 687 | + lambda *a, **kw: self._codex_result("Codex 0.130.0\n"), |
| 688 | + ) |
| 689 | + result = _check_codex_graduation(backend=CodexBackend(), log_dir=tmp_path) |
| 690 | + assert result.severity == Severity.INFO |
| 691 | + assert "probe=not-yet-run" in result.message |
| 692 | + assert "matrix=not-yet-run" in result.message |
| 693 | + assert "smoke=not-found" in result.message |
0 commit comments