From cd51654bf1c8719669391b4a629a2e4d455fe471 Mon Sep 17 00:00:00 2001 From: chengyongru Date: Thu, 18 Jun 2026 14:00:22 +0800 Subject: [PATCH] fix: keep channel login generic maintainer edit: remove the Feishu-specific config-file guard from the shared CLI login command. Feishu now follows the Weixin pattern where channel.login owns its setup and persistence. --- nanobot/cli/commands.py | 12 +------ tests/channels/test_feishu_login.py | 51 ++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index f74e7dc7..93f5dc30 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -1480,22 +1480,12 @@ def channels_login( ): """Authenticate with a channel via QR code or other interactive login.""" from nanobot.channels.registry import discover_all - from nanobot.config.loader import get_config_path, load_config, set_config_path + from nanobot.config.loader import load_config, set_config_path resolved_config_path = Path(config_path).expanduser().resolve() if config_path else None if resolved_config_path is not None: set_config_path(resolved_config_path) - default_config_path = get_config_path() - # Feishu requires a configuration file to be present - if channel_name == "feishu" and not config_path and not default_config_path.exists(): - console.print( - "[yellow]No configuration file found.[/yellow] " - "Please run [bold]nanobot onboard[/bold] to initialize nanobot first, " - "then retry this command." - ) - raise typer.Exit(1) - config = load_config(resolved_config_path) channel_cfg = getattr(config.channels, channel_name, None) or {} diff --git a/tests/channels/test_feishu_login.py b/tests/channels/test_feishu_login.py index ec6bceaa..366369ff 100644 --- a/tests/channels/test_feishu_login.py +++ b/tests/channels/test_feishu_login.py @@ -50,11 +50,52 @@ def test_begin_registration_requires_login_url(monkeypatch): feishu_module._begin_registration() -def test_channels_login_feishu_requires_default_config_file(monkeypatch, tmp_path): +@pytest.mark.asyncio +async def test_feishu_login_creates_missing_active_config(monkeypatch, tmp_path): missing_config = tmp_path / "missing.json" - monkeypatch.setattr(loader, "get_config_path", lambda: missing_config) + monkeypatch.setattr(loader, "_current_config_path", missing_config) + monkeypatch.setattr( + feishu_module, + "qr_register", + lambda initial_domain="feishu": { + "app_id": "cli_app", + "app_secret": "secret", + "domain": "feishu", + "bot_name": None, + "bot_open_id": None, + }, + ) - result = CliRunner().invoke(app, ["channels", "login", "feishu"]) + channel = FeishuChannel({}, None) - assert result.exit_code == 1 - assert "No configuration file found" in result.output + assert await channel.login() is True + assert missing_config.exists() + data = json.loads(missing_config.read_text(encoding="utf-8")) + assert data["channels"]["feishu"]["appId"] == "cli_app" + + +def test_channels_login_feishu_uses_generic_channel_login(monkeypatch, tmp_path): + missing_config = tmp_path / "missing.json" + seen: dict[str, object] = {} + + class _LoginChannel: + display_name = "Feishu" + + def __init__(self, config, bus): + seen["config"] = config + seen["bus"] = bus + + async def login(self, force: bool = False) -> bool: + seen["force"] = force + return True + + monkeypatch.setattr(loader, "_current_config_path", missing_config) + monkeypatch.setattr( + "nanobot.channels.registry.discover_all", + lambda: {"feishu": _LoginChannel}, + ) + + result = CliRunner().invoke(app, ["channels", "login", "feishu", "--force"]) + + assert result.exit_code == 0 + assert seen["force"] is True