from __future__ import annotations import pytest from pydantic import ValidationError from yuxi.channels.adapters.imessage.config_schema import ( DmPolicyEnum, GroupOverrideConfig, GroupPolicyEnum, IMessageAccountConfig, IMessageConnectionConfig, IMessageFullConfig, IMessageSecurityConfigSchema, validate_config, ) class TestDmPolicyEnum: def test_values(self): assert DmPolicyEnum.PAIRING.value == "pairing" assert DmPolicyEnum.ALLOWLIST.value == "allowlist" assert DmPolicyEnum.OPEN.value == "open" assert DmPolicyEnum.DISABLED.value == "disabled" def test_coercion(self): schema = IMessageSecurityConfigSchema(dm_policy="open") assert schema.dm_policy == DmPolicyEnum.OPEN def test_coercion_invalid(self): schema = IMessageSecurityConfigSchema(dm_policy="invalid_policy") assert schema.dm_policy == DmPolicyEnum.PAIRING class TestGroupPolicyEnum: def test_values(self): assert GroupPolicyEnum.OPEN.value == "open" assert GroupPolicyEnum.ALLOWLIST.value == "allowlist" assert GroupPolicyEnum.DISABLED.value == "disabled" def test_coercion_invalid(self): schema = IMessageSecurityConfigSchema(group_policy="invalid") assert schema.group_policy == GroupPolicyEnum.ALLOWLIST class TestIMessageConnectionConfig: def test_defaults(self): cfg = IMessageConnectionConfig() assert cfg.server_url == "http://localhost:1234" assert cfg.password == "" assert cfg.probe_timeout_ms == 10000 assert cfg.max_retries == 3 assert cfg.http_timeout_s == 30.0 def test_custom_values(self): cfg = IMessageConnectionConfig( server_url="http://mac.local:4321", password="secret", probe_timeout_ms=5000, max_retries=5, http_timeout_s=60.0, ) assert cfg.server_url == "http://mac.local:4321" assert cfg.password == "secret" assert cfg.probe_timeout_ms == 5000 assert cfg.max_retries == 5 assert cfg.http_timeout_s == 60.0 def test_probe_timeout_out_of_range(self): with pytest.raises(ValidationError): IMessageConnectionConfig(probe_timeout_ms=500) def test_max_retries_out_of_range(self): with pytest.raises(ValidationError): IMessageConnectionConfig(max_retries=0) class TestIMessageSecurityConfigSchema: def test_defaults(self): cfg = IMessageSecurityConfigSchema() assert cfg.dm_policy == DmPolicyEnum.PAIRING assert cfg.group_policy == GroupPolicyEnum.ALLOWLIST assert cfg.allow_from == [] assert cfg.group_allow_from == [] assert cfg.require_mention is False def test_custom_values(self): cfg = IMessageSecurityConfigSchema( dm_policy=DmPolicyEnum.OPEN, group_policy=GroupPolicyEnum.OPEN, allow_from=["+8613800138000"], group_allow_from=["iMessage;-;group001"], require_mention=True, ) assert cfg.dm_policy == DmPolicyEnum.OPEN assert cfg.group_policy == GroupPolicyEnum.OPEN assert "+8613800138000" in cfg.allow_from assert "iMessage;-;group001" in cfg.group_allow_from assert cfg.require_mention is True class TestIMessageFullConfig: def test_defaults(self): cfg = IMessageFullConfig() assert cfg.name == "iMessage" assert cfg.block_streaming is False assert cfg.text_chunk_limit == 4096 assert cfg.media_max_mb == 100 assert cfg.history_limit == 20 assert cfg.include_attachments is False assert cfg.accounts == {} assert cfg.groups == {} assert cfg.loop_rate_limit == 5 def test_text_chunk_limit_out_of_range(self): with pytest.raises(ValidationError): IMessageFullConfig(text_chunk_limit=50) def test_accounts_from_list(self): cfg = IMessageFullConfig( accounts=[ IMessageAccountConfig(account_id="acct1"), IMessageAccountConfig(account_id="acct2"), ] ) assert len(cfg.accounts) == 2 assert "acct1" in cfg.accounts assert "acct2" in cfg.accounts def test_accounts_none_coerced_to_dict(self): cfg = IMessageFullConfig(accounts=None) assert cfg.accounts == {} class TestGroupOverrideConfig: def test_defaults(self): cfg = GroupOverrideConfig() assert cfg.enabled is None assert cfg.require_mention is None assert cfg.tools is None def test_custom_values(self): cfg = GroupOverrideConfig(enabled=True, require_mention=True, tools=["calc", "weather"]) assert cfg.enabled is True assert cfg.require_mention is True assert cfg.tools == ["calc", "weather"] class TestValidateConfig: def test_basic_valid_config(self): result = validate_config({"server_url": "http://localhost:1234", "password": "test"}) assert isinstance(result, IMessageFullConfig) def test_empty_config(self): result = validate_config({}) assert isinstance(result, IMessageFullConfig) def test_dm_policy_string(self): result = validate_config({"security": {"dm_policy": "open"}}) assert result.security.dm_policy == DmPolicyEnum.OPEN def test_block_streaming(self): result = validate_config({"block_streaming": True}) assert result.block_streaming is True def test_ws_reconnect_params(self): result = validate_config({"ws_reconnect_initial_delay": 10.0}) assert result.ws_reconnect_initial_delay == 10.0 def test_loop_rate_params(self): result = validate_config( {"loop_rate_limit": 10, "loop_rate_window_s": 120.0, "loop_cooldown_s": 300.0} ) assert result.loop_rate_limit == 10 assert result.loop_rate_window_s == 120.0 assert result.loop_cooldown_s == 300.0