async def test_handle_api_error(self, log_mock): """Should `ctx.send` on HTTP error codes, `log.debug|warning` depends on code.""" test_cases = ({ "error": ResponseCodeError(AsyncMock(status=400)), "log_level": "debug" }, { "error": ResponseCodeError(AsyncMock(status=404)), "log_level": "debug" }, { "error": ResponseCodeError(AsyncMock(status=550)), "log_level": "warning" }, { "error": ResponseCodeError(AsyncMock(status=1000)), "log_level": "warning" }) for case in test_cases: with self.subTest(error=case["error"], log_level=case["log_level"]): self.ctx.reset_mock() log_mock.reset_mock() await self.cog.handle_api_error(self.ctx, case["error"]) self.ctx.send.assert_awaited_once() if case["log_level"] == "warning": log_mock.warning.assert_called_once() else: log_mock.debug.assert_called_once()
async def test_user_not_found_none_post_infraction(self, post_user_mock): """Should abort and return `None` when a new user fails to be posted.""" self.bot.api_client.post.side_effect = ResponseCodeError(MagicMock(status=400), {"user": "******"}) actual = await utils.post_infraction(self.ctx, self.user, "mute", "Test reason") self.assertIsNone(actual) post_user_mock.assert_awaited_once_with(self.ctx, self.user)
async def test_handle_check_failure_errors(self): """Should await `ctx.send` when error is check failure.""" test_cases = ({ "error": errors.BotMissingPermissions(MagicMock()), "call_ctx_send": True }, { "error": errors.BotMissingRole(MagicMock()), "call_ctx_send": True }, { "error": errors.BotMissingAnyRole(MagicMock()), "call_ctx_send": True }, { "error": errors.NoPrivateMessage(), "call_ctx_send": True }, { "error": InWhitelistCheckFailure(1234), "call_ctx_send": True }, { "error": ResponseCodeError(MagicMock()), "call_ctx_send": False }) for case in test_cases: with self.subTest(error=case["error"], call_ctx_send=case["call_ctx_send"]): self.ctx.reset_mock() await self.cog.handle_check_failure(self.ctx, case["error"]) if case["call_ctx_send"]: self.ctx.send.assert_awaited_once() else: self.ctx.send.assert_not_awaited()
async def test_error_handler_command_invoke_error(self): """Should call `handle_api_error` or `handle_unexpected_error` depending on original error.""" cog = ErrorHandler(self.bot) cog.handle_api_error = AsyncMock() cog.handle_unexpected_error = AsyncMock() test_cases = ( { "args": (self.ctx, errors.CommandInvokeError(ResponseCodeError(AsyncMock()))), "expect_mock_call": cog.handle_api_error }, { "args": (self.ctx, errors.CommandInvokeError(TypeError)), "expect_mock_call": cog.handle_unexpected_error }, { "args": (self.ctx, errors.CommandInvokeError(LockedResourceError("abc", "test"))), "expect_mock_call": "send" }, { "args": (self.ctx, errors.CommandInvokeError(InvalidInfractedUser(self.ctx.author))), "expect_mock_call": "send" } ) for case in test_cases: with self.subTest(args=case["args"], expect_mock_call=case["expect_mock_call"]): self.ctx.send.reset_mock() self.assertIsNone(await cog.on_command_error(*case["args"])) if case["expect_mock_call"] == "send": self.ctx.send.assert_awaited_once() else: case["expect_mock_call"].assert_awaited_once_with( self.ctx, case["args"][1].original )
async def test_unknown_error_post_infraction(self): """Should send an error message to chat when a non-400 error occurs.""" self.ctx.bot.api_client.post.side_effect = ResponseCodeError(AsyncMock(), AsyncMock()) self.ctx.bot.api_client.post.side_effect.status = 500 actual = await utils.post_infraction(self.ctx, self.user, "ban", "Test reason") self.assertIsNone(actual) self.assertTrue("500" in self.ctx.send.call_args[0][0])
async def test_first_fail_second_success_user_post_infraction(self, post_user_mock): """Should post the user if they don't exist, POST infraction again, and return the response if successful.""" payload = { "actor": self.ctx.author.id, "hidden": False, "reason": "Test reason", "type": "mute", "user": self.user.id, "active": True } self.bot.api_client.post.side_effect = [ResponseCodeError(MagicMock(status=400), {"user": "******"}), "foo"] actual = await utils.post_infraction(self.ctx, self.user, "mute", "Test reason") self.assertEqual(actual, "foo") self.bot.api_client.post.assert_has_awaits([call("bot/infractions", json=payload)] * 2) post_user_mock.assert_awaited_once_with(self.ctx, self.user)
async def test_sync_message_edited(self): """The message should be edited if one was sent, even if the sync has an API error.""" subtests = ( (None, None, False), (helpers.MockMessage(), None, True), (helpers.MockMessage(), ResponseCodeError(mock.MagicMock()), True), ) for message, side_effect, should_edit in subtests: with self.subTest(message=message, side_effect=side_effect, should_edit=should_edit): TestSyncer._sync.side_effect = side_effect ctx = helpers.MockContext() ctx.send.return_value = message await TestSyncer.sync(self.guild, ctx) if should_edit: message.edit.assert_called_once() self.assertIn("content", message.edit.call_args[1])
async def test_error_handler_conversion_error(self): """Should call `handle_api_error` or `handle_unexpected_error` depending on original error.""" cog = ErrorHandler(self.bot) cog.handle_api_error = AsyncMock() cog.handle_unexpected_error = AsyncMock() cases = ( { "error": errors.ConversionError(AsyncMock(), ResponseCodeError(AsyncMock())), "mock_function_to_call": cog.handle_api_error }, { "error": errors.ConversionError(AsyncMock(), TypeError), "mock_function_to_call": cog.handle_unexpected_error } ) for case in cases: with self.subTest(**case): self.assertIsNone(await 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_sync_message_edited(self): """The message should be edited if one was sent, even if the sync has an API error.""" subtests = ( (None, None, False), (helpers.MockMessage(), None, True), (helpers.MockMessage(), ResponseCodeError(mock.MagicMock()), True), ) for message, side_effect, should_edit in subtests: with self.subTest(message=message, side_effect=side_effect, should_edit=should_edit): self.syncer._sync.side_effect = side_effect self.syncer._get_confirmation_result = mock.AsyncMock( return_value=(True, message)) guild = helpers.MockGuild() await self.syncer.sync(guild) if should_edit: message.edit.assert_called_once() self.assertIn("content", message.edit.call_args[1])
async def test_post_user(self): """Should POST a new user and return the response if successful or otherwise send an error message.""" user = MockUser(discriminator=5678, id=1234, name="Test user") not_user = MagicMock(discriminator=3333, id=5678, name="Wrong user") test_cases = [ { "user": user, "post_result": "bar", "raise_error": None, "payload": { "discriminator": 5678, "id": self.user.id, "in_guild": False, "name": "Test user", "roles": [] } }, { "user": self.member, "post_result": "foo", "raise_error": ResponseCodeError(MagicMock(status=400), "foo"), "payload": { "discriminator": 0, "id": self.member.id, "in_guild": False, "name": "Name unknown", "roles": [] } }, { "user": not_user, "post_result": "bar", "raise_error": None, "payload": { "discriminator": not_user.discriminator, "id": not_user.id, "in_guild": False, "name": not_user.name, "roles": [] } } ] for case in test_cases: user = case["user"] post_result = case["post_result"] raise_error = case["raise_error"] payload = case["payload"] with self.subTest(user=user, post_result=post_result, raise_error=raise_error, payload=payload): self.bot.api_client.post.reset_mock(side_effect=True) self.ctx.bot.api_client.post.return_value = post_result self.ctx.bot.api_client.post.side_effect = raise_error result = await utils.post_user(self.ctx, user) if raise_error: self.assertIsNone(result) self.ctx.send.assert_awaited_once() self.assertIn(str(raise_error.status), self.ctx.send.call_args[0][0]) else: self.assertEqual(result, post_result) self.bot.api_client.post.assert_awaited_once_with("bot/users", json=payload)
def response_error(status: int) -> ResponseCodeError: """Fixture to return a ResponseCodeError with the given status code.""" response = mock.MagicMock() response.status = status return ResponseCodeError(response)