from __future__ import annotations import pytest from yuxi.channels.adapters.slack.security import ( DmPolicy, GroupPolicy, SecurityConfig, SecurityDecision, ) class TestDmPolicy: def test_values(self): assert DmPolicy.OPEN == "open" assert DmPolicy.PAIRING == "pairing" assert DmPolicy.ALLOWLIST == "allowlist" assert DmPolicy.DISABLED == "disabled" class TestGroupPolicy: def test_values(self): assert GroupPolicy.OPEN == "open" assert GroupPolicy.ALLOWLIST == "allowlist" assert GroupPolicy.DISABLED == "disabled" class TestSecurityConfigDefaults: def test_default_values(self): config = SecurityConfig() assert config.dm_policy == DmPolicy.ALLOWLIST assert config.group_policy == GroupPolicy.ALLOWLIST assert config.require_mention is False assert config.allow_from == set() assert config.allow_from_wildcard is False class TestSecurityConfigFromConfig: def test_empty_config(self): config = SecurityConfig.from_config(None) assert config.dm_policy == DmPolicy.ALLOWLIST def test_empty_dict(self): config = SecurityConfig.from_config({}) assert config.dm_policy == DmPolicy.ALLOWLIST def test_dm_policy_open(self): config = SecurityConfig.from_config({"dm_policy": "open"}) assert config.dm_policy == DmPolicy.OPEN def test_dm_policy_disabled(self): config = SecurityConfig.from_config({"dm_policy": "disabled"}) assert config.dm_policy == DmPolicy.DISABLED def test_dm_policy_pairing(self): config = SecurityConfig.from_config({"dm_policy": "pairing"}) assert config.dm_policy == DmPolicy.PAIRING def test_dm_policy_invalid_fallback(self): config = SecurityConfig.from_config({"dm_policy": "invalid_value"}) assert config.dm_policy == DmPolicy.ALLOWLIST def test_dm_policy_uppercase_env(self): config = SecurityConfig.from_config({"DM_POLICY": "open"}) assert config.dm_policy == DmPolicy.OPEN def test_group_policy_from_config(self): config = SecurityConfig.from_config({"group_policy": "open"}) assert config.group_policy == GroupPolicy.OPEN def test_group_policy_uppercase_env(self): config = SecurityConfig.from_config({"GROUP_POLICY": "disabled"}) assert config.group_policy == GroupPolicy.DISABLED def test_group_policy_invalid_fallback(self): config = SecurityConfig.from_config({"group_policy": "bad"}) assert config.group_policy == GroupPolicy.ALLOWLIST def test_require_mention(self): config = SecurityConfig.from_config({"require_mention": True}) assert config.require_mention is True def test_require_mention_uppercase_env(self): config = SecurityConfig.from_config({"REQUIRE_MENTION": True}) assert config.require_mention is True def test_allow_from_list(self): config = SecurityConfig.from_config({"allow_from": ["U001", "C001"]}) assert config.allow_from == {"U001", "C001"} def test_allow_from_comma_string(self): config = SecurityConfig.from_config({"allow_from": "U001, U002, C001"}) assert config.allow_from == {"U001", "U002", "C001"} def test_allow_from_wildcard(self): config = SecurityConfig.from_config({"allow_from": ["*"]}) assert config.allow_from_wildcard is True assert config.allow_from == set() def test_allow_from_mixed_wildcard_and_ids(self): config = SecurityConfig.from_config({"allow_from": ["*", "U001"]}) assert config.allow_from_wildcard is True assert config.allow_from == {"U001"} def test_allow_from_invalid_format_ignored(self): config = SecurityConfig.from_config({"allow_from": ["not_a_slack_id"]}) assert "not_a_slack_id" in config.allow_from def test_allow_from_empty_entries_filtered(self): config = SecurityConfig.from_config({"allow_from": ["U001", "", " ", "C001"]}) assert config.allow_from == {"U001", "C001"} def test_allow_from_non_list_scalar(self): config = SecurityConfig.from_config({"allow_from": 123}) assert config.allow_from == set() class TestSecurityConfigIsAllowed: def test_is_allowed_user_no_allowlist(self): config = SecurityConfig() assert config.is_allowed_user("U001") is False def test_is_allowed_user_in_allowlist(self): config = SecurityConfig(allow_from={"U001"}) assert config.is_allowed_user("U001") is True assert config.is_allowed_user("U002") is False def test_is_allowed_user_wildcard(self): config = SecurityConfig(allow_from_wildcard=True) assert config.is_allowed_user("U001") is True assert config.is_allowed_user("ANYONE") is True def test_is_allowed_channel_in_allowlist(self): config = SecurityConfig(allow_from={"C001"}) assert config.is_allowed_channel("C001") is True assert config.is_allowed_channel("C002") is False def test_is_allowed_channel_wildcard(self): config = SecurityConfig(allow_from_wildcard=True) assert config.is_allowed_channel("C001") is True class TestSecurityConfigDmPolicy: def test_dm_open(self): config = SecurityConfig(dm_policy=DmPolicy.OPEN) assert config.is_allowed_dm("U001") is True def test_dm_disabled(self): config = SecurityConfig(dm_policy=DmPolicy.DISABLED) assert config.is_allowed_dm("U001") is False def test_dm_pairing(self): config = SecurityConfig(dm_policy=DmPolicy.PAIRING) assert config.is_allowed_dm("U001") is True def test_dm_allowlist_user_in_list(self): config = SecurityConfig(dm_policy=DmPolicy.ALLOWLIST, allow_from={"U001"}) assert config.is_allowed_dm("U001") is True assert config.is_allowed_dm("U002") is False def test_dm_allowlist_empty(self): config = SecurityConfig(dm_policy=DmPolicy.ALLOWLIST, allow_from=set()) assert config.is_allowed_dm("U001") is False class TestSecurityConfigGroupPolicy: def test_group_open(self): config = SecurityConfig(group_policy=GroupPolicy.OPEN) assert config.is_allowed_group("C001") is True def test_group_disabled(self): config = SecurityConfig(group_policy=GroupPolicy.DISABLED) assert config.is_allowed_group("C001") is False def test_group_allowlist_channel_in_list(self): config = SecurityConfig(group_policy=GroupPolicy.ALLOWLIST, allow_from={"C001"}) assert config.is_allowed_group("C001") is True assert config.is_allowed_group("C002") is False class TestSecurityConfigShouldRequireMention: def test_require_mention_true(self): config = SecurityConfig(require_mention=True) assert config.should_require_mention("C001") is True def test_require_mention_false(self): config = SecurityConfig(require_mention=False) assert config.should_require_mention("C001") is False class TestSecurityConfigToDict: def test_to_dict(self): config = SecurityConfig( dm_policy=DmPolicy.OPEN, group_policy=GroupPolicy.ALLOWLIST, require_mention=True, allow_from={"U001", "C001"}, allow_from_wildcard=False, ) d = config.to_dict() assert d["dm_policy"] == "open" assert d["group_policy"] == "allowlist" assert d["require_mention"] is True assert d["allow_from"] == ["C001", "U001"] assert d["allow_from_wildcard"] is False class TestSecurityDecision: def test_allowed_decision(self): decision = SecurityDecision(allowed=True, reason="user_allowed") assert decision.allowed is True assert decision.reason == "user_allowed" assert decision.requires_pairing is False assert decision.pairing_code is None def test_pairing_required_decision(self): decision = SecurityDecision( allowed=False, reason="pairing_required", requires_pairing=True, pairing_code="ABC123", ) assert decision.allowed is False assert decision.requires_pairing is True assert decision.pairing_code == "ABC123"