058092c1a1
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: banteg <4562643+banteg@users.noreply.github.com>
201 lines
6.0 KiB
Python
201 lines
6.0 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from takopi import cli
|
|
from takopi.config import ConfigError
|
|
from takopi.lockfile import LockError
|
|
from takopi.settings import TakopiSettings, TelegramTransportSettings
|
|
|
|
|
|
def _settings(overrides: dict | None = None) -> TakopiSettings:
|
|
payload = {
|
|
"transport": "telegram",
|
|
"transports": {"telegram": {"bot_token": "token", "chat_id": 123}},
|
|
}
|
|
if overrides:
|
|
payload.update(overrides)
|
|
return TakopiSettings.model_validate(payload)
|
|
|
|
|
|
def _telegram_settings(settings: TakopiSettings) -> TelegramTransportSettings:
|
|
tg = settings.transports.telegram
|
|
assert tg is not None
|
|
return tg
|
|
|
|
|
|
def test_parse_key_path_valid() -> None:
|
|
assert cli._parse_key_path("transports.telegram.chat_id") == [
|
|
"transports",
|
|
"telegram",
|
|
"chat_id",
|
|
]
|
|
|
|
|
|
def test_parse_key_path_invalid_segment() -> None:
|
|
with pytest.raises(ConfigError):
|
|
cli._parse_key_path("transports..chat_id")
|
|
|
|
|
|
def test_parse_value_toml_and_fallback() -> None:
|
|
assert cli._parse_value("true") is True
|
|
assert cli._parse_value("123") == 123
|
|
assert cli._parse_value("not-toml") == "not-toml"
|
|
|
|
|
|
def test_toml_literal_and_error() -> None:
|
|
assert cli._toml_literal("hello") == '"hello"'
|
|
with pytest.raises(ConfigError):
|
|
cli._toml_literal({"a": 1})
|
|
|
|
|
|
def test_flatten_config() -> None:
|
|
flattened = cli._flatten_config(
|
|
{"transports": {"telegram": {"chat_id": 123}}, "watch_config": True}
|
|
)
|
|
assert ("transports.telegram.chat_id", 123) in flattened
|
|
assert ("watch_config", True) in flattened
|
|
|
|
|
|
def test_normalized_value_from_settings() -> None:
|
|
settings = _settings()
|
|
assert cli._normalized_value_from_settings(settings, ["transport"]) == "telegram"
|
|
assert (
|
|
cli._normalized_value_from_settings(
|
|
settings, ["transports", "telegram", "chat_id"]
|
|
)
|
|
== 123
|
|
)
|
|
|
|
|
|
def test_should_run_interactive(monkeypatch) -> None:
|
|
class _Tty:
|
|
def isatty(self) -> bool:
|
|
return True
|
|
|
|
class _NotTty:
|
|
def isatty(self) -> bool:
|
|
return False
|
|
|
|
monkeypatch.setenv("TAKOPI_NO_INTERACTIVE", "1")
|
|
assert cli._should_run_interactive() is False
|
|
monkeypatch.delenv("TAKOPI_NO_INTERACTIVE")
|
|
|
|
monkeypatch.setattr(cli.sys, "stdin", _Tty())
|
|
monkeypatch.setattr(cli.sys, "stdout", _Tty())
|
|
assert cli._should_run_interactive() is True
|
|
|
|
monkeypatch.setattr(cli.sys, "stdin", _NotTty())
|
|
monkeypatch.setattr(cli.sys, "stdout", _Tty())
|
|
assert cli._should_run_interactive() is False
|
|
|
|
|
|
def test_resolve_transport_id_override(monkeypatch) -> None:
|
|
assert cli._resolve_transport_id(" telegram ") == "telegram"
|
|
with pytest.raises(ConfigError):
|
|
cli._resolve_transport_id(" ")
|
|
|
|
def _raise() -> None:
|
|
raise ConfigError("boom")
|
|
|
|
monkeypatch.setattr(cli, "load_or_init_config", _raise)
|
|
assert cli._resolve_transport_id(None) == "telegram"
|
|
|
|
|
|
def test_doctor_file_checks() -> None:
|
|
settings = _settings()
|
|
checks = cli._doctor_file_checks(_telegram_settings(settings))
|
|
assert checks[0].detail == "disabled"
|
|
|
|
settings = _settings(
|
|
{
|
|
"transports": {
|
|
"telegram": {
|
|
"bot_token": "token",
|
|
"chat_id": 1,
|
|
"files": {"enabled": True},
|
|
}
|
|
}
|
|
}
|
|
)
|
|
checks = cli._doctor_file_checks(_telegram_settings(settings))
|
|
assert checks[0].status == "warning"
|
|
|
|
|
|
def test_doctor_voice_checks(monkeypatch) -> None:
|
|
settings = _settings()
|
|
checks = cli._doctor_voice_checks(_telegram_settings(settings))
|
|
assert checks[0].detail == "disabled"
|
|
|
|
settings = _settings(
|
|
{
|
|
"transports": {
|
|
"telegram": {
|
|
"bot_token": "token",
|
|
"chat_id": 1,
|
|
"voice_transcription": True,
|
|
}
|
|
}
|
|
}
|
|
)
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
checks = cli._doctor_voice_checks(_telegram_settings(settings))
|
|
assert checks[0].status == "error"
|
|
assert checks[0].detail == "API key not set"
|
|
|
|
settings_with_key = _settings(
|
|
{
|
|
"transports": {
|
|
"telegram": {
|
|
"bot_token": "token",
|
|
"chat_id": 1,
|
|
"voice_transcription": True,
|
|
"voice_transcription_api_key": "local",
|
|
}
|
|
}
|
|
}
|
|
)
|
|
checks = cli._doctor_voice_checks(_telegram_settings(settings_with_key))
|
|
assert checks[0].status == "ok"
|
|
assert checks[0].detail == "voice_transcription_api_key set"
|
|
|
|
monkeypatch.setenv("OPENAI_API_KEY", "key")
|
|
checks = cli._doctor_voice_checks(_telegram_settings(settings))
|
|
assert checks[0].status == "ok"
|
|
|
|
|
|
def test_load_settings_optional(monkeypatch, tmp_path) -> None:
|
|
def _raise() -> None:
|
|
raise ConfigError("boom")
|
|
|
|
monkeypatch.setattr(cli, "load_settings_if_exists", _raise)
|
|
assert cli._load_settings_optional() == (None, None)
|
|
|
|
monkeypatch.setattr(cli, "load_settings_if_exists", lambda: None)
|
|
assert cli._load_settings_optional() == (None, None)
|
|
|
|
settings = _settings()
|
|
config_path = tmp_path / "takopi.toml"
|
|
monkeypatch.setattr(cli, "load_settings_if_exists", lambda: (settings, config_path))
|
|
assert cli._load_settings_optional() == (settings, config_path)
|
|
|
|
|
|
def test_acquire_config_lock_reports_error(monkeypatch, tmp_path) -> None:
|
|
config_path = tmp_path / "takopi.toml"
|
|
error = LockError(path=config_path, state="running")
|
|
|
|
def _raise(*_args, **_kwargs):
|
|
raise error
|
|
|
|
messages: list[tuple[str, bool]] = []
|
|
monkeypatch.setattr(cli, "acquire_lock", _raise)
|
|
monkeypatch.setattr(
|
|
cli.typer, "echo", lambda msg, err=False: messages.append((msg, err))
|
|
)
|
|
|
|
with pytest.raises(cli.typer.Exit) as exc:
|
|
cli.acquire_config_lock(config_path, "token")
|
|
|
|
assert exc.value.exit_code == 1
|
|
assert any("already running" in msg for msg, _ in messages)
|