aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorGravatar ChrisJL <[email protected]>2022-11-02 14:21:07 +0000
committerGravatar GitHub <[email protected]>2022-11-02 14:21:07 +0000
commit983d23777ef8cf084ec2a51471668edea266ca91 (patch)
tree531c6ed6edd65ade28841ff00e15095037397ae2 /tests
parentSimplify REPL example (diff)
parentMerge pull request #2311 from python-discord/mbaruh-bump-psql (diff)
Merge branch 'main' into Keyacom-patch-1
Diffstat (limited to 'tests')
-rw-r--r--tests/bot/exts/backend/test_error_handler.py20
-rw-r--r--tests/bot/exts/backend/test_security.py (renamed from tests/bot/exts/filters/test_security.py)2
-rw-r--r--tests/bot/exts/info/test_information.py24
-rw-r--r--tests/bot/exts/recruitment/__init__.py0
-rw-r--r--tests/bot/exts/recruitment/talentpool/test_review.py203
5 files changed, 230 insertions, 19 deletions
diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py
index 7562f6aa8..562c827b9 100644
--- a/tests/bot/exts/backend/test_error_handler.py
+++ b/tests/bot/exts/backend/test_error_handler.py
@@ -9,7 +9,7 @@ from bot.exts.backend import error_handler
from bot.exts.info.tags import Tags
from bot.exts.moderation.silence import Silence
from bot.utils.checks import InWhitelistCheckFailure
-from tests.helpers import MockBot, MockContext, MockGuild, MockRole, MockTextChannel
+from tests.helpers import MockBot, MockContext, MockGuild, MockRole, MockTextChannel, MockVoiceChannel
class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):
@@ -163,12 +163,11 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):
self.assertIsNone(await self.cog.on_command_error(self.ctx, case["error"]))
case["mock_function_to_call"].assert_awaited_once_with(self.ctx, case["error"].original)
- async def test_error_handler_two_other_errors(self):
- """Should call `handle_unexpected_error` if error is `MaxConcurrencyReached` or `ExtensionError`."""
+ async def test_error_handler_unexpected_errors(self):
+ """Should call `handle_unexpected_error` if error is `ExtensionError`."""
self.cog.handle_unexpected_error = AsyncMock()
errs = (
- errors.MaxConcurrencyReached(1, MagicMock()),
- errors.ExtensionError(name="foo")
+ errors.ExtensionError(name="foo"),
)
for err in errs:
@@ -192,7 +191,16 @@ class TrySilenceTests(unittest.IsolatedAsyncioTestCase):
self.bot = MockBot()
self.silence = Silence(self.bot)
self.bot.get_command.return_value = self.silence.silence
- self.ctx = MockContext(bot=self.bot)
+
+ # Use explicit mock channels so that discord.utils.get doesn't think
+ # guild.text_channels is an async iterable due to the MagicMock having
+ # a __aiter__ attr.
+ guild_overrides = {
+ "text_channels": [MockTextChannel(), MockTextChannel()],
+ "voice_channels": [MockVoiceChannel(), MockVoiceChannel()],
+ }
+ self.guild = MockGuild(**guild_overrides)
+ self.ctx = MockContext(bot=self.bot, guild=self.guild)
self.cog = error_handler.ErrorHandler(self.bot)
async def test_try_silence_context_invoked_from_error_handler(self):
diff --git a/tests/bot/exts/filters/test_security.py b/tests/bot/exts/backend/test_security.py
index 007b7b1eb..c3985c609 100644
--- a/tests/bot/exts/filters/test_security.py
+++ b/tests/bot/exts/backend/test_security.py
@@ -2,7 +2,7 @@ import unittest
from discord.ext.commands import NoPrivateMessage
-from bot.exts.filters import security
+from bot.exts.backend import security
from tests.helpers import MockBot, MockContext
diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py
index 9f5143c01..65595e959 100644
--- a/tests/bot/exts/info/test_information.py
+++ b/tests/bot/exts/info/test_information.py
@@ -603,9 +603,9 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
async def test_return_none_if_one_rule_number_is_invalid(self):
test_cases = [
- (('1', '6', '7', '8'), (6, 7, 8)),
- (('10', "first"), (10, )),
- (("first", 10), (10, ))
+ ("1 6 7 8", (6, 7, 8)),
+ ("10 first", (10,)),
+ ("first 10", (10,))
]
for raw_user_input, extracted_rule_numbers in test_cases:
@@ -614,7 +614,7 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
str(rule_number) for rule_number in extracted_rule_numbers
if rule_number < 1 or rule_number > len(self.full_rules))
- final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input)
+ final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input)
self.assertEqual(
self.ctx.send.call_args,
@@ -624,26 +624,26 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase):
async def test_return_correct_rule_numbers(self):
test_cases = [
- (("1", "2", "first"), {1, 2}),
- (("1", "hello", "2", "second"), {1}),
- (("second", "third", "unknown", "999"), {2, 3})
+ ("1 2 first", {1, 2}),
+ ("1 hello 2 second", {1}),
+ ("second third unknown 999", {2, 3}),
]
for raw_user_input, expected_matched_rule_numbers in test_cases:
with self.subTest(identifier=raw_user_input):
- final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input)
+ final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input)
self.assertEqual(expected_matched_rule_numbers, final_rule_numbers)
async def test_return_default_rules_when_no_input_or_no_match_are_found(self):
test_cases = [
- ((), None),
- (("hello", "2", "second"), None),
- (("hello", "999"), None),
+ ("", None),
+ ("hello 2 second", None),
+ ("hello 999", None),
]
for raw_user_input, expected_matched_rule_numbers in test_cases:
with self.subTest(identifier=raw_user_input):
- final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input)
+ final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input)
embed = self.ctx.send.call_args.kwargs['embed']
self.assertEqual(information.DEFAULT_RULES_DESCRIPTION, embed.description)
self.assertEqual(expected_matched_rule_numbers, final_rule_numbers)
diff --git a/tests/bot/exts/recruitment/__init__.py b/tests/bot/exts/recruitment/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/bot/exts/recruitment/__init__.py
diff --git a/tests/bot/exts/recruitment/talentpool/test_review.py b/tests/bot/exts/recruitment/talentpool/test_review.py
new file mode 100644
index 000000000..ed9b66e12
--- /dev/null
+++ b/tests/bot/exts/recruitment/talentpool/test_review.py
@@ -0,0 +1,203 @@
+import unittest
+from datetime import datetime, timedelta, timezone
+from unittest.mock import Mock, patch
+
+from bot.exts.recruitment.talentpool import _review
+from tests.helpers import MockBot, MockMember, MockMessage, MockTextChannel
+
+
+class AsyncIterator:
+ """Normal->Async iterator helper."""
+
+ def __init__(self, seq):
+ self.iter = iter(seq)
+
+ def __aiter__(self):
+ return self
+
+ # Allows it to be used to mock the discord TextChannel.history function
+ def __call__(self):
+ return self
+
+ async def __anext__(self):
+ try:
+ return next(self.iter)
+ except StopIteration:
+ raise StopAsyncIteration
+
+
+def nomination(
+ inserted_at: datetime,
+ num_entries: int,
+ reviewed: bool = False,
+ id: int | None = None
+) -> tuple[int, dict]:
+ return (
+ id or MockMember().id,
+ {"inserted_at": inserted_at.isoformat(), "entries": [Mock() for _ in range(num_entries)], "reviewed": reviewed},
+ )
+
+
+class ReviewerTests(unittest.IsolatedAsyncioTestCase):
+ """Tests for the talentpool reviewer."""
+
+ def setUp(self):
+ self.bot_user = MockMember(bot=True)
+ self.bot = MockBot(user=self.bot_user)
+
+ self.voting_channel = MockTextChannel()
+ self.bot.get_channel = Mock(return_value=self.voting_channel)
+
+ self.pool = Mock(name="MockTalentPool")
+ self.reviewer = _review.Reviewer(self.bot, self.pool)
+
+ @patch("bot.exts.recruitment.talentpool._review.MAX_ONGOING_REVIEWS", 3)
+ @patch("bot.exts.recruitment.talentpool._review.MIN_REVIEW_INTERVAL", timedelta(days=1))
+ async def test_is_ready_for_review(self):
+ """Tests for the `is_ready_for_review` function."""
+ too_recent = datetime.now(timezone.utc) - timedelta(hours=1)
+ not_too_recent = datetime.now(timezone.utc) - timedelta(days=7)
+ cases = (
+ # Only one review, and not too recent, so ready.
+ (
+ [
+ MockMessage(author=self.bot_user, content="wookie for Helper!", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
+ ],
+ True,
+ ),
+
+ # Three reviews, so not ready.
+ (
+ [
+ MockMessage(author=self.bot_user, content="Chrisjl for Helper!", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="Zig for Helper!", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="Scaleios for Helper!", created_at=not_too_recent),
+ ],
+ False,
+ ),
+
+ # Only one review, but too recent, so not ready.
+ (
+ [
+ MockMessage(author=self.bot_user, content="Chrisjl for Helper!", created_at=too_recent),
+ ],
+ False,
+ ),
+
+ # Only two reviews, and not too recent, so ready.
+ (
+ [
+ MockMessage(author=self.bot_user, content="Not a review", created_at=too_recent),
+ MockMessage(author=self.bot_user, content="wookie for Helper!", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="wookie for Helper!", created_at=not_too_recent),
+ MockMessage(author=self.bot_user, content="Not a review", created_at=not_too_recent),
+ ],
+ True,
+ ),
+
+ # No messages, so ready.
+ ([], True),
+ )
+
+ for messages, expected in cases:
+ with self.subTest(messages=messages, expected=expected):
+ self.voting_channel.history = AsyncIterator(messages)
+ res = await self.reviewer.is_ready_for_review()
+ self.assertIs(res, expected)
+
+ @patch("bot.exts.recruitment.talentpool._review.MIN_NOMINATION_TIME", timedelta(days=7))
+ async def test_get_user_for_review(self):
+ """Test get_user_for_review function."""
+ now = datetime.now(timezone.utc)
+
+ # Each case contains a list of nominations, followed by the index in that list
+ # of the one that should be selected, or None if None should be returned
+ cases = [
+ # One nomination, too recent so don't send.
+ (
+ [
+ nomination(now - timedelta(days=1), 5),
+ ],
+ None,
+ ),
+
+ # First one has most entries so should be returned.
+ (
+ [
+ nomination(now - timedelta(days=10), 6),
+ nomination(now - timedelta(days=10), 5),
+ nomination(now - timedelta(days=9), 5),
+ nomination(now - timedelta(days=11), 5),
+ ],
+ 0,
+ ),
+
+ # Same number of entries so oldest (second) should be returned.
+ (
+ [
+ nomination(now - timedelta(days=1), 2),
+ nomination(now - timedelta(days=80), 2),
+ nomination(now - timedelta(days=79), 2),
+ ],
+ 1,
+ ),
+ ]
+
+ for (case_num, (nominations, expected)) in enumerate(cases, 1):
+ nomination_dict = dict(nominations)
+
+ with self.subTest(case_num=case_num):
+ self.pool.cache = nomination_dict
+ res = await self.reviewer.get_user_for_review()
+
+ if expected is None:
+ self.assertIsNone(res)
+ else:
+ self.assertEqual(res, nominations[expected][0])
+
+ @patch("bot.exts.recruitment.talentpool._review.MIN_NOMINATION_TIME", timedelta(days=0))
+ async def test_get_user_for_review_order(self):
+ now = datetime.now(timezone.utc)
+
+ # Each case in cases is a list of nominations in the order they should be chosen from first to last
+ cases = [
+ [
+ nomination(now - timedelta(days=10), 3, id=1),
+ nomination(now - timedelta(days=50), 2, id=2),
+ nomination(now - timedelta(days=100), 1, id=3),
+ ],
+ [
+ nomination(now - timedelta(days=100), 2, id=1),
+ nomination(now - timedelta(days=10), 3, id=2),
+ nomination(now - timedelta(days=80), 1, id=3),
+ nomination(now - timedelta(days=79), 1, id=4),
+ nomination(now - timedelta(days=10), 2, id=5),
+ ],
+ [
+ nomination(now - timedelta(days=200), 8, id=1),
+ nomination(now - timedelta(days=359), 4, id=2),
+ nomination(now - timedelta(days=230), 5, id=3),
+ nomination(now - timedelta(days=331), 3, id=4),
+ nomination(now - timedelta(days=113), 5, id=5),
+ nomination(now - timedelta(days=186), 3, id=6),
+ nomination(now - timedelta(days=272), 2, id=7),
+ nomination(now - timedelta(days=30), 4, id=8),
+ nomination(now - timedelta(days=198), 2, id=9),
+ nomination(now - timedelta(days=270), 1, id=10),
+ nomination(now - timedelta(days=140), 1, id=11),
+ nomination(now - timedelta(days=19), 2, id=12),
+ nomination(now - timedelta(days=30), 1, id=13),
+ ]
+ ]
+
+ for case_num, case in enumerate(cases, 1):
+ with self.subTest(case_num=case_num):
+ for i in range(len(case)):
+ with self.subTest(nomination_num=i+1):
+ sub_case = dict(case[i:])
+ self.pool.cache = sub_case
+
+ res = await self.reviewer.get_user_for_review()
+ self.assertEqual(res, case[i][0])