diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/bot/exts/filtering/__init__.py | 0 | ||||
-rw-r--r-- | tests/bot/exts/filtering/test_filters.py | 41 | ||||
-rw-r--r-- | tests/bot/exts/filtering/test_settings.py | 20 | ||||
-rw-r--r-- | tests/bot/exts/filtering/test_settings_entries.py | 272 |
4 files changed, 333 insertions, 0 deletions
diff --git a/tests/bot/exts/filtering/__init__.py b/tests/bot/exts/filtering/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/bot/exts/filtering/__init__.py diff --git a/tests/bot/exts/filtering/test_filters.py b/tests/bot/exts/filtering/test_filters.py new file mode 100644 index 000000000..214637b52 --- /dev/null +++ b/tests/bot/exts/filtering/test_filters.py @@ -0,0 +1,41 @@ +import unittest + +from bot.exts.filtering._filter_context import Event, FilterContext +from bot.exts.filtering._filters.token import TokenFilter +from tests.helpers import MockMember, MockMessage, MockTextChannel + + +class FilterTests(unittest.TestCase): + """Test functionality of the token filter.""" + + def setUp(self) -> None: + member = MockMember(id=123) + channel = MockTextChannel(id=345) + message = MockMessage(author=member, channel=channel) + self.ctx = FilterContext(Event.MESSAGE, member, channel, "", message) + + def test_token_filter_triggers(self): + """The filter should evaluate to True only if its token is found in the context content.""" + test_cases = ( + (r"hi", "oh hi there", True), + (r"hi", "goodbye", False), + (r"bla\d{2,4}", "bla18", True), + (r"bla\d{2,4}", "bla1", False) + ) + + for pattern, content, expected in test_cases: + with self.subTest( + pattern=pattern, + content=content, + expected=expected, + ): + filter_ = TokenFilter({ + "id": 1, + "content": pattern, + "description": None, + "settings": {}, + "additional_field": "{}" # noqa: P103 + }) + self.ctx.content = content + result = filter_.triggered_on(self.ctx) + self.assertEqual(result, expected) diff --git a/tests/bot/exts/filtering/test_settings.py b/tests/bot/exts/filtering/test_settings.py new file mode 100644 index 000000000..ac21a5d47 --- /dev/null +++ b/tests/bot/exts/filtering/test_settings.py @@ -0,0 +1,20 @@ +import unittest + +import bot.exts.filtering._settings +from bot.exts.filtering._settings import create_settings + + +class FilterTests(unittest.TestCase): + """Test functionality of the Settings class and its subclasses.""" + + def test_create_settings_returns_none_for_empty_data(self): + """`create_settings` should return a tuple of two Nones when passed an empty dict.""" + result = create_settings({}) + + self.assertEquals(result, (None, None)) + + def test_unrecognized_entry_makes_a_warning(self): + """When an unrecognized entry name is passed to `create_settings`, it should be added to `_already_warned`.""" + create_settings({"abcd": {}}) + + self.assertIn("abcd", bot.exts.filtering._settings._already_warned) diff --git a/tests/bot/exts/filtering/test_settings_entries.py b/tests/bot/exts/filtering/test_settings_entries.py new file mode 100644 index 000000000..4db6438ab --- /dev/null +++ b/tests/bot/exts/filtering/test_settings_entries.py @@ -0,0 +1,272 @@ +import unittest + +from bot.exts.filtering._filter_context import Event, FilterContext +from bot.exts.filtering._settings_types.bypass_roles import RoleBypass +from bot.exts.filtering._settings_types.channel_scope import ChannelScope +from bot.exts.filtering._settings_types.filter_dm import FilterDM +from bot.exts.filtering._settings_types.infraction_and_notification import ( + Infraction, InfractionAndNotification, superstar +) +from tests.helpers import MockCategoryChannel, MockDMChannel, MockMember, MockMessage, MockRole, MockTextChannel + + +class FilterTests(unittest.TestCase): + """Test functionality of the Settings class and its subclasses.""" + + def setUp(self) -> None: + member = MockMember(id=123) + channel = MockTextChannel(id=345) + message = MockMessage(author=member, channel=channel) + self.ctx = FilterContext(Event.MESSAGE, member, channel, "", message) + + def test_role_bypass_is_off_for_user_without_roles(self): + """The role bypass should trigger when a user has no roles.""" + member = MockMember() + self.ctx.author = member + bypass_entry = RoleBypass(["123"]) + + result = bypass_entry.triggers_on(self.ctx) + + self.assertTrue(result) + + def test_role_bypass_is_on_for_a_user_with_the_right_role(self): + """The role bypass should not trigger when the user has one of its roles.""" + cases = ( + ([123], ["123"]), + ([123, 234], ["123"]), + ([123], ["123", "234"]), + ([123, 234], ["123", "234"]) + ) + + for user_role_ids, bypasses in cases: + with self.subTest(user_role_ids=user_role_ids, bypasses=bypasses): + user_roles = [MockRole(id=role_id) for role_id in user_role_ids] + member = MockMember(roles=user_roles) + self.ctx.author = member + bypass_entry = RoleBypass(bypasses) + + result = bypass_entry.triggers_on(self.ctx) + + self.assertFalse(result) + + def test_context_doesnt_trigger_for_empty_channel_scope(self): + """A filter is enabled for all channels by default.""" + channel = MockTextChannel() + scope = ChannelScope({"disabled_channels": None, "disabled_categories": None, "enabled_channels": None}) + self.ctx.channel = channel + + result = scope.triggers_on(self.ctx) + + self.assertTrue(result) + + def test_context_doesnt_trigger_for_disabled_channel(self): + """A filter shouldn't trigger if it's been disabled in the channel.""" + channel = MockTextChannel(id=123) + scope = ChannelScope({"disabled_channels": [123], "disabled_categories": None, "enabled_channels": None}) + self.ctx.channel = channel + + result = scope.triggers_on(self.ctx) + + self.assertFalse(result) + + def test_context_doesnt_trigger_in_disabled_category(self): + """A filter shouldn't trigger if it's been disabled in the category.""" + channel = MockTextChannel() + scope = ChannelScope({ + "disabled_channels": None, "disabled_categories": [channel.category.id], "enabled_channels": None + }) + self.ctx.channel = channel + + result = scope.triggers_on(self.ctx) + + self.assertFalse(result) + + def test_context_triggers_in_enabled_channel_in_disabled_category(self): + """A filter should trigger in an enabled channel even if it's been disabled in the category.""" + channel = MockTextChannel(id=123, category=MockCategoryChannel(id=234)) + scope = ChannelScope({"disabled_channels": None, "disabled_categories": [234], "enabled_channels": [123]}) + self.ctx.channel = channel + + result = scope.triggers_on(self.ctx) + + self.assertTrue(result) + + def test_filtering_dms_when_necessary(self): + """A filter correctly ignores or triggers in a channel depending on the value of FilterDM.""" + cases = ( + (True, MockDMChannel(), True), + (False, MockDMChannel(), False), + (True, MockTextChannel(), True), + (False, MockTextChannel(), True) + ) + + for apply_in_dms, channel, expected in cases: + with self.subTest(apply_in_dms=apply_in_dms, channel=channel): + filter_dms = FilterDM(apply_in_dms) + self.ctx.channel = channel + + result = filter_dms.triggers_on(self.ctx) + + self.assertEqual(expected, result) + + def test_infraction_merge_of_same_infraction_type(self): + """When both infractions are of the same type, the one with the longer duration wins.""" + infraction1 = InfractionAndNotification({ + "infraction_type": "mute", + "infraction_reason": "hi", + "infraction_duration": 10, + "dm_content": "how", + "dm_embed": "what is" + }) + infraction2 = InfractionAndNotification({ + "infraction_type": "mute", + "infraction_reason": "there", + "infraction_duration": 20, + "dm_content": "are you", + "dm_embed": "your name" + }) + + result = infraction1 | infraction2 + + self.assertDictEqual( + result.to_dict(), + { + "infraction_type": Infraction.MUTE, + "infraction_reason": "there", + "infraction_duration": 20.0, + "dm_content": "are you", + "dm_embed": "your name", + "_superstar": None + } + ) + + def test_infraction_merge_of_different_infraction_types(self): + """If there are two different infraction types, the one higher up the hierarchy should be picked.""" + infraction1 = InfractionAndNotification({ + "infraction_type": "mute", + "infraction_reason": "hi", + "infraction_duration": 20, + "dm_content": "", + "dm_embed": "" + }) + infraction2 = InfractionAndNotification({ + "infraction_type": "ban", + "infraction_reason": "", + "infraction_duration": 10, + "dm_content": "there", + "dm_embed": "" + }) + + result = infraction1 | infraction2 + + self.assertDictEqual( + result.to_dict(), + { + "infraction_type": Infraction.BAN, + "infraction_reason": "", + "infraction_duration": 10.0, + "dm_content": "there", + "dm_embed": "", + "_superstar": None + } + ) + + def test_infraction_merge_with_a_superstar(self): + """If there is a superstar infraction, it should be added to a separate field.""" + infraction1 = InfractionAndNotification({ + "infraction_type": "mute", + "infraction_reason": "hi", + "infraction_duration": 20, + "dm_content": "there", + "dm_embed": "" + }) + infraction2 = InfractionAndNotification({ + "infraction_type": "superstar", + "infraction_reason": "hello", + "infraction_duration": 10, + "dm_content": "you", + "dm_embed": "" + }) + + result = infraction1 | infraction2 + + self.assertDictEqual( + result.to_dict(), + { + "infraction_type": Infraction.MUTE, + "infraction_reason": "hi", + "infraction_duration": 20.0, + "dm_content": "there", + "dm_embed": "", + "_superstar": superstar("hello", 10.0) + } + ) + + def test_merge_two_superstar_infractions(self): + """When two superstar infractions are merged, the infraction type remains a superstar.""" + infraction1 = InfractionAndNotification({ + "infraction_type": "superstar", + "infraction_reason": "hi", + "infraction_duration": 20, + "dm_content": "", + "dm_embed": "" + }) + infraction2 = InfractionAndNotification({ + "infraction_type": "superstar", + "infraction_reason": "", + "infraction_duration": 10, + "dm_content": "there", + "dm_embed": "" + }) + + result = infraction1 | infraction2 + + self.assertDictEqual( + result.to_dict(), + { + "infraction_type": Infraction.SUPERSTAR, + "infraction_reason": "hi", + "infraction_duration": 20.0, + "dm_content": "", + "dm_embed": "", + "_superstar": None + } + ) + + def test_merge_a_voiceban_and_a_superstar_with_another_superstar(self): + """An infraction with a superstar merged with a superstar should combine under `_superstar`.""" + infraction1 = InfractionAndNotification({ + "infraction_type": "voice ban", + "infraction_reason": "hi", + "infraction_duration": 20, + "dm_content": "hello", + "dm_embed": "" + }) + infraction2 = InfractionAndNotification({ + "infraction_type": "superstar", + "infraction_reason": "bla", + "infraction_duration": 10, + "dm_content": "there", + "dm_embed": "" + }) + infraction3 = InfractionAndNotification({ + "infraction_type": "superstar", + "infraction_reason": "blabla", + "infraction_duration": 20, + "dm_content": "there", + "dm_embed": "" + }) + + result = infraction1 | infraction2 | infraction3 + + self.assertDictEqual( + result.to_dict(), + { + "infraction_type": Infraction.VOICE_BAN, + "infraction_reason": "hi", + "infraction_duration": 20, + "dm_content": "hello", + "dm_embed": "", + "_superstar": superstar("blabla", 20) + } + ) |