@@ -162,3 +162,131 @@ def begin(self):
162162 second_session = await core ._get_async_session ()
163163 assert first_session is second_session
164164 assert counting_engine .begin_calls == 1
165+
166+
167+ @pytest .mark .sql
168+ @pytest .mark .asyncio
169+ async def test_async_sql_set_entry_skips_when_too_large (async_sql_engine ):
170+ """aset_entry returns False without storing when _should_store rejects the value."""
171+ core = _SQLCore (hash_func = None , sql_engine = async_sql_engine , entry_size_limit = 1 )
172+ core .set_func (lambda x : x )
173+
174+ result = await core .aset_entry ("too_large_key" , "a value that exceeds the tiny limit" )
175+ assert result is False
176+
177+ _ , entry = await core .aget_entry_by_key ("too_large_key" )
178+ assert entry is None
179+
180+
181+ @pytest .mark .sql
182+ @pytest .mark .asyncio
183+ async def test_async_sql_clear_being_calculated (async_sql_engine ):
184+ """aclear_being_calculated resets all processing flags for this function."""
185+ core = _SQLCore (hash_func = None , sql_engine = async_sql_engine )
186+ core .set_func (lambda x : x )
187+
188+ await core .amark_entry_being_calculated ("calc_key_1" )
189+ await core .amark_entry_being_calculated ("calc_key_2" )
190+
191+ _ , e1 = await core .aget_entry_by_key ("calc_key_1" )
192+ _ , e2 = await core .aget_entry_by_key ("calc_key_2" )
193+ assert e1 ._processing is True
194+ assert e2 ._processing is True
195+
196+ await core .aclear_being_calculated ()
197+
198+ _ , e1_after = await core .aget_entry_by_key ("calc_key_1" )
199+ _ , e2_after = await core .aget_entry_by_key ("calc_key_2" )
200+ assert e1_after ._processing is False
201+ assert e2_after ._processing is False
202+
203+ await core .aclear_cache ()
204+
205+
206+ @pytest .mark .sql
207+ @pytest .mark .asyncio
208+ async def test_async_sql_delete_stale_entries (async_sql_engine ):
209+ """adelete_stale_entries removes old entries and keeps recent ones."""
210+ core = _SQLCore (hash_func = None , sql_engine = async_sql_engine )
211+ core .set_func (lambda x : x )
212+
213+ await core .aset_entry ("keep_key" , "recent_value" )
214+
215+ # Nothing older than 1 hour exists, so keep_key should survive.
216+ await core .adelete_stale_entries (timedelta (hours = 1 ))
217+ _ , entry = await core .aget_entry_by_key ("keep_key" )
218+ assert entry is not None
219+
220+ # Delete everything (stale_after=0 s means all entries are older than threshold).
221+ await core .adelete_stale_entries (timedelta (seconds = 0 ))
222+ _ , entry_gone = await core .aget_entry_by_key ("keep_key" )
223+ assert entry_gone is None
224+
225+ await core .aclear_cache ()
226+
227+
228+ @pytest .mark .sql
229+ @pytest .mark .asyncio
230+ async def test_async_sql_set_entry_executes_conflict_statement (async_sql_engine , monkeypatch ):
231+ """aset_entry executes the upsert statement when on_conflict_do_update is available."""
232+ from sqlalchemy .ext .asyncio import AsyncSession
233+
234+ sentinel_stmt = object ()
235+ seen_stmt : dict = {"value" : None }
236+
237+ class FakeInsert :
238+ def values (self , ** kwargs ):
239+ return self
240+
241+ def on_conflict_do_update (self , ** kwargs ):
242+ return sentinel_stmt
243+
244+ def fake_insert (_table ):
245+ return FakeInsert ()
246+
247+ async def fake_execute (self , stmt , * args , ** kwargs ):
248+ seen_stmt ["value" ] = stmt
249+
250+ class DummyResult :
251+ def scalar_one_or_none (self ):
252+ return None
253+
254+ return DummyResult ()
255+
256+ monkeypatch .setitem (_SQLCore .aset_entry .__globals__ , "insert" , fake_insert )
257+ monkeypatch .setattr (AsyncSession , "execute" , fake_execute )
258+
259+ core = _SQLCore (hash_func = None , sql_engine = async_sql_engine )
260+ core .set_func (lambda x : x )
261+ assert await core .aset_entry ("upsert_key" , 123 ) is True
262+ assert seen_stmt ["value" ] is sentinel_stmt
263+
264+
265+ @pytest .mark .sql
266+ @pytest .mark .asyncio
267+ async def test_async_sql_set_entry_fallback_without_on_conflict (async_sql_engine ):
268+ """aset_entry uses insert/update fallback when on_conflict_do_update is absent."""
269+ from unittest .mock import patch
270+
271+ core = _SQLCore (hash_func = None , sql_engine = async_sql_engine )
272+ core .set_func (lambda x : x )
273+ await core .aclear_cache ()
274+
275+ _real_hasattr = hasattr
276+
277+ def _no_on_conflict (obj , name ):
278+ if name == "on_conflict_do_update" :
279+ return False
280+ return _real_hasattr (obj , name )
281+
282+ with patch ("builtins.hasattr" , _no_on_conflict ):
283+ # First call: no existing row → session.add (the else branch).
284+ assert await core .aset_entry ("fb_key" , "val1" ) is True
285+ # Second call: row already exists → update (the if-row branch).
286+ assert await core .aset_entry ("fb_key" , "val2" ) is True
287+
288+ _ , entry = await core .aget_entry_by_key ("fb_key" )
289+ assert entry is not None
290+ assert entry .value == "val2"
291+
292+ await core .aclear_cache ()
0 commit comments