Files
2026-07-13 13:12:33 +08:00

143 lines
3.9 KiB
Python

"""Tests for agent command no-key three-section error output."""
from __future__ import annotations
import re
from pathlib import Path
from unittest.mock import AsyncMock, patch
import pytest
import typer
from typer.testing import CliRunner
from opensquilla.cli.agent_cmd import AgentRunResult
# Provider env vars the project supports
_PROVIDER_ENV_VARS = [
"OPENROUTER_API_KEY",
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"DEEPSEEK_API_KEY",
"GEMINI_API_KEY",
"DASHSCOPE_API_KEY",
"MOONSHOT_API_KEY",
"MISTRAL_API_KEY",
"GROQ_API_KEY",
]
_NO_KEY_RESULT = AgentRunResult(
status="error",
agent_id="main",
session_key="agent:main:main",
text="",
usage={},
errors=[{"message": "No provider available", "code": "no_provider"}],
)
def _make_app() -> typer.Typer:
from opensquilla.cli.agent_cmd import run_agent_command
app = typer.Typer()
app.command()(run_agent_command)
return app
def _mock_patch():
return patch(
"opensquilla.cli.agent_cmd.run_agent_once",
new=AsyncMock(return_value=_NO_KEY_RESULT),
)
def test_tty_message_contains_onboard_and_envvar(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""Error output contains 'opensquilla onboard' and at least one provider env var."""
for key in _PROVIDER_ENV_VARS:
monkeypatch.delenv(key, raising=False)
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
runner = CliRunner()
app = _make_app()
with _mock_patch():
result = runner.invoke(app, ["-m", "hi"])
combined = result.output or ""
assert "opensquilla onboard" in combined
assert any(var in combined for var in _PROVIDER_ENV_VARS)
def test_no_color_env_strips_ansi_but_keeps_three_sections(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""With NO_COLOR=1 output has no ANSI codes but still shows all three section labels."""
for key in _PROVIDER_ENV_VARS:
monkeypatch.delenv(key, raising=False)
monkeypatch.setenv("NO_COLOR", "1")
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
runner = CliRunner()
app = _make_app()
with _mock_patch():
result = runner.invoke(app, ["-m", "hi"])
combined = result.output or ""
# No ANSI escape sequences when NO_COLOR is set
assert not re.search(r"\x1b\[", combined), "ANSI escape codes found with NO_COLOR=1"
# All three section labels must be present
assert "Symptom" in combined
assert "Cause" in combined
assert "Next" in combined
def test_term_dumb_does_not_raise(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""TERM=dumb environment: command completes without raising an unexpected exception."""
for key in _PROVIDER_ENV_VARS:
monkeypatch.delenv(key, raising=False)
monkeypatch.setenv("TERM", "dumb")
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
runner = CliRunner()
app = _make_app()
with _mock_patch():
result = runner.invoke(app, ["-m", "hi"])
# No unexpected exception: only SystemExit or clean exit allowed
assert result.exception is None or isinstance(result.exception, SystemExit)
combined = result.output or ""
assert len(combined) > 0, "Expected some output even under TERM=dumb"
def test_exit_code_nonzero_when_no_key(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""Exit code is non-zero when no provider key is available."""
for key in _PROVIDER_ENV_VARS:
monkeypatch.delenv(key, raising=False)
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
runner = CliRunner()
app = _make_app()
with _mock_patch():
result = runner.invoke(app, ["-m", "hi"])
assert result.exit_code != 0