112 lines
4.2 KiB
Python
112 lines
4.2 KiB
Python
"""Tests for the OpenCode ``omni setup`` default-model picker helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
import omnigent.cli as cli
|
|
from omnigent.cli import _list_opencode_models, _load_global_config, _set_opencode_default_model
|
|
|
|
|
|
@pytest.fixture
|
|
def _isolated_config(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path:
|
|
"""Point the global config at a tmp file so saves don't touch ``~/.omnigent``."""
|
|
path = tmp_path / "config.yaml"
|
|
monkeypatch.setattr("omnigent.cli._GLOBAL_CONFIG_PATH", path)
|
|
return path
|
|
|
|
|
|
def _fake_spec() -> SimpleNamespace:
|
|
return SimpleNamespace(binary="opencode")
|
|
|
|
|
|
# ── _list_opencode_models ───────────────────────────────────────────────────
|
|
|
|
|
|
def test_list_models_parses_nonblank_lines(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
"omnigent.onboarding.harness_install.harness_install_spec", lambda _key: _fake_spec()
|
|
)
|
|
monkeypatch.setattr(
|
|
cli.subprocess,
|
|
"run",
|
|
lambda *a, **k: subprocess.CompletedProcess(
|
|
a, 0, stdout="anthropic/claude-sonnet-4-5\nopenai/gpt-5.5\n\n \n", stderr=""
|
|
),
|
|
)
|
|
assert _list_opencode_models() == ["anthropic/claude-sonnet-4-5", "openai/gpt-5.5"]
|
|
|
|
|
|
def test_list_models_empty_when_cli_absent(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
"omnigent.onboarding.harness_install.harness_install_spec", lambda _key: None
|
|
)
|
|
assert _list_opencode_models() == []
|
|
|
|
|
|
def test_list_models_empty_on_subprocess_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
"omnigent.onboarding.harness_install.harness_install_spec", lambda _key: _fake_spec()
|
|
)
|
|
|
|
def _boom(*_a: object, **_k: object) -> object:
|
|
raise OSError("no binary")
|
|
|
|
monkeypatch.setattr(cli.subprocess, "run", _boom)
|
|
assert _list_opencode_models() == []
|
|
|
|
|
|
# ── _set_opencode_default_model ─────────────────────────────────────────────
|
|
|
|
|
|
def test_set_default_model_persists_choice(
|
|
monkeypatch: pytest.MonkeyPatch, _isolated_config: Path
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
cli, "_list_opencode_models", lambda: ["anthropic/claude-sonnet-4-5", "x/y"]
|
|
)
|
|
monkeypatch.setattr("omnigent.onboarding.interactive.select", lambda *a, **k: 0)
|
|
status = _set_opencode_default_model(current=None)
|
|
assert status == "✓ default model: anthropic/claude-sonnet-4-5"
|
|
assert _load_global_config()["opencode_model"] == "anthropic/claude-sonnet-4-5"
|
|
|
|
|
|
def test_set_default_model_clear_unsets(
|
|
monkeypatch: pytest.MonkeyPatch, _isolated_config: Path
|
|
) -> None:
|
|
cli._save_global_config({"opencode_model": "x/y"})
|
|
monkeypatch.setattr(cli, "_list_opencode_models", lambda: ["a/b"])
|
|
# options == ["a/b", "Clear default ..."]; index 1 is the clear row.
|
|
monkeypatch.setattr("omnigent.onboarding.interactive.select", lambda *a, **k: 1)
|
|
status = _set_opencode_default_model(current="x/y")
|
|
assert status == "✓ default model cleared"
|
|
assert "opencode_model" not in _load_global_config()
|
|
|
|
|
|
def test_set_default_model_cancel_is_noop(
|
|
monkeypatch: pytest.MonkeyPatch, _isolated_config: Path
|
|
) -> None:
|
|
monkeypatch.setattr(cli, "_list_opencode_models", lambda: ["a/b"])
|
|
monkeypatch.setattr("omnigent.onboarding.interactive.select", lambda *a, **k: -1)
|
|
assert _set_opencode_default_model(current=None) is None
|
|
assert _load_global_config() == {}
|
|
|
|
|
|
def test_set_default_model_no_models_short_circuits(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(cli, "_list_opencode_models", list)
|
|
called = False
|
|
|
|
def _select(*_a: object, **_k: object) -> int:
|
|
nonlocal called
|
|
called = True
|
|
return 0
|
|
|
|
monkeypatch.setattr("omnigent.onboarding.interactive.select", _select)
|
|
status = _set_opencode_default_model(current=None)
|
|
assert status is not None and status.startswith("✗")
|
|
assert called is False # never prompts when there's nothing to pick
|