diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/_autospec.py | 2 | ||||
-rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 36 | ||||
-rw-r--r-- | tests/bot/exts/backend/test_logging.py | 5 | ||||
-rw-r--r-- | tests/bot/exts/filtering/test_settings_entries.py | 8 | ||||
-rw-r--r-- | tests/bot/exts/moderation/infraction/test_utils.py | 10 | ||||
-rw-r--r-- | tests/bot/exts/moderation/test_incidents.py | 22 | ||||
-rw-r--r-- | tests/bot/exts/moderation/test_modlog.py | 4 | ||||
-rw-r--r-- | tests/bot/exts/moderation/test_silence.py | 9 | ||||
-rw-r--r-- | tests/bot/exts/test_cogs.py | 3 | ||||
-rw-r--r-- | tests/bot/exts/utils/snekbox/test_snekbox.py | 45 | ||||
-rw-r--r-- | tests/bot/test_pagination.py | 46 | ||||
-rw-r--r-- | tests/bot/utils/test_services.py | 90 | ||||
-rw-r--r-- | tests/helpers.py | 10 | ||||
-rw-r--r-- | tests/test_helpers.py | 1 |
14 files changed, 83 insertions, 208 deletions
diff --git a/tests/_autospec.py b/tests/_autospec.py index 6f990a580..a9cd59dc0 100644 --- a/tests/_autospec.py +++ b/tests/_autospec.py @@ -51,7 +51,7 @@ def autospec(target, *attributes: str, pass_mocks: bool = True, **patch_kwargs) # Import the target if it's a string. # This is to support both object and string targets like patch.multiple. - if type(target) is str: + if isinstance(target, str): target = pkgutil.resolve_name(target) def decorator(func): diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index 0ba2fcf11..9670d42a0 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import AsyncMock, MagicMock, call, patch +from unittest.mock import AsyncMock, MagicMock, Mock, call, patch from discord.ext.commands import errors from pydis_core.site_api import ResponseCodeError @@ -54,12 +54,15 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase): self.ctx.reset_mock() self.cog.try_silence.reset_mock(return_value=True) self.cog.try_get_tag.reset_mock() + self.ctx.invoked_from_error_handler = False self.cog.try_silence.return_value = case["try_silence_return"] self.ctx.channel.id = 1234 self.assertIsNone(await self.cog.on_command_error(self.ctx, error)) + self.assertTrue(self.ctx.invoked_from_error_handler) + if case["try_silence_return"]: self.cog.try_get_tag.assert_not_awaited() self.cog.try_silence.assert_awaited_once() @@ -203,13 +206,6 @@ class TrySilenceTests(unittest.IsolatedAsyncioTestCase): 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): - """Should set `Context.invoked_from_error_handler` to `True`.""" - self.ctx.invoked_with = "foo" - await self.cog.try_silence(self.ctx) - self.assertTrue(hasattr(self.ctx, "invoked_from_error_handler")) - self.assertTrue(self.ctx.invoked_from_error_handler) - async def test_try_silence_get_command(self): """Should call `get_command` with `silence`.""" self.ctx.invoked_with = "foo" @@ -342,26 +338,12 @@ class TryGetTagTests(unittest.IsolatedAsyncioTestCase): await self.cog.try_get_tag(self.ctx) self.bot.get_cog.assert_called_once_with("Tags") - async def test_try_get_tag_invoked_from_error_handler(self): - """`self.ctx` should have `invoked_from_error_handler` `True`.""" - self.ctx.invoked_from_error_handler = False - await self.cog.try_get_tag(self.ctx) - self.assertTrue(self.ctx.invoked_from_error_handler) - async def test_try_get_tag_no_permissions(self): """Test how to handle checks failing.""" self.bot.can_run = AsyncMock(return_value=False) self.ctx.invoked_with = "foo" self.assertIsNone(await self.cog.try_get_tag(self.ctx)) - async def test_try_get_tag_command_error(self): - """Should call `on_command_error` when `CommandError` raised.""" - err = errors.CommandError() - self.bot.can_run = AsyncMock(side_effect=err) - self.cog.on_command_error = AsyncMock() - self.assertIsNone(await self.cog.try_get_tag(self.ctx)) - self.cog.on_command_error.assert_awaited_once_with(self.ctx, err) - async def test_dont_call_suggestion_tag_sent(self): """Should never call command suggestion if tag is already sent.""" self.ctx.message = MagicMock(content="foo") @@ -375,7 +357,7 @@ class TryGetTagTests(unittest.IsolatedAsyncioTestCase): async def test_dont_call_suggestion_if_user_mod(self): """Should not call command suggestion if user is a mod.""" self.ctx.invoked_with = "foo" - self.ctx.invoke = AsyncMock(return_value=False) + self.tag.get_command_ctx = AsyncMock(return_value=False) self.ctx.author.roles = [MockRole(id=1234)] self.cog.send_command_suggestion = AsyncMock() @@ -521,6 +503,10 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase): self.ctx.reset_mock() log_mock.reset_mock() push_scope_mock.reset_mock() + scope_mock = Mock() + + # Mock `with push_scope_mock() as scope:` + push_scope_mock.return_value.__enter__.return_value = scope_mock self.ctx.guild = case await self.cog.handle_unexpected_error(self.ctx, errors.CommandError()) @@ -544,8 +530,8 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase): ) set_extra_calls.append(call("jump_to", url)) - push_scope_mock.set_tag.has_calls(set_tag_calls) - push_scope_mock.set_extra.has_calls(set_extra_calls) + scope_mock.set_tag.assert_has_calls(set_tag_calls) + scope_mock.set_extra.assert_has_calls(set_extra_calls) class ErrorHandlerSetupTests(unittest.IsolatedAsyncioTestCase): diff --git a/tests/bot/exts/backend/test_logging.py b/tests/bot/exts/backend/test_logging.py index 466f207d9..3a41220ad 100644 --- a/tests/bot/exts/backend/test_logging.py +++ b/tests/bot/exts/backend/test_logging.py @@ -3,7 +3,7 @@ from unittest.mock import patch from bot import constants from bot.exts.backend.logging import Logging -from tests.helpers import MockBot, MockTextChannel +from tests.helpers import MockBot, MockTextChannel, no_create_task class LoggingTests(unittest.IsolatedAsyncioTestCase): @@ -11,7 +11,8 @@ class LoggingTests(unittest.IsolatedAsyncioTestCase): def setUp(self): self.bot = MockBot() - self.cog = Logging(self.bot) + with no_create_task(): + self.cog = Logging(self.bot) self.dev_log = MockTextChannel(id=1234, name="dev-log") @patch("bot.exts.backend.logging.DEBUG_MODE", False) diff --git a/tests/bot/exts/filtering/test_settings_entries.py b/tests/bot/exts/filtering/test_settings_entries.py index 3ae0b5ab5..f12b2caa5 100644 --- a/tests/bot/exts/filtering/test_settings_entries.py +++ b/tests/bot/exts/filtering/test_settings_entries.py @@ -2,7 +2,9 @@ import unittest from bot.exts.filtering._filter_context import Event, FilterContext from bot.exts.filtering._settings_types.actions.infraction_and_notification import ( - Infraction, InfractionAndNotification, InfractionDuration + Infraction, + InfractionAndNotification, + InfractionDuration, ) from bot.exts.filtering._settings_types.validations.bypass_roles import RoleBypass from bot.exts.filtering._settings_types.validations.channel_scope import ChannelScope @@ -173,7 +175,7 @@ class FilterTests(unittest.TestCase): result = infraction1.union(infraction2) self.assertDictEqual( - result.dict(), + result.model_dump(), { "infraction_type": Infraction.TIMEOUT, "infraction_reason": "there", @@ -206,7 +208,7 @@ class FilterTests(unittest.TestCase): result = infraction1.union(infraction2) self.assertDictEqual( - result.dict(), + result.model_dump(), { "infraction_type": Infraction.BAN, "infraction_reason": "", diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py index 25337673e..e2a7bad9f 100644 --- a/tests/bot/exts/moderation/infraction/test_utils.py +++ b/tests/bot/exts/moderation/infraction/test_utils.py @@ -142,7 +142,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): ), "expected_output": Embed( title=utils.INFRACTION_TITLE, - description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format( + description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format( type="Ban", expires="2020-02-26 09:20 (23 hours and 59 minutes)", reason="No reason provided." @@ -160,7 +160,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): "args": (dict(id=0, type="warning", reason="Test reason.", expires_at=None), self.user), "expected_output": Embed( title=utils.INFRACTION_TITLE, - description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format( + description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format( type="Warning", expires="N/A", reason="Test reason." @@ -180,7 +180,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): "args": (dict(id=0, type="note", reason=None, expires_at=None), self.user), "expected_output": Embed( title=utils.INFRACTION_TITLE, - description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format( + description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format( type="Note", expires="N/A", reason="No reason provided." @@ -201,7 +201,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): ), "expected_output": Embed( title=utils.INFRACTION_TITLE, - description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format( + description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format( type="Mute", expires="2020-02-26 09:20 (23 hours and 59 minutes)", reason="Test" @@ -219,7 +219,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase): "args": (dict(id=0, type="mute", reason="foo bar" * 4000, expires_at=None), self.user), "expected_output": Embed( title=utils.INFRACTION_TITLE, - description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format( + description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format( type="Mute", expires="N/A", reason="foo bar" * 4000 diff --git a/tests/bot/exts/moderation/test_incidents.py b/tests/bot/exts/moderation/test_incidents.py index bb337aeba..444bb1142 100644 --- a/tests/bot/exts/moderation/test_incidents.py +++ b/tests/bot/exts/moderation/test_incidents.py @@ -16,8 +16,16 @@ from bot.utils.messages import format_user from bot.utils.time import TimestampFormats, discord_timestamp from tests.base import RedisTestCase from tests.helpers import ( - MockAsyncWebhook, MockAttachment, MockBot, MockMember, MockMessage, MockReaction, MockRole, MockTextChannel, - MockUser + MockAsyncWebhook, + MockAttachment, + MockBot, + MockMember, + MockMessage, + MockReaction, + MockRole, + MockTextChannel, + MockUser, + no_create_task, ) CURRENT_TIME = datetime.datetime(2022, 1, 1, tzinfo=datetime.UTC) @@ -306,7 +314,8 @@ class TestIncidents(RedisTestCase): Note that this will not schedule `crawl_incidents` in the background, as everything is being mocked. The `crawl_task` attribute will end up being None. """ - self.cog_instance = incidents.Incidents(MockBot()) + with no_create_task(): + self.cog_instance = incidents.Incidents(MockBot()) @patch("asyncio.sleep", AsyncMock()) # Prevent the coro from sleeping to speed up the test @@ -458,7 +467,8 @@ class TestMakeConfirmationTask(TestIncidents): If this function begins to fail, first check that `created_check` is being retrieved correctly. It should be the function that is built locally in the tested method. """ - self.cog_instance.make_confirmation_task(MockMessage(id=123)) + with no_create_task(): + self.cog_instance.make_confirmation_task(MockMessage(id=123)) self.cog_instance.bot.wait_for.assert_called_once() created_check = self.cog_instance.bot.wait_for.call_args.kwargs["check"] @@ -547,7 +557,7 @@ class TestProcessEvent(TestIncidents): exception should it propagate out of `process_event`. This is so that we can then manually fail the test with a more informative message than just the plain traceback. """ - mock_task = AsyncMock(side_effect=asyncio.TimeoutError()) + mock_task = AsyncMock(side_effect=TimeoutError()) try: with patch("bot.exts.moderation.incidents.Incidents.make_confirmation_task", mock_task): @@ -556,7 +566,7 @@ class TestProcessEvent(TestIncidents): incident=MockMessage(id=123, created_at=CURRENT_TIME), member=MockMember(roles=[MockRole(id=1)]) ) - except asyncio.TimeoutError: + except TimeoutError: self.fail("TimeoutError was not handled gracefully, and propagated out of `process_event`!") diff --git a/tests/bot/exts/moderation/test_modlog.py b/tests/bot/exts/moderation/test_modlog.py index 79e04837d..f2b02bd1b 100644 --- a/tests/bot/exts/moderation/test_modlog.py +++ b/tests/bot/exts/moderation/test_modlog.py @@ -3,6 +3,7 @@ import unittest import discord from bot.exts.moderation.modlog import ModLog +from bot.utils.modlog import send_log_message from tests.helpers import MockBot, MockTextChannel @@ -17,7 +18,8 @@ class ModLogTests(unittest.IsolatedAsyncioTestCase): async def test_log_entry_description_truncation(self): """Test that embed description for ModLog entry is truncated.""" self.bot.get_channel.return_value = self.channel - await self.cog.send_log_message( + await send_log_message( + self.bot, icon_url="foo", colour=discord.Colour.blue(), title="bar", diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py index ec0b3bf43..a7f239d7f 100644 --- a/tests/bot/exts/moderation/test_silence.py +++ b/tests/bot/exts/moderation/test_silence.py @@ -10,7 +10,14 @@ from bot.constants import Channels, Guild, MODERATION_ROLES, Roles from bot.exts.moderation import silence from tests.base import RedisTestCase from tests.helpers import ( - MockBot, MockContext, MockGuild, MockMember, MockRole, MockTextChannel, MockVoiceChannel, autospec + MockBot, + MockContext, + MockGuild, + MockMember, + MockRole, + MockTextChannel, + MockVoiceChannel, + autospec, ) diff --git a/tests/bot/exts/test_cogs.py b/tests/bot/exts/test_cogs.py index 99bc87120..0c117b3e6 100644 --- a/tests/bot/exts/test_cogs.py +++ b/tests/bot/exts/test_cogs.py @@ -62,8 +62,7 @@ class CommandNameTests(unittest.TestCase): """Yield all commands for all cogs in all extensions.""" for module in self.walk_modules(): for cog in self.walk_cogs(module): - for cmd in self.walk_commands(cog): - yield cmd + yield from self.walk_commands(cog) def test_names_dont_shadow(self): """Names and aliases of commands should be unique.""" diff --git a/tests/bot/exts/utils/snekbox/test_snekbox.py b/tests/bot/exts/utils/snekbox/test_snekbox.py index fa28aade8..8ee0f46ff 100644 --- a/tests/bot/exts/utils/snekbox/test_snekbox.py +++ b/tests/bot/exts/utils/snekbox/test_snekbox.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, Mock, call, create_autospec, pat from discord import AllowedMentions from discord.ext import commands +from pydis_core.utils.paste_service import MAX_PASTE_SIZE from bot import constants from bot.errors import LockedResourceError @@ -54,21 +55,15 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ) resp.json.assert_awaited_once() + @patch( + "bot.exts.utils.snekbox._cog.paste_service._lexers_supported_by_pastebin", + {"https://paste.pythondiscord.com": ["text"]}, + ) async def test_upload_output_reject_too_long(self): """Reject output longer than MAX_PASTE_LENGTH.""" - result = await self.cog.upload_output("-" * (snekbox._cog.MAX_PASTE_LENGTH + 1)) + result = await self.cog.upload_output("-" * (MAX_PASTE_SIZE + 1)) self.assertEqual(result, "too long to upload") - @patch("bot.exts.utils.snekbox._cog.send_to_paste_service") - async def test_upload_output(self, mock_paste_util): - """Upload the eval output to the URLs.paste_service.format(key="documents") endpoint.""" - await self.cog.upload_output("Test output.") - mock_paste_util.assert_called_once_with( - "Test output.", - extension="txt", - max_length=snekbox._cog.MAX_PASTE_LENGTH - ) - async def test_codeblock_converter(self): ctx = MockContext() cases = ( @@ -108,9 +103,9 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): def test_eval_result_message(self): """EvalResult.get_message(), should return message.""" cases = ( - ("ERROR", None, ("Your 3.11 eval job has failed", "ERROR", "")), - ("", 128 + snekbox._eval.SIGKILL, ("Your 3.11 eval job timed out or ran out of memory", "", "")), - ("", 255, ("Your 3.11 eval job has failed", "A fatal NsJail error occurred", "")) + ("ERROR", None, ("Your 3.12 eval job has failed", "ERROR", "")), + ("", 128 + snekbox._eval.SIGKILL, ("Your 3.12 eval job timed out or ran out of memory", "", "")), + ("", 255, ("Your 3.12 eval job has failed", "A fatal NsJail error occurred", "")) ) for stdout, returncode, expected in cases: exp_msg, exp_err, exp_files_err = expected @@ -182,8 +177,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): mock_signals.return_value.name = "SIGTEST" result = EvalResult(stdout="", returncode=127) self.assertEqual( - result.get_message(EvalJob([], version="3.11")), - "Your 3.11 eval job has completed with return code 127 (SIGTEST)" + result.get_message(EvalJob([], version="3.12")), + "Your 3.12 eval job has completed with return code 127 (SIGTEST)" ) def test_eval_result_status_emoji(self): @@ -257,7 +252,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): self.cog.send_job = AsyncMock(return_value=response) self.cog.continue_job = AsyncMock(return_value=None) - await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.11", code=["MyAwesomeCode"]) + await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"]) job = EvalJob.from_code("MyAwesomeCode") self.cog.send_job.assert_called_once_with(ctx, job) self.cog.continue_job.assert_called_once_with(ctx, response, "eval") @@ -271,7 +266,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): self.cog.continue_job = AsyncMock() self.cog.continue_job.side_effect = (EvalJob.from_code("MyAwesomeFormattedCode"), None) - await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.11", code=["MyAwesomeCode"]) + await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"]) expected_job = EvalJob.from_code("MyAwesomeFormattedCode") self.cog.send_job.assert_called_with(ctx, expected_job) @@ -316,7 +311,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :warning: Your 3.11 eval job has completed " + "@LemonLemonishBeard#0042 :warning: Your 3.12 eval job has completed " "with return code 0.\n\n```\n[No output]\n```" ) allowed_mentions = ctx.send.call_args.kwargs["allowed_mentions"] @@ -342,13 +337,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, [])) self.bot.get_cog.return_value = mocked_filter_cog - job = EvalJob.from_code("MyAwesomeCode").as_version("3.11") + job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") await self.cog.send_job(ctx, job), ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :white_check_mark: Your 3.11 eval job " + "@LemonLemonishBeard#0042 :white_check_mark: Your 3.12 eval job " "has completed with return code 0." "\n\n```\nWay too long beard\n```\nFull output: lookatmybeard.com" ) @@ -371,13 +366,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, [])) self.bot.get_cog.return_value = mocked_filter_cog - job = EvalJob.from_code("MyAwesomeCode").as_version("3.11") + job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") await self.cog.send_job(ctx, job), ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :x: Your 3.11 eval job has completed with return code 127." + "@LemonLemonishBeard#0042 :x: Your 3.12 eval job has completed with return code 127." "\n\n```\nERROR\n```" ) @@ -399,13 +394,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, [".disallowed"])) self.bot.get_cog.return_value = mocked_filter_cog - job = EvalJob.from_code("MyAwesomeCode").as_version("3.11") + job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") await self.cog.send_job(ctx, job), ctx.send.assert_called_once() res = ctx.send.call_args.args[0] self.assertTrue( - res.startswith("@user#7700 :white_check_mark: Your 3.11 eval job has completed with return code 0.") + res.startswith("@user#7700 :white_check_mark: Your 3.12 eval job has completed with return code 0.") ) self.assertIn("Files with disallowed extensions can't be uploaded: **.disallowed**", res) diff --git a/tests/bot/test_pagination.py b/tests/bot/test_pagination.py deleted file mode 100644 index cf23f1948..000000000 --- a/tests/bot/test_pagination.py +++ /dev/null @@ -1,46 +0,0 @@ -from unittest import TestCase - -from bot import pagination - - -class LinePaginatorTests(TestCase): - """Tests functionality of the `LinePaginator`.""" - - def setUp(self): - """Create a paginator for the test method.""" - self.paginator = pagination.LinePaginator(prefix="", suffix="", max_size=30, - scale_to_size=50) - - def test_add_line_works_on_small_lines(self): - """`add_line` should allow small lines to be added.""" - self.paginator.add_line("x" * (self.paginator.max_size - 3)) - # Note that the page isn't added to _pages until it's full. - self.assertEqual(len(self.paginator._pages), 0) - - def test_add_line_works_on_long_lines(self): - """After additional lines after `max_size` is exceeded should go on the next page.""" - self.paginator.add_line("x" * self.paginator.max_size) - self.assertEqual(len(self.paginator._pages), 0) - - # Any additional lines should start a new page after `max_size` is exceeded. - self.paginator.add_line("x") - self.assertEqual(len(self.paginator._pages), 1) - - def test_add_line_continuation(self): - """When `scale_to_size` is exceeded, remaining words should be split onto the next page.""" - self.paginator.add_line("zyz " * (self.paginator.scale_to_size//4 + 1)) - self.assertEqual(len(self.paginator._pages), 1) - - def test_add_line_no_continuation(self): - """If adding a new line to an existing page would exceed `max_size`, it should start a new - page rather than using continuation. - """ - self.paginator.add_line("z" * (self.paginator.max_size - 3)) - self.paginator.add_line("z") - self.assertEqual(len(self.paginator._pages), 1) - - def test_add_line_truncates_very_long_words(self): - """`add_line` should truncate if a single long word exceeds `scale_to_size`.""" - self.paginator.add_line("x" * (self.paginator.scale_to_size + 1)) - # Note: item at index 1 is the truncated line, index 0 is prefix - self.assertEqual(self.paginator._current_page[1], "x" * self.paginator.scale_to_size) diff --git a/tests/bot/utils/test_services.py b/tests/bot/utils/test_services.py deleted file mode 100644 index 3c9e037ce..000000000 --- a/tests/bot/utils/test_services.py +++ /dev/null @@ -1,90 +0,0 @@ -import logging -import unittest -from unittest.mock import AsyncMock, MagicMock, Mock, patch - -from aiohttp import ClientConnectorError - -from bot.utils.services import ( - FAILED_REQUEST_ATTEMPTS, MAX_PASTE_LENGTH, PasteTooLongError, PasteUploadError, send_to_paste_service -) -from tests.helpers import MockBot - - -class PasteTests(unittest.IsolatedAsyncioTestCase): - def setUp(self) -> None: - patcher = patch("bot.instance", new=MockBot()) - self.bot = patcher.start() - self.addCleanup(patcher.stop) - - @patch("bot.utils.services.URLs.paste_service", "https://paste_service.com/{key}") - async def test_url_and_sent_contents(self): - """Correct url was used and post was called with expected data.""" - response = MagicMock( - json=AsyncMock(return_value={"key": ""}) - ) - self.bot.http_session.post.return_value.__aenter__.return_value = response - self.bot.http_session.post.reset_mock() - await send_to_paste_service("Content") - self.bot.http_session.post.assert_called_once_with("https://paste_service.com/documents", data="Content") - - @patch("bot.utils.services.URLs.paste_service", "https://paste_service.com/{key}") - async def test_paste_returns_correct_url_on_success(self): - """Url with specified extension is returned on successful requests.""" - key = "paste_key" - test_cases = ( - (f"https://paste_service.com/{key}.txt?noredirect", "txt"), - (f"https://paste_service.com/{key}.py", "py"), - (f"https://paste_service.com/{key}?noredirect", ""), - ) - response = MagicMock( - json=AsyncMock(return_value={"key": key}) - ) - self.bot.http_session.post.return_value.__aenter__.return_value = response - - for expected_output, extension in test_cases: - with self.subTest(msg=f"Send contents with extension {extension!r}"): - self.assertEqual( - await send_to_paste_service("", extension=extension), - expected_output - ) - - async def test_request_repeated_on_json_errors(self): - """Json with error message and invalid json are handled as errors and requests repeated.""" - test_cases = ({"message": "error"}, {"unexpected_key": None}, {}) - self.bot.http_session.post.return_value.__aenter__.return_value = response = MagicMock() - self.bot.http_session.post.reset_mock() - - for error_json in test_cases: - with self.subTest(error_json=error_json): - response.json = AsyncMock(return_value=error_json) - with self.assertRaises(PasteUploadError): - await send_to_paste_service("") - self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS) - - self.bot.http_session.post.reset_mock() - - async def test_request_repeated_on_connection_errors(self): - """Requests are repeated in the case of connection errors.""" - self.bot.http_session.post = MagicMock(side_effect=ClientConnectorError(Mock(), Mock())) - with self.assertRaises(PasteUploadError): - await send_to_paste_service("") - self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS) - - async def test_general_error_handled_and_request_repeated(self): - """All `Exception`s are handled, logged and request repeated.""" - self.bot.http_session.post = MagicMock(side_effect=Exception) - with self.assertRaises(PasteUploadError): - await send_to_paste_service("") - self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS) - self.assertLogs("bot.utils", logging.ERROR) - - async def test_raises_error_on_too_long_input(self): - """Ensure PasteTooLongError is raised if `contents` is longer than `MAX_PASTE_LENGTH`.""" - contents = "a" * (MAX_PASTE_LENGTH + 1) - with self.assertRaises(PasteTooLongError): - await send_to_paste_service(contents) - - async def test_raises_on_too_large_max_length(self): - """Ensure ValueError is raised if `max_length` passed is greater than `MAX_PASTE_LENGTH`.""" - with self.assertRaises(ValueError): - await send_to_paste_service("Hello World!", max_length=MAX_PASTE_LENGTH + 1) diff --git a/tests/helpers.py b/tests/helpers.py index 26ac42697..580848c25 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -6,6 +6,7 @@ import logging import unittest.mock from asyncio import AbstractEventLoop from collections.abc import Iterable +from contextlib import contextmanager from functools import cached_property import discord @@ -664,3 +665,12 @@ class MockAsyncWebhook(CustomMockMixin, unittest.mock.MagicMock): """ spec_set = webhook_instance additional_spec_asyncs = ("send", "edit", "delete", "execute") + +@contextmanager +def no_create_task(): + def side_effect(coro, *_, **__): + coro.close() + + with unittest.mock.patch("pydis_core.utils.scheduling.create_task") as create_task: + create_task.side_effect = side_effect + yield diff --git a/tests/test_helpers.py b/tests/test_helpers.py index fa7d0eb44..3d4cb09e7 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -316,7 +316,6 @@ class MockObjectTests(unittest.TestCase): class MyMock(helpers.CustomMockMixin, unittest.mock.MagicMock): child_mock_type = unittest.mock.MagicMock - pass mock = MyMock() unittest.mock.seal(mock) |