134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
"""End-to-end coverage for the ``onboard --if-needed`` gate.
|
|
|
|
The gate represents first-run readiness. Optional capability sections still
|
|
surface action-required metadata, but they must not keep provider-complete
|
|
installs inside the interactive wizard forever.
|
|
|
|
Console output is captured by monkeypatching ``opensquilla.cli.onboard_cmd.console``
|
|
rather than ``capsys``. Rich consoles bind to a stdout reference at import
|
|
time, which makes ``capsys`` brittle under full-suite execution.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from opensquilla.cli.main import app
|
|
from opensquilla.gateway.config import GatewayConfig, LlmProviderConfig
|
|
from opensquilla.onboarding.status import get_onboarding_status
|
|
|
|
|
|
class _RecordingConsole:
|
|
def __init__(self) -> None:
|
|
self.messages: list[str] = []
|
|
|
|
def print(self, message: str = "", *_a, **_kw) -> None:
|
|
self.messages.append(str(message))
|
|
|
|
def joined(self) -> str:
|
|
return "\n".join(self.messages)
|
|
|
|
|
|
@pytest.fixture()
|
|
def runner() -> CliRunner:
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture()
|
|
def recorder(monkeypatch) -> _RecordingConsole:
|
|
instance = _RecordingConsole()
|
|
monkeypatch.setattr("opensquilla.cli.onboard_cmd.console", instance)
|
|
return instance
|
|
|
|
|
|
def _llm_ok_cfg() -> GatewayConfig:
|
|
cfg = GatewayConfig()
|
|
cfg.llm = LlmProviderConfig(
|
|
provider="openrouter",
|
|
model="m",
|
|
api_key="sk-x",
|
|
base_url="https://openrouter.ai/api/v1",
|
|
)
|
|
return cfg
|
|
|
|
|
|
def test_if_needed_skips_when_all_sections_ok_or_optional(
|
|
monkeypatch, tmp_path, recorder, runner
|
|
):
|
|
cfg = _llm_ok_cfg()
|
|
# Fresh config persisted to disk so has_config=True.
|
|
config_path = tmp_path / "config.toml"
|
|
config_path.write_text("")
|
|
cfg.config_path = str(config_path)
|
|
|
|
monkeypatch.setattr("opensquilla.cli.onboard_cmd.load_config", lambda _path=None: cfg)
|
|
|
|
result = runner.invoke(app, ["onboard", "--if-needed"])
|
|
assert result.exit_code == 0, result.output
|
|
assert "core setup is ready" in recorder.joined()
|
|
assert "optional capabilities need action" in recorder.joined()
|
|
assert "Optional next moves" in recorder.joined()
|
|
assert "Channel recipes:" in recorder.joined()
|
|
assert "Image recipes:" in recorder.joined()
|
|
|
|
|
|
def test_if_needed_surfaces_optional_actions_without_running_wizard(
|
|
monkeypatch, tmp_path, recorder, runner
|
|
):
|
|
cfg = _llm_ok_cfg()
|
|
cfg.llm = LlmProviderConfig(
|
|
provider="deepseek",
|
|
model="m",
|
|
api_key="sk-x",
|
|
base_url="https://api.deepseek.com/v1",
|
|
)
|
|
cfg.search_provider = "brave"
|
|
cfg.search_api_key = ""
|
|
cfg.search_api_key_env = ""
|
|
cfg.image_generation.enabled = True
|
|
cfg.image_generation.primary = "openai/gpt-image-1"
|
|
cfg.memory.embedding.provider = "auto"
|
|
|
|
config_path = tmp_path / "config.toml"
|
|
config_path.write_text("")
|
|
cfg.config_path = str(config_path)
|
|
monkeypatch.delenv("BRAVE_API_KEY", raising=False)
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
monkeypatch.setattr("opensquilla.cli.onboard_cmd.load_config", lambda _path=None: cfg)
|
|
|
|
status = get_onboarding_status(cfg)
|
|
assert status.needs_onboarding is False
|
|
action_sections = ("search", "image_generation")
|
|
for section in action_sections:
|
|
assert status.section_details[section]["actionRequired"] is True
|
|
|
|
# Guard the assertion: this path should exit before the wizard is reached.
|
|
monkeypatch.setattr("opensquilla.onboarding.flow._is_tty", lambda: False)
|
|
|
|
result = runner.invoke(app, ["onboard", "--if-needed"])
|
|
assert result.exit_code == 0, result.output
|
|
assert "core setup is ready" in recorder.joined()
|
|
assert "optional capabilities need action" in recorder.joined()
|
|
for section in action_sections:
|
|
assert status.section_details[section]["label"] in recorder.joined()
|
|
assert "already complete" not in recorder.joined()
|
|
assert "unfinished sections" not in recorder.joined()
|
|
|
|
|
|
def test_onboard_status_subcommand_emits_json(monkeypatch, tmp_path, runner):
|
|
cfg = _llm_ok_cfg()
|
|
config_path = tmp_path / "config.toml"
|
|
config_path.write_text("")
|
|
cfg.config_path = str(config_path)
|
|
monkeypatch.setattr("opensquilla.cli.onboard_cmd.load_config", lambda _path=None: cfg)
|
|
|
|
result = runner.invoke(app, ["onboard", "status", "--json"])
|
|
assert result.exit_code == 0, result.output
|
|
import json
|
|
|
|
payload = json.loads(result.output.strip().splitlines()[-1])
|
|
assert payload["needsOnboarding"] is False
|
|
assert payload["sections"]["llm"] == "ok"
|
|
assert "router" in payload["sections"]
|