88from sqlmodel import Session , text
99
1010from bot .database .service import get_database , init_database , reset_database
11+ from bot .group_config import GroupConfig , GroupRegistry
1112from bot .services .captcha_recovery import (
1213 handle_captcha_expiration ,
1314 recover_pending_captchas ,
1415)
1516
1617
1718@pytest .fixture
18- def mock_settings ():
19- settings = MagicMock ()
20- settings .group_id = - 1001234567890
21- settings .captcha_timeout_seconds = 300
22- settings .captcha_timeout_timedelta = timedelta (seconds = 300 )
23- return settings
19+ def group_config ():
20+ return GroupConfig (
21+ group_id = - 1001234567890 ,
22+ warning_topic_id = 0 ,
23+ captcha_timeout_seconds = 300 ,
24+ )
25+
26+
27+ @pytest .fixture
28+ def mock_registry (group_config ):
29+ registry = GroupRegistry ()
30+ registry .register (group_config )
31+ return registry
2432
2533
2634@pytest .fixture
@@ -58,7 +66,7 @@ async def test_handle_captcha_expiration_success(
5866
5967 with patch ("bot.services.captcha_recovery.BotInfoCache.get_username" ) as mock_username :
6068 mock_username .return_value = "testbot"
61-
69+
6270 await handle_captcha_expiration (
6371 bot = mock_bot ,
6472 user_id = 12345 ,
@@ -106,7 +114,7 @@ async def test_handle_captcha_expiration_message_edit_fails(
106114
107115 with patch ("bot.services.captcha_recovery.BotInfoCache.get_username" ) as mock_username :
108116 mock_username .return_value = "testbot"
109-
117+
110118 await handle_captcha_expiration (
111119 bot = mock_bot ,
112120 user_id = 12345 ,
@@ -122,35 +130,35 @@ async def test_handle_captcha_expiration_message_edit_fails(
122130
123131class TestRecoverPendingCaptchas :
124132 async def test_recover_pending_captchas_no_records (
125- self , mock_application , mock_settings , temp_db , caplog
133+ self , mock_application , mock_registry , temp_db , caplog
126134 ):
127135 caplog .set_level (logging .INFO )
128- with patch ("bot.services.captcha_recovery.get_settings " , return_value = mock_settings ):
136+ with patch ("bot.services.captcha_recovery.get_group_registry " , return_value = mock_registry ):
129137 await recover_pending_captchas (mock_application )
130138
131139 assert "No pending captcha verifications to recover" in caplog .text
132140 mock_application .job_queue .run_once .assert_not_called ()
133141
134142 async def test_recover_pending_captchas_expired_timeout (
135- self , mock_application , mock_settings , temp_db , caplog
143+ self , mock_application , mock_registry , temp_db , caplog
136144 ):
137145 caplog .set_level (logging .INFO )
138146 db = get_database ()
139-
147+
140148 # Create a record that expired 100 seconds ago
141149 old_time = datetime .now (UTC ) - timedelta (seconds = 400 )
142150 record = db .add_pending_captcha (
143151 12345 , - 1001234567890 , - 1001234567890 , 999 , "Test User"
144152 )
145-
153+
146154 # Manually update created_at to simulate old record
147155 with Session (db ._engine ) as session :
148156 stmt = text ("UPDATE pending_validations SET created_at = :created_at WHERE id = :id" )
149157 session .execute (stmt , {"created_at" : old_time , "id" : record .id })
150158 session .commit ()
151159
152160 with (
153- patch ("bot.services.captcha_recovery.get_settings " , return_value = mock_settings ),
161+ patch ("bot.services.captcha_recovery.get_group_registry " , return_value = mock_registry ),
154162 patch ("bot.services.captcha_recovery.handle_captcha_expiration" ) as mock_expire ,
155163 ):
156164 mock_expire .return_value = AsyncMock ()
@@ -171,32 +179,32 @@ async def test_recover_pending_captchas_expired_timeout(
171179 assert "Captcha recovery complete" in caplog .text
172180
173181 async def test_recover_pending_captchas_reschedule_timeout (
174- self , mock_application , mock_settings , temp_db , caplog
182+ self , mock_application , mock_registry , temp_db , caplog
175183 ):
176184 caplog .set_level (logging .INFO )
177185 db = get_database ()
178-
186+
179187 # Create a record with 150 seconds remaining (150 seconds ago)
180188 recent_time = datetime .now (UTC ) - timedelta (seconds = 150 )
181189 record = db .add_pending_captcha (
182190 12345 , - 1001234567890 , - 1001234567890 , 999 , "Test User"
183191 )
184-
192+
185193 # Manually update created_at
186194 with Session (db ._engine ) as session :
187195 stmt = text ("UPDATE pending_validations SET created_at = :created_at WHERE id = :id" )
188196 session .execute (stmt , {"created_at" : recent_time , "id" : record .id })
189197 session .commit ()
190198
191199 with (
192- patch ("bot.services.captcha_recovery.get_settings " , return_value = mock_settings ),
200+ patch ("bot.services.captcha_recovery.get_group_registry " , return_value = mock_registry ),
193201 patch ("bot.services.captcha_recovery.captcha_timeout_callback" ) as mock_callback ,
194202 ):
195203 await recover_pending_captchas (mock_application )
196204
197205 mock_application .job_queue .run_once .assert_called_once ()
198206 call_args = mock_application .job_queue .run_once .call_args
199-
207+
200208 assert call_args .args [0 ] == mock_callback
201209 assert 149 <= call_args .kwargs ["when" ] <= 151 # Allow 1 second tolerance
202210 assert call_args .kwargs ["name" ] == "captcha_timeout_-1001234567890_12345"
@@ -211,52 +219,52 @@ async def test_recover_pending_captchas_reschedule_timeout(
211219 assert "remaining:" in caplog .text
212220
213221 async def test_recover_pending_captchas_handles_errors (
214- self , mock_application , mock_settings , temp_db , caplog
222+ self , mock_application , mock_registry , temp_db , caplog
215223 ):
216224 caplog .set_level (logging .INFO )
217225 db = get_database ()
218-
226+
219227 # Create a record
220228 record = db .add_pending_captcha (
221229 12345 , - 1001234567890 , - 1001234567890 , 999 , "Test User"
222230 )
223231
224232 with (
225- patch ("bot.services.captcha_recovery.get_settings " , return_value = mock_settings ),
233+ patch ("bot.services.captcha_recovery.get_group_registry " , return_value = mock_registry ),
226234 patch ("bot.services.captcha_recovery.handle_captcha_expiration" ) as mock_expire ,
227235 ):
228236 mock_expire .side_effect = Exception ("Something went wrong" )
229-
237+
230238 # Manually update to make it expired
231239 old_time = datetime .now (UTC ) - timedelta (seconds = 400 )
232240 with Session (db ._engine ) as session :
233241 stmt = text ("UPDATE pending_validations SET created_at = :created_at WHERE id = :id" )
234242 session .execute (stmt , {"created_at" : old_time , "id" : record .id })
235243 session .commit ()
236-
244+
237245 await recover_pending_captchas (mock_application )
238246
239247 assert "Failed to recover captcha for user 12345: Something went wrong" in caplog .text
240248 assert "Captcha recovery complete" in caplog .text
241249
242250 async def test_recover_pending_captchas_multiple_records (
243- self , mock_application , mock_settings , temp_db , caplog
251+ self , mock_application , mock_registry , temp_db , caplog
244252 ):
245253 caplog .set_level (logging .INFO )
246254 db = get_database ()
247-
255+
248256 # Create expired record
249257 old_time = datetime .now (UTC ) - timedelta (seconds = 400 )
250258 record1 = db .add_pending_captcha (
251259 12345 , - 1001234567890 , - 1001234567890 , 999 , "User One"
252260 )
253-
261+
254262 # Create pending record
255263 recent_time = datetime .now (UTC ) - timedelta (seconds = 150 )
256264 record2 = db .add_pending_captcha (
257265 67890 , - 1001234567890 , - 1001234567890 , 888 , "User Two"
258266 )
259-
267+
260268 # Manually update created_at for both
261269 with Session (db ._engine ) as session :
262270 stmt = text ("UPDATE pending_validations SET created_at = :created_at WHERE id = :id" )
@@ -265,7 +273,7 @@ async def test_recover_pending_captchas_multiple_records(
265273 session .commit ()
266274
267275 with (
268- patch ("bot.services.captcha_recovery.get_settings " , return_value = mock_settings ),
276+ patch ("bot.services.captcha_recovery.get_group_registry " , return_value = mock_registry ),
269277 patch ("bot.services.captcha_recovery.handle_captcha_expiration" ) as mock_expire ,
270278 patch ("bot.services.captcha_recovery.captcha_timeout_callback" ),
271279 ):
@@ -275,7 +283,7 @@ async def test_recover_pending_captchas_multiple_records(
275283 # Should expire the first one
276284 mock_expire .assert_called_once ()
277285 assert mock_expire .call_args .kwargs ["user_id" ] == 12345
278-
286+
279287 # Should reschedule the second one
280288 mock_application .job_queue .run_once .assert_called_once ()
281289 call_args = mock_application .job_queue .run_once .call_args
@@ -285,3 +293,29 @@ async def test_recover_pending_captchas_multiple_records(
285293 assert "Expiring captcha for user 12345" in caplog .text
286294 assert "Rescheduling captcha timeout for user 67890" in caplog .text
287295 assert "Captcha recovery complete" in caplog .text
296+
297+ async def test_recover_pending_captchas_skips_unknown_group (
298+ self , mock_application , mock_registry , temp_db , caplog
299+ ):
300+ caplog .set_level (logging .WARNING )
301+ db = get_database ()
302+
303+ # Create a captcha record for a group NOT in the registry
304+ unknown_group_id = - 1009999999999
305+ old_time = datetime .now (UTC ) - timedelta (seconds = 400 )
306+ record = db .add_pending_captcha (
307+ 12345 , unknown_group_id , unknown_group_id , 999 , "Test User"
308+ )
309+
310+ with Session (db ._engine ) as session :
311+ stmt = text ("UPDATE pending_validations SET created_at = :created_at WHERE id = :id" )
312+ session .execute (stmt , {"created_at" : old_time , "id" : record .id })
313+ session .commit ()
314+
315+ with patch ("bot.services.captcha_recovery.get_group_registry" , return_value = mock_registry ):
316+ await recover_pending_captchas (mock_application )
317+
318+ # Should skip - no expiration, no reschedule
319+ mock_application .job_queue .run_once .assert_not_called ()
320+
321+ assert "group no longer in registry" in caplog .text
0 commit comments