From db015cd647e2a8522a04d9bce30b858a441fe7e1 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+ailitonia@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:57:07 +0800 Subject: [PATCH 1/2] =?UTF-8?q?:bug:=20Fix:=20=E4=BF=AE=E5=A4=8D=20bot.cal?= =?UTF-8?q?l=5Fapi=20=E5=9C=A8=20CalledAPI=20hooks=20=E4=B8=AD=E6=8A=9B?= =?UTF-8?q?=E5=87=BA=20MockApiException=20=E5=90=8E=E4=BB=8D=E7=84=B6?= =?UTF-8?q?=E4=BC=9A=E6=8A=9B=E5=87=BA=E5=8E=9F=E5=BC=82=E5=B8=B8=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20(#2373)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot/internal/adapter/bot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nonebot/internal/adapter/bot.py b/nonebot/internal/adapter/bot.py index 8961b89f3fb6..2742891c6098 100644 --- a/nonebot/internal/adapter/bot.py +++ b/nonebot/internal/adapter/bot.py @@ -76,6 +76,7 @@ async def call_api(self, api: str, **data: Any) -> Any: result: Any = None skip_calling_api: bool = False exception: Optional[Exception] = None + ignore_exception: bool = False if coros := [hook(self, api, data) for hook in self._calling_api_hook]: try: @@ -106,6 +107,7 @@ async def call_api(self, api: str, **data: Any) -> Any: logger.debug("Running CalledAPI hooks...") await asyncio.gather(*coros) except MockApiException as e: + ignore_exception = True result = e.result logger.debug( f"Calling API {api} result is mocked. Return {result} instead." @@ -116,7 +118,7 @@ async def call_api(self, api: str, **data: Any) -> Any: "Running cancelled!" ) - if exception: + if not ignore_exception and exception: raise exception return result From 7e6035f432cc72633664b41cb6699525cda35a98 Mon Sep 17 00:00:00 2001 From: Ju4tCode <42488585+yanyongyu@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:53:31 +0800 Subject: [PATCH 2/2] :white_check_mark: add tests --- nonebot/internal/adapter/bot.py | 7 +- tests/test_adapters/test_bot.py | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 tests/test_adapters/test_bot.py diff --git a/nonebot/internal/adapter/bot.py b/nonebot/internal/adapter/bot.py index 2742891c6098..3f827b18884a 100644 --- a/nonebot/internal/adapter/bot.py +++ b/nonebot/internal/adapter/bot.py @@ -76,7 +76,6 @@ async def call_api(self, api: str, **data: Any) -> Any: result: Any = None skip_calling_api: bool = False exception: Optional[Exception] = None - ignore_exception: bool = False if coros := [hook(self, api, data) for hook in self._calling_api_hook]: try: @@ -107,8 +106,10 @@ async def call_api(self, api: str, **data: Any) -> Any: logger.debug("Running CalledAPI hooks...") await asyncio.gather(*coros) except MockApiException as e: - ignore_exception = True + # mock api result result = e.result + # ignore exception + exception = None logger.debug( f"Calling API {api} result is mocked. Return {result} instead." ) @@ -118,7 +119,7 @@ async def call_api(self, api: str, **data: Any) -> Any: "Running cancelled!" ) - if not ignore_exception and exception: + if exception: raise exception return result diff --git a/tests/test_adapters/test_bot.py b/tests/test_adapters/test_bot.py new file mode 100644 index 000000000000..765714705c64 --- /dev/null +++ b/tests/test_adapters/test_bot.py @@ -0,0 +1,152 @@ +from typing import Any, Dict, Optional + +import pytest +from nonebug import App + +from nonebot.adapters import Bot +from nonebot.exception import MockApiException + + +@pytest.mark.asyncio +async def test_bot_call_api(app: App): + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, True) + result = await bot.call_api("test") + + assert result is True + + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, exception=RuntimeError("test")) + with pytest.raises(RuntimeError, match="test"): + await bot.call_api("test") + + +@pytest.mark.asyncio +async def test_bot_calling_api_hook_simple(app: App): + runned: bool = False + + async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]): + nonlocal runned + runned = True + + hooks = set() + + with pytest.MonkeyPatch.context() as m: + m.setattr(Bot, "_calling_api_hook", hooks) + + Bot.on_calling_api(calling_api_hook) + + assert hooks == {calling_api_hook} + + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, True) + result = await bot.call_api("test") + + assert runned is True + assert result is True + + +@pytest.mark.asyncio +async def test_bot_calling_api_hook_mock(app: App): + runned: bool = False + + async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]): + nonlocal runned + runned = True + + raise MockApiException(False) + + hooks = set() + + with pytest.MonkeyPatch.context() as m: + m.setattr(Bot, "_calling_api_hook", hooks) + + Bot.on_calling_api(calling_api_hook) + + assert hooks == {calling_api_hook} + + async with app.test_api() as ctx: + bot = ctx.create_bot() + result = await bot.call_api("test") + + assert runned is True + assert result is False + + +@pytest.mark.asyncio +async def test_bot_called_api_hook_simple(app: App): + runned: bool = False + + async def called_api_hook( + bot: Bot, + exception: Optional[Exception], + api: str, + data: Dict[str, Any], + result: Any, + ): + nonlocal runned + runned = True + + hooks = set() + + with pytest.MonkeyPatch.context() as m: + m.setattr(Bot, "_called_api_hook", hooks) + + Bot.on_called_api(called_api_hook) + + assert hooks == {called_api_hook} + + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, True) + result = await bot.call_api("test") + + assert runned is True + assert result is True + + +@pytest.mark.asyncio +async def test_bot_called_api_hook_mock(app: App): + runned: bool = False + + async def called_api_hook( + bot: Bot, + exception: Optional[Exception], + api: str, + data: Dict[str, Any], + result: Any, + ): + nonlocal runned + runned = True + + raise MockApiException(False) + + hooks = set() + + with pytest.MonkeyPatch.context() as m: + m.setattr(Bot, "_called_api_hook", hooks) + + Bot.on_called_api(called_api_hook) + + assert hooks == {called_api_hook} + + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, True) + result = await bot.call_api("test") + + assert runned is True + assert result is False + + runned = False + + async with app.test_api() as ctx: + bot = ctx.create_bot() + ctx.should_call_api("test", {}, exception=RuntimeError("test")) + result = await bot.call_api("test") + + assert runned is True + assert result is False