@@ -108,32 +108,48 @@ async def call_foo_bar(
108108
109109async def test__botx_method_callback__callback_not_found (
110110 bot_account : BotAccountWithSecret ,
111+ loguru_caplog : pytest .LogCaptureFixture ,
111112) -> None :
112113 # - Arrange -
113- built_bot = Bot (collectors = [HandlerCollector ()], bot_accounts = [bot_account ])
114+ memory_repo = CallbackMemoryRepo (timeout = 0.5 )
115+ built_bot = Bot (
116+ collectors = [HandlerCollector ()],
117+ bot_accounts = [bot_account ],
118+ callback_repo = memory_repo ,
119+ )
114120
115121 # - Act -
116122 async with lifespan_wrapper (built_bot ) as bot :
117- with pytest .raises (BotXMethodCallbackNotFoundError ) as exc :
118- await bot .set_raw_botx_method_result (
119- {
120- "status" : "error" ,
121- "sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ,
122- "reason" : "chat_not_found" ,
123- "errors" : [],
124- "error_data" : {
125- "group_chat_id" : "705df263-6bfd-536a-9d51-13524afaab5c" ,
126- "error_description" : (
127- "Chat with id 705df263-6bfd-536a-9d51-13524afaab5c not found"
128- ),
129- },
123+ await bot .set_raw_botx_method_result (
124+ {
125+ "status" : "error" ,
126+ "sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ,
127+ "reason" : "chat_not_found" ,
128+ "errors" : [],
129+ "error_data" : {
130+ "group_chat_id" : "705df263-6bfd-536a-9d51-13524afaab5c" ,
131+ "error_description" : (
132+ "Chat with id 705df263-6bfd-536a-9d51-13524afaab5c not found"
133+ ),
130134 },
131- verify_request = False ,
132- )
135+ },
136+ verify_request = False ,
137+ )
133138
134139 # - Assert -
135- assert "Callback `21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3` doesn't exist" in str (
136- exc .value ,
140+ assert (
141+ "Callback `21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3` doesn't exist"
142+ in loguru_caplog .text
143+ )
144+ assert memory_repo ._callback_futures .get (
145+ UUID ("21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ),
146+ )
147+
148+ await asyncio .sleep (0.7 )
149+ # Drop callback after timeout
150+ assert (
151+ memory_repo ._callback_futures .get (UUID ("21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ))
152+ is None
137153 )
138154
139155
@@ -303,7 +319,12 @@ async def test__botx_method_callback__callback_received_after_timeout(
303319 },
304320 ),
305321 )
306- built_bot = Bot (collectors = [HandlerCollector ()], bot_accounts = [bot_account ])
322+ memory_repo = CallbackMemoryRepo (timeout = 0.5 )
323+ built_bot = Bot (
324+ collectors = [HandlerCollector ()],
325+ bot_accounts = [bot_account ],
326+ callback_repo = memory_repo ,
327+ )
307328
308329 built_bot .call_foo_bar = types .MethodType (call_foo_bar , built_bot )
309330
@@ -312,26 +333,28 @@ async def test__botx_method_callback__callback_received_after_timeout(
312333 with pytest .raises (CallbackNotReceivedError ) as not_received_exc :
313334 await bot .call_foo_bar (bot_id , baz = 1 , callback_timeout = 0 )
314335
315- with pytest .raises (BotXMethodCallbackNotFoundError ) as not_found_exc :
316- await bot .set_raw_botx_method_result (
317- {
318- "status" : "error" ,
319- "sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ,
320- "reason" : "quux_error" ,
321- "errors" : [],
322- "error_data" : {
323- "group_chat_id" : "705df263-6bfd-536a-9d51-13524afaab5c" ,
324- "error_description" : (
325- "Chat with id 705df263-6bfd-536a-9d51-13524afaab5c not found"
326- ),
327- },
336+ await bot .set_raw_botx_method_result (
337+ {
338+ "status" : "error" ,
339+ "sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ,
340+ "reason" : "quux_error" ,
341+ "errors" : [],
342+ "error_data" : {
343+ "group_chat_id" : "705df263-6bfd-536a-9d51-13524afaab5c" ,
344+ "error_description" : (
345+ "Chat with id 705df263-6bfd-536a-9d51-13524afaab5c not found"
346+ ),
328347 },
329- verify_request = False ,
330- )
348+ },
349+ verify_request = False ,
350+ )
331351
332352 # - Assert -
333353 assert "hasn't been received" in str (not_received_exc .value )
334- assert "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" in str (not_found_exc .value )
354+ assert (
355+ "Callback `21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3` doesn't exist"
356+ in loguru_caplog .text
357+ )
335358 assert endpoint .called
336359
337360
@@ -611,6 +634,62 @@ async def test__botx_method_callback__bot_wait_callback_after_its_receiving(
611634 assert endpoint .called
612635
613636
637+ async def test__botx_method_callback__callback_received_before_its_expecting (
638+ respx_mock : MockRouter ,
639+ httpx_client : httpx .AsyncClient ,
640+ host : str ,
641+ bot_id : UUID ,
642+ bot_account : BotAccountWithSecret ,
643+ ) -> None :
644+ """https://github.com/ExpressApp/pybotx/issues/482."""
645+ # - Arrange -
646+ endpoint = respx_mock .post (
647+ f"https://{ host } /foo/bar" ,
648+ json = {"baz" : 1 },
649+ headers = {"Content-Type" : "application/json" },
650+ ).mock (
651+ return_value = httpx .Response (
652+ HTTPStatus .ACCEPTED ,
653+ json = {
654+ "status" : "ok" ,
655+ "result" : {"sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" },
656+ },
657+ ),
658+ )
659+ built_bot = Bot (
660+ collectors = [HandlerCollector ()],
661+ bot_accounts = [bot_account ],
662+ httpx_client = httpx_client ,
663+ callback_repo = CallbackMemoryRepo (timeout = 0.5 ),
664+ )
665+
666+ built_bot .call_foo_bar = types .MethodType (call_foo_bar , built_bot )
667+
668+ # - Act -
669+ async with lifespan_wrapper (built_bot ) as bot :
670+ await bot .set_raw_botx_method_result (
671+ {
672+ "sync_id" : "21a9ec9e-f21f-4406-ac44-1a78d2ccf9e3" ,
673+ "status" : "ok" ,
674+ "result" : {},
675+ },
676+ verify_request = False ,
677+ )
678+ foo_bar = await bot .call_foo_bar (bot_id , baz = 1 , wait_callback = False )
679+
680+ callback = await bot .wait_botx_method_callback (foo_bar )
681+
682+ await asyncio .sleep (1 )
683+
684+ # - Assert -
685+ assert callback == BotAPIMethodSuccessfulCallback (
686+ sync_id = foo_bar ,
687+ status = "ok" ,
688+ result = {},
689+ )
690+ assert endpoint .called
691+
692+
614693async def test__botx_method_callback__bot_dont_wait_received_callback (
615694 respx_mock : MockRouter ,
616695 httpx_client : httpx .AsyncClient ,
0 commit comments