diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..007731a --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,3 @@ +pytest~=6.2.3 +pytest-cov +coveralls \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/scanners/__init__.py b/tests/integration/scanners/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/scanners/test_first_scanner.py b/tests/integration/scanners/test_first_scanner.py new file mode 100644 index 0000000..5f8c4bb --- /dev/null +++ b/tests/integration/scanners/test_first_scanner.py @@ -0,0 +1,90 @@ +from unittest import TestCase +from unittest.mock import MagicMock +from src.scanners import FirstScanner +from datetime import datetime, timedelta + +from tests.utils import AsyncTestCase, fake_message + + +class TestFirstScanner(AsyncTestCase): + def test_help(self): + self.assertGreater(len(FirstScanner.help()), 0) + self.assertIn("%first", FirstScanner.help()) + + def test_empty_no_messages(self): + scanner = FirstScanner() + + command_msg = MagicMock() + self._await(scanner.init(command_msg, [])) + + results = self._await(scanner.get_results("")) + self.assertListEqual(["There was no messages matching your filters"], results) + + def test_empty_filtered(self): + scanner = FirstScanner() + scanner.raw_members = [1] + + self._await(scanner.init(fake_message(), [])) + + messages = [fake_message(author=2), fake_message(author=3)] + + for msg in messages: + scanner.compute_message(msg.channel, msg) + + results = self._await(scanner.get_results("")) + self.assertListEqual(["There was no messages matching your filters"], results) + + def test_normal(self): + scanner = FirstScanner() + + self._await(scanner.init(fake_message(), [])) + + messages = [ + fake_message(id=1, created_at=timedelta(days=-2)), + fake_message(id=2, created_at=timedelta(days=-3)), + fake_message(id=3, created_at=timedelta(days=-1)), + ] + + for msg in messages: + scanner.compute_message(msg.channel, msg) + + results = self._await(scanner.get_results("")) + + expected = messages[1] + self.assertListEqual( + [ + "First message out of 3", + f"{expected.created_at.strftime('%H:%M, %d %b. %Y')} (2 days ago) <@1> said:", + f"> {expected.content}", + "", + ], + results, + ) + + def test_filtered(self): + scanner = FirstScanner() + scanner.raw_members = [1] + + self._await(scanner.init(fake_message(), [])) + + messages = [ + fake_message(id=1, author=1, created_at=timedelta(days=-2)), + fake_message(id=2, author=2, created_at=timedelta(days=-3)), + fake_message(id=3, author=1, created_at=timedelta(days=-1)), + ] + + for msg in messages: + scanner.compute_message(msg.channel, msg) + + results = self._await(scanner.get_results("")) + + expected = messages[0] + self.assertListEqual( + [ + "First message out of 2", + f"{expected.created_at.strftime('%H:%M, %d %b. %Y')} (yesterday) <@1> said:", + f"> {expected.content}", + "", + ], + results, + ) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..e2051a0 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,99 @@ +from typing import List, Optional, Dict, Union +from unittest import TestCase +import asyncio +from datetime import datetime, timedelta +from unittest.mock import MagicMock +import random +import string + + +class AsyncTestCase(TestCase): + def setUp(self): + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(None) + + def tearDown(self): + self.loop.close() + + def _await(self, fn): + return self.loop.run_until_complete(fn) + + +RANDOM_TEXT_CHARS = string.ascii_letters + string.digits + string.punctuation + + +def random_text(min_len: int = 3, max_len: int = 45): + return "".join( + random.choice(RANDOM_TEXT_CHARS) + for _ in range(random.randrange(min_len, max_len)) + ) + + +def fake_guild(id: int = 1): + return MagicMock(id=id) + + +def fake_channel(id: int = 1, name: str = "fake-channel"): + return MagicMock(id=id, name=name, guild=fake_guild()) + + +def fake_message( + id: int = 1, + channel_id: int = 1, + channel_name: str = "fake-channel", + created_at: Optional[Union[datetime, timedelta]] = None, + edited_at: Optional[datetime] = None, + author: int = 1, + pinned: bool = False, + mention_everyone: bool = False, + tts: bool = False, + bot: bool = False, + content: Optional[str] = None, + mentions: Optional[List[int]] = None, + reference: Optional[int] = None, + role_mentions: Optional[List[int]] = None, + channel_mentions: Optional[List[int]] = None, + image: bool = False, + attachment: bool = False, + embed: bool = False, + reactions: Optional[Dict[str, List[int]]] = None, +): + if created_at is None: + created_at = datetime.now() + timedelta(hours=random.randrange(-30 * 24, 0)) + elif isinstance(created_at, timedelta): + created_at = datetime.now() + created_at + if isinstance(edited_at, timedelta): + edited_at = datetime.now() + edited_at + if content is None: + content = random_text() + if mentions is None: + mentions = [] + if role_mentions is None: + role_mentions = [] + if channel_mentions is None: + channel_mentions = [] + if reactions is None: + reactions = {} + return MagicMock( + id=id, + channel=fake_channel(channel_id, channel_name), + created_at=created_at, + edited_at=edited_at, + author=author, + pinned=pinned, + mention_everyone=mention_everyone, + tts=tts, + bot=bot, + content=content, + mentions=mentions, + raw_mentions=mentions, + reference=reference, + role_mentions=role_mentions, + raw_role_mentions=role_mentions, + channel_mentions=channel_mentions, + raw_channel_mentions=channel_mentions, + image=image, + attachment=attachment, + embed=embed, + reactions=reactions, + )