228 lines
5.8 KiB
Python
228 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests for discovering standalone Codex CLI installations."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from pineagents.harnesses.codex import discovery
|
|
from pineagents.harnesses.codex.discovery import (
|
|
default_install_candidates,
|
|
resolve_codex_binary,
|
|
resolve_codex_binary_info,
|
|
)
|
|
|
|
|
|
def _executable(path: Path) -> Path:
|
|
path.parent.mkdir(parents=True)
|
|
path.touch()
|
|
path.chmod(path.stat().st_mode | 0o111)
|
|
return path
|
|
|
|
|
|
def test_reads_official_sdk_bundled_cli_path(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
binary = tmp_path / "sdk" / "codex"
|
|
module = SimpleNamespace(bundled_codex_path=lambda: binary)
|
|
monkeypatch.setattr(
|
|
discovery.importlib,
|
|
"import_module",
|
|
lambda name: module,
|
|
)
|
|
|
|
assert discovery.bundled_cli_candidate() == binary
|
|
|
|
|
|
def test_resolves_explicit_binary(tmp_path: Path) -> None:
|
|
binary = _executable(tmp_path / "custom" / "codex")
|
|
bundled = _executable(tmp_path / "sdk" / "codex")
|
|
|
|
assert resolve_codex_binary(str(binary), sdk_candidate=bundled) == binary
|
|
|
|
|
|
def test_resolves_qwenpaw_environment_binary(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
binary = _executable(tmp_path / "environment" / "codex")
|
|
monkeypatch.setenv("PATH", "")
|
|
monkeypatch.setenv("CODEX_BINARY", str(binary))
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
sdk_candidate=tmp_path / "missing",
|
|
install_candidates=[],
|
|
)
|
|
|
|
assert resolution is not None
|
|
assert resolution.path == binary
|
|
assert resolution.source == "environment"
|
|
|
|
|
|
def test_prefers_python_sdk_bundled_cli(tmp_path: Path) -> None:
|
|
binary = _executable(tmp_path / "sdk" / "codex")
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
environ={},
|
|
sdk_candidate=binary,
|
|
install_candidates=[],
|
|
)
|
|
|
|
assert resolution is not None
|
|
assert resolution.path == binary
|
|
assert resolution.source == "python-sdk"
|
|
|
|
|
|
def test_rejects_chatgpt_extension_binary_from_path(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
binary = _executable(
|
|
tmp_path
|
|
/ "extensions"
|
|
/ "openai.chatgpt-26.707.1-darwin-arm64"
|
|
/ "bin"
|
|
/ "macos-aarch64"
|
|
/ "codex",
|
|
)
|
|
monkeypatch.setenv("PATH", str(binary.parent))
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
"codex",
|
|
sdk_candidate=tmp_path / "missing",
|
|
install_candidates=[],
|
|
)
|
|
|
|
assert resolution is None
|
|
|
|
|
|
def test_rejects_explicit_chatgpt_app_binary(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
binary = _executable(
|
|
tmp_path
|
|
/ "Applications"
|
|
/ "ChatGPT.app"
|
|
/ "Contents"
|
|
/ "Resources"
|
|
/ "codex",
|
|
)
|
|
|
|
resolution = resolve_codex_binary_info(str(binary))
|
|
|
|
assert resolution is None
|
|
|
|
|
|
def test_falls_back_from_path_extension_to_standalone(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
extension_binary = _executable(
|
|
tmp_path
|
|
/ "extensions"
|
|
/ "openai.chatgpt-26.707.1-win32-x64"
|
|
/ "bin"
|
|
/ "linux-x64"
|
|
/ "codex",
|
|
)
|
|
standalone = _executable(tmp_path / ".local" / "bin" / "codex")
|
|
monkeypatch.setenv("PATH", str(extension_binary.parent))
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
"codex",
|
|
sdk_candidate=tmp_path / "missing",
|
|
install_candidates=[(standalone, "standalone")],
|
|
)
|
|
|
|
assert resolution is not None
|
|
assert resolution.path == standalone
|
|
assert resolution.source == "standalone"
|
|
|
|
|
|
def test_macos_candidates_only_include_standalone_cli(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
candidates = default_install_candidates(
|
|
tmp_path,
|
|
platform_name="darwin",
|
|
environ={},
|
|
)
|
|
|
|
assert candidates == (
|
|
(tmp_path / ".local" / "bin" / "codex", "standalone"),
|
|
)
|
|
|
|
|
|
def test_discovers_windows_standalone_binary(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("PATH", "")
|
|
local_app_data = tmp_path / "LocalAppData"
|
|
binary = (
|
|
local_app_data / "Programs" / "OpenAI" / "Codex" / "bin" / "codex.exe"
|
|
)
|
|
binary.parent.mkdir(parents=True)
|
|
binary.touch()
|
|
environment = {"LOCALAPPDATA": str(local_app_data)}
|
|
candidates = default_install_candidates(
|
|
tmp_path,
|
|
platform_name="win32",
|
|
environ=environment,
|
|
)
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
sdk_candidate=tmp_path / "missing.exe",
|
|
install_candidates=candidates,
|
|
platform_name="win32",
|
|
environ=environment,
|
|
)
|
|
|
|
assert resolution is not None
|
|
assert resolution.path == binary
|
|
assert resolution.source == "standalone"
|
|
|
|
|
|
def test_discovers_linux_standalone_binary(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("PATH", "")
|
|
binary = _executable(tmp_path / ".local" / "bin" / "codex")
|
|
candidates = default_install_candidates(
|
|
tmp_path,
|
|
platform_name="linux",
|
|
environ={},
|
|
)
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
sdk_candidate=tmp_path / "missing",
|
|
install_candidates=candidates,
|
|
platform_name="linux",
|
|
environ={},
|
|
)
|
|
|
|
assert resolution is not None
|
|
assert resolution.path == binary
|
|
assert resolution.source == "standalone"
|
|
|
|
|
|
def test_invalid_manual_binary_does_not_fall_back(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("PATH", "")
|
|
fallback = _executable(tmp_path / ".local" / "bin" / "codex")
|
|
|
|
resolution = resolve_codex_binary_info(
|
|
str(tmp_path / "missing" / "codex"),
|
|
sdk_candidate=tmp_path / "missing-sdk" / "codex",
|
|
install_candidates=[(fallback, "standalone")],
|
|
)
|
|
|
|
assert resolution is None
|