ForcePilot/backend/test/unit/channels/test_twitch_setup.py

423 lines
15 KiB
Python
Raw Normal View History

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"])