mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
* Preserve middleware context across scene goto transitions (#1687) * Add After.goto coverage for scene middleware context (#1687)
This commit is contained in:
parent
e37eddbe8c
commit
73710acb4c
4 changed files with 113 additions and 0 deletions
1
CHANGES/1687.bugfix.rst
Normal file
1
CHANGES/1687.bugfix.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed scene transitions to preserve middleware-injected data when moving between scenes via ``SceneWizard.goto``.
|
||||
|
|
@ -259,6 +259,7 @@ class SceneHandlerWrapper:
|
|||
)
|
||||
raise SceneException(msg) from None
|
||||
event_update: Update = kwargs["event_update"]
|
||||
scenes.data = {**scenes.data, **kwargs}
|
||||
scene = self.scene(
|
||||
wizard=SceneWizard(
|
||||
scene_config=self.scene.__scene_config__,
|
||||
|
|
@ -712,6 +713,9 @@ class ScenesManager:
|
|||
:param kwargs: Additional keyword arguments to pass to the scene's wizard.enter() method.
|
||||
:return: None
|
||||
"""
|
||||
if kwargs:
|
||||
self.data = {**self.data, **kwargs}
|
||||
|
||||
if _check_active:
|
||||
active_scene = await self._get_active_scene()
|
||||
if active_scene is not None:
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ class TestSceneHandlerWrapper:
|
|||
|
||||
state_mock = AsyncMock(spec=FSMContext)
|
||||
scenes_mock = AsyncMock(spec=ScenesManager)
|
||||
scenes_mock.data = {}
|
||||
event_update_mock = Update(
|
||||
update_id=42,
|
||||
message=Message(
|
||||
|
|
@ -282,6 +283,7 @@ class TestSceneHandlerWrapper:
|
|||
|
||||
state_mock = AsyncMock(spec=FSMContext)
|
||||
scenes_mock = AsyncMock(spec=ScenesManager)
|
||||
scenes_mock.data = {}
|
||||
event_update_mock = Update(
|
||||
update_id=42,
|
||||
message=Message(
|
||||
|
|
|
|||
106
tests/test_issues/test_1687_scene_goto_loses_middleware_data.py
Normal file
106
tests/test_issues/test_1687_scene_goto_loses_middleware_data.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
from collections.abc import Awaitable, Callable
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from aiogram import BaseMiddleware, Dispatcher
|
||||
from aiogram.enums import ChatType
|
||||
from aiogram.filters import CommandStart
|
||||
from aiogram.fsm.scene import After, Scene, SceneRegistry, on
|
||||
from aiogram.types import Chat, Message, TelegramObject, Update, User
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestContextMiddleware(BaseMiddleware):
|
||||
async def __call__(
|
||||
self,
|
||||
handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
|
||||
event: TelegramObject,
|
||||
data: dict[str, Any],
|
||||
) -> Any:
|
||||
data["test_context"] = "context from middleware"
|
||||
return await handler(event, data)
|
||||
|
||||
|
||||
class TargetScene(Scene, state="target"):
|
||||
entered_with_context: str | None = None
|
||||
|
||||
@on.message.enter()
|
||||
async def on_enter(self, message: Message, test_context: str) -> None:
|
||||
type(self).entered_with_context = test_context
|
||||
|
||||
|
||||
class StartScene(Scene, state="start"):
|
||||
@on.message.enter()
|
||||
async def on_start(self, message: Message) -> None:
|
||||
await self.wizard.goto(TargetScene)
|
||||
|
||||
|
||||
class StartSceneWithAfter(Scene, state="start_with_after"):
|
||||
@on.message(after=After.goto(TargetScene))
|
||||
async def goto_target_with_after(self, message: Message) -> None:
|
||||
pass
|
||||
|
||||
|
||||
async def test_scene_goto_preserves_message_middleware_data(bot: MockedBot) -> None:
|
||||
dp = Dispatcher()
|
||||
registry = SceneRegistry(dp)
|
||||
registry.add(StartScene, TargetScene)
|
||||
dp.message.register(StartScene.as_handler(), CommandStart())
|
||||
dp.message.middleware(TestContextMiddleware())
|
||||
|
||||
TargetScene.entered_with_context = None
|
||||
|
||||
update = Update(
|
||||
update_id=1,
|
||||
message=Message(
|
||||
message_id=1,
|
||||
date=datetime.now(),
|
||||
chat=Chat(id=42, type=ChatType.PRIVATE),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
text="/start",
|
||||
),
|
||||
)
|
||||
|
||||
await dp.feed_update(bot, update)
|
||||
|
||||
assert TargetScene.entered_with_context == "context from middleware"
|
||||
|
||||
|
||||
async def test_scene_after_goto_preserves_message_middleware_data(bot: MockedBot) -> None:
|
||||
dp = Dispatcher()
|
||||
registry = SceneRegistry(dp)
|
||||
registry.add(StartSceneWithAfter, TargetScene)
|
||||
dp.message.register(StartSceneWithAfter.as_handler(), CommandStart())
|
||||
dp.message.middleware(TestContextMiddleware())
|
||||
|
||||
TargetScene.entered_with_context = None
|
||||
|
||||
await dp.feed_update(
|
||||
bot,
|
||||
Update(
|
||||
update_id=1,
|
||||
message=Message(
|
||||
message_id=1,
|
||||
date=datetime.now(),
|
||||
chat=Chat(id=42, type=ChatType.PRIVATE),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
text="/start",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await dp.feed_update(
|
||||
bot,
|
||||
Update(
|
||||
update_id=2,
|
||||
message=Message(
|
||||
message_id=2,
|
||||
date=datetime.now(),
|
||||
chat=Chat(id=42, type=ChatType.PRIVATE),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
text="go",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assert TargetScene.entered_with_context == "context from middleware"
|
||||
Loading…
Add table
Add a link
Reference in a new issue