ForcePilot/backend/test/unit/channels/test_twitch_setup.py
Kris 69fe97a90d test: 批量修复并新增单元测试用例
1. 移除Telegram格式化测试中未使用的导入项
2. 修复Teams测试用例,添加monkeypatch参数并配置通配符开关
3. 更新钉钉适配器测试,替换弃用的流属性检查
4. 修正Twitch规范化测试,更新ROOMSTATE测试逻辑
5. 重构会话映射测试,完善数据库执行结果模拟
6. 格式化Slack块构建测试的长参数调用
7. 修复LINE适配器测试,更新能力断言和异步锁使用
8. 修正Slack会话解析测试,修复聊天类型判断错误
9. 更新能力测试,补充缺失的字段检查
10. 修复Matrix适配器测试,修正位置参数和配置校验逻辑
11. 为飞书分析模块测试添加跳过标记
12. 新增微信能力、限流、链接格式、会话路由等模块的单元测试
13. 修复Twitch适配器导入路径和测试断言
14. 新增Discord Webhook、Nextcloud Talk、Signal多账户等模块的单元测试
15. 修复Manager阶段测试的导入路径
16. 新增iMessage异常和命令处理的单元测试
17. 新增Nostr健康检查和相关模块的单元测试
18. 新增Signal守护进程和SSE重连相关测试
2026-05-13 16:43:01 +08:00

423 lines
15 KiB
Python

from __future__ import annotations
from yuxi.channels.adapters.twitch.setup import (
SetupWizardState,
SetupWizardStep,
TwitchSetupWizard,
create_setup_wizard_steps,
setup_wizard_to_config,
)
class TestSetupWizardStep:
def test_default_values(self):
step = SetupWizardStep(step_id="test")
assert step.step_id == "test"
assert step.required is True
assert step.completed is False
assert step.config_key == ""
assert step.config_value is None
def test_custom_values(self):
step = SetupWizardStep(
step_id="username",
title="Bot Username",
description="Enter username",
required=True,
completed=True,
config_key="bot_username",
config_value="mybot",
)
assert step.step_id == "username"
assert step.title == "Bot Username"
assert step.completed is True
assert step.config_value == "mybot"
class TestSetupWizardState:
def test_empty_state(self):
state = SetupWizardState()
assert state.steps == []
assert state.current_step == 0
assert state.current is None
assert state.is_complete is True
def test_with_steps_not_complete(self):
step1 = SetupWizardStep(step_id="s1", required=True)
step2 = SetupWizardStep(step_id="s2", required=True)
state = SetupWizardState(steps=[step1, step2])
assert state.is_complete is False
assert state.current == step1
def test_advance_marks_completed(self):
step1 = SetupWizardStep(step_id="s1", required=True)
step2 = SetupWizardStep(step_id="s2", required=True)
state = SetupWizardState(steps=[step1, step2])
state.advance()
assert step1.completed is True
assert state.current_step == 1
assert state.current == step2
def test_state_to_config(self):
step = SetupWizardStep(step_id="s1", config_key="bot_username", config_value="mybot")
state = SetupWizardState(steps=[step])
config = state.to_config()
assert config["bot_username"] == "mybot"
def test_skips_step_with_none_value(self):
step = SetupWizardStep(step_id="s1", config_key="key", config_value=None)
state = SetupWizardState(steps=[step])
config = state.to_config()
assert "key" not in config
def test_optional_step_not_required_for_complete(self):
step1 = SetupWizardStep(step_id="s1", required=True, completed=True)
step2 = SetupWizardStep(step_id="s2", required=False)
state = SetupWizardState(steps=[step1, step2])
assert state.is_complete is True
def test_advance_beyond_last_step(self):
step = SetupWizardStep(step_id="s1", required=True)
state = SetupWizardState(steps=[step])
state.advance()
state.advance()
assert state.current_step == 2
assert state.current is None
class TestCreateSetupWizardSteps:
def test_returns_steps_list(self):
steps = create_setup_wizard_steps()
assert len(steps) > 0
def test_contains_required_steps(self):
steps = create_setup_wizard_steps()
step_ids = [s.step_id for s in steps]
assert "username" in step_ids
assert "token" in step_ids
assert "client_id" in step_ids
assert "channels" in step_ids
assert "group_access" in step_ids
def test_required_steps_are_required(self):
steps = create_setup_wizard_steps()
for step in steps:
if step.step_id in ("username", "token", "client_id", "channels", "group_access"):
assert step.required is True
def test_optional_steps_not_required(self):
steps = create_setup_wizard_steps()
for step in steps:
if step.step_id in ("client_secret", "refresh_token", "dm_policy", "allowed_roles", "silent", "probe_timeout"):
assert step.required is False
def test_completed_reuses_existing_config(self):
steps = create_setup_wizard_steps({"bot_username": "existing_bot", "client_id": "existing_cid"})
username_step = next(s for s in steps if s.step_id == "username")
assert username_step.completed is True
client_id_step = next(s for s in steps if s.step_id == "client_id")
assert client_id_step.completed is True
token_step = next(s for s in steps if s.step_id == "token")
assert token_step.completed is False
class TestSetupWizardToConfig:
def test_converts_state_to_dict(self):
step = SetupWizardStep(step_id="s1", config_key="bot_username", config_value="testbot")
state = SetupWizardState(steps=[step])
result = setup_wizard_to_config(state)
assert result == {"bot_username": "testbot"}
class TestTwitchSetupWizard:
def test_initial_state(self):
wizard = TwitchSetupWizard()
assert wizard.config == {}
def test_initial_with_existing_config(self):
wizard = TwitchSetupWizard({"bot_username": "existing_bot"})
assert wizard.config["bot_username"] == "existing_bot"
def test_resolve_account_id(self):
wizard = TwitchSetupWizard()
result = wizard.resolve_account_id("my_account")
assert result == "my_account"
def test_resolve_account_id_default(self):
wizard = TwitchSetupWizard()
result = wizard.resolve_account_id()
assert result == "default"
def test_prompt_username_valid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_username("TestBot")
assert result is True
assert wizard.config["bot_username"] == "testbot"
def test_prompt_username_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_username("")
assert result is False
def test_prompt_username_whitespace(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_username(" ")
assert result is False
def test_prompt_token_with_value(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_token("mytoken")
assert result is True
assert wizard.config["access_token"].startswith("oauth:")
def test_prompt_token_with_oauth_prefix(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_token("oauth:existing")
assert result is True
assert wizard.config["access_token"] == "oauth:existing"
def test_prompt_token_from_env(self, monkeypatch):
import os
monkeypatch.setitem(os.environ, "TWITCH_ACCESS_TOKEN", "env_token")
wizard = TwitchSetupWizard()
result = wizard.prompt_token("")
assert result is True
assert "env_token" in wizard.config["access_token"]
def test_prompt_token_no_value_or_env(self, monkeypatch):
monkeypatch.delenv("TWITCH_ACCESS_TOKEN", raising=False)
monkeypatch.delenv("OPENCLAW_TWITCH_ACCESS_TOKEN", raising=False)
wizard = TwitchSetupWizard()
result = wizard.prompt_token("")
assert result is False
def test_prompt_client_id_valid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_client_id("my_client_id")
assert result is True
assert wizard.config["client_id"] == "my_client_id"
def test_prompt_client_id_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_client_id("")
assert result is False
def test_prompt_client_secret_with_value(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_client_secret("my_secret")
assert result is True
assert wizard.config["client_secret"] == "my_secret"
def test_prompt_client_secret_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_client_secret("")
assert result is True
def test_prompt_channels_string(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_channels("ch1, ch2, CH3")
assert result is True
assert wizard.config["channels"] == ["ch1", "ch2", "ch3"]
def test_prompt_channels_list(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_channels(["ch1", "CH2"])
assert result is True
assert wizard.config["channels"] == ["ch1", "CH2"]
def test_prompt_channels_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_channels("")
assert result is False
def test_prompt_refresh_token_with_value(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_refresh_token("refresh123")
assert result is True
assert wizard.config["refresh_token"] == "refresh123"
def test_prompt_refresh_token_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_refresh_token("")
assert result is True
def test_prompt_dm_policy_open(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_dm_policy("open")
assert result is True
assert wizard.config["dm_policy"] == "open"
def test_prompt_dm_policy_pairing(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_dm_policy("pairing")
assert result is True
assert wizard.config["dm_policy"] == "pairing"
def test_prompt_dm_policy_invalid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_dm_policy("block")
assert result is False
def test_prompt_group_access_valid(self):
wizard = TwitchSetupWizard()
for policy in ("open", "allowlist", "disabled", "mention_only"):
result = wizard.prompt_group_access(policy)
assert result is True
def test_prompt_group_access_invalid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_group_access("invalid")
assert result is False
def test_prompt_allowed_roles_string(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_allowed_roles("moderator, vip, subscriber")
assert result is True
assert "moderator" in wizard.config["allowedRoles"]
assert "vip" in wizard.config["allowedRoles"]
assert "subscriber" in wizard.config["allowedRoles"]
def test_prompt_allowed_roles_list(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_allowed_roles(["moderator", "vip"])
assert result is True
assert "moderator" in wizard.config["allowedRoles"]
def test_prompt_allowed_roles_invalid_defaults_to_all(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_allowed_roles("admin, super")
assert result is True
assert wizard.config["allowedRoles"] == ["all"]
def test_prompt_silent_yes(self):
wizard = TwitchSetupWizard()
for val in ("yes", "true", "1", "y", "on"):
result = wizard.prompt_silent(val)
assert result is True
assert wizard.config["silent"] is True
def test_prompt_silent_no(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_silent("no")
assert result is True
assert wizard.config["silent"] is False
def test_prompt_probe_timeout_valid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_probe_timeout(5000)
assert result is True
assert wizard.config["probe_timeout_ms"] == 5000
def test_prompt_probe_timeout_string_valid(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_probe_timeout("5000")
assert result is True
assert wizard.config["probe_timeout_ms"] == 5000
def test_prompt_probe_timeout_empty(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_probe_timeout("")
assert result is True
assert wizard.config["probe_timeout_ms"] == 10000
def test_prompt_probe_timeout_too_small(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_probe_timeout(500)
assert result is False
def test_prompt_probe_timeout_invalid_string(self):
wizard = TwitchSetupWizard()
result = wizard.prompt_probe_timeout("not_a_number")
assert result is False
def test_disable(self):
wizard = TwitchSetupWizard()
result = wizard.disable()
assert result == {"enabled": False}
def test_finalize_complete(self):
wizard = TwitchSetupWizard()
wizard.prompt_username("testbot")
wizard.prompt_token("mytoken")
wizard.prompt_client_id("cid123")
wizard.prompt_channels("test_channel")
wizard.prompt_group_access("open")
result = wizard.finalize()
assert result["enabled"] is True
assert result["config"]["bot_username"] == "testbot"
assert result["validation"]["valid"] is True
def test_finalize_incomplete(self):
wizard = TwitchSetupWizard()
result = wizard.finalize()
assert result["enabled"] is True
assert result["validation"]["valid"] is False
def test_finalize_disabled(self):
wizard = TwitchSetupWizard()
wizard.disable()
result = wizard.finalize()
assert result == {"enabled": False}
def test_finalize_and_write_no_path(self):
wizard = TwitchSetupWizard()
wizard.prompt_username("testbot")
wizard.prompt_token("token")
wizard.prompt_client_id("cid")
wizard.prompt_channels("ch")
wizard.prompt_group_access("open")
result = wizard.finalize_and_write()
assert result["storage"]["mode"] == "memory"
assert result["storage"]["success"] is True
def test_finalize_and_write_with_path(self, tmp_path):
import json
wizard = TwitchSetupWizard()
wizard.prompt_username("testbot")
wizard.prompt_token("token")
wizard.prompt_client_id("cid")
wizard.prompt_channels("ch")
wizard.prompt_group_access("open")
result = wizard.finalize_and_write(str(tmp_path))
assert result["storage"]["success"] is True
config_path = tmp_path / "twitch_config.json"
assert config_path.exists()
saved = json.loads(config_path.read_text(encoding="utf-8"))
assert saved["bot_username"] == "testbot"
def test_state_property(self):
wizard = TwitchSetupWizard()
assert isinstance(wizard.state, SetupWizardState)
def test_config_property(self):
wizard = TwitchSetupWizard({"bot_username": "mybot"})
assert wizard.config["bot_username"] == "mybot"
def test_validate_no_client_secret_warning(self):
wizard = TwitchSetupWizard()
wizard.prompt_username("bot")
wizard.prompt_token("tok")
wizard.prompt_client_id("cid")
wizard.prompt_channels("ch")
wizard.prompt_group_access("open")
result = wizard.finalize()
assert any("EventSub" in w for w in result["validation"]["warnings"])
def test_validate_invalid_group_policy_error(self):
wizard = TwitchSetupWizard()
wizard._config["group_policy"] = "invalid"
result = wizard.finalize()
assert result["validation"]["valid"] is False
def test_validate_unknown_dm_policy_warns(self):
wizard = TwitchSetupWizard()
wizard.prompt_username("bot")
wizard.prompt_token("tok")
wizard.prompt_client_id("cid")
wizard.prompt_channels("ch")
wizard.prompt_group_access("open")
wizard._config["dm_policy"] = "unknown_policy"
result = wizard.finalize()
assert any("dm_policy" in w for w in result["validation"]["warnings"])