@@ -482,6 +482,71 @@ async def _go():
482482 asyncio .run (_go ())
483483
484484
485+
486+ def test_close ():
487+ """Test WebDBer.close() behavior with and without clear=True."""
488+ async def _go ():
489+ backend = FakeStorageBackend ()
490+
491+ # --- 1. close(clear=False) preserves items and dirty flags ---
492+ dber , _ = await _open_fake_dber (
493+ name = "close-test" ,
494+ stores = ["vals." ],
495+ clear = True ,
496+ backend = backend ,
497+ )
498+ sdb = dber .env .open_db (key = b"vals." )
499+
500+ # Add some data
501+ assert dber .putVal (sdb , b"a.1" , b"wow" ) is True
502+ assert dber .putVal (sdb , b"a.2" , b"wee" ) is True
503+ assert dber .putVal (sdb , b"b.1" , b"woo" ) is True
504+
505+ dber .close (clear = False )
506+
507+ # Stores removed from dber
508+ assert dber ._stores == {}
509+ assert dber .stores == []
510+ assert sdb .dirty == True
511+
512+ # Original SubDb values unchanged
513+ assert list (dber .getTopItemIter (sdb )) == [
514+ (b"a.1" , b"wow" ), (b"a.2" , b"wee" ), (b"b.1" , b"woo" ),
515+ ]
516+
517+ # 2. close(clear=True) clears items
518+ dber2 , _ = await _open_fake_dber (
519+ name = "close-test-2" ,
520+ stores = ["vals." ],
521+ clear = True ,
522+ backend = backend ,
523+ )
524+ sdb2 = dber2 .env .open_db (key = b"vals." )
525+
526+ # Add some data
527+ assert dber2 .putVal (sdb2 , b"a.1" , b"wow" ) is True
528+ assert dber2 .putVal (sdb2 , b"a.2" , b"wee" ) is True
529+ assert dber2 .putVal (sdb2 , b"b.1" , b"woo" ) is True
530+
531+ dber2 .close (clear = True )
532+
533+ # Stores removed from dber2
534+ assert dber2 ._stores == {}
535+ assert dber2 .stores == []
536+ assert sdb .dirty == True
537+
538+ # Items cleared
539+ assert list (dber2 .getTopItemIter (sdb2 )) == []
540+
541+
542+ # 3. close() is idempotent
543+ dber2 .close ()
544+ assert dber2 ._stores == {}
545+ assert dber2 .stores == []
546+
547+ asyncio .run (_go ())
548+
549+
485550@pytest .mark .skip (reason = "Requires hio>=0.7.20 Doist.ado() for async task integration" )
486551def test_flush_with_hio_ado ():
487552 """Test flush completion under hio Doist.ado() scheduling."""
@@ -3758,6 +3823,161 @@ async def _go():
37583823 # on=0 entries should still exist
37593824 assert dber .cntOnIoSet (db , key1 , on = 0 ) == 4
37603825
3826+
3827+ # more tests
3828+ assert dber .remOnAllIoSet (db ) == True
3829+ assert [item for item in dber .getOnIoSetItemIter (db , b'A' )] == []
3830+ # test pinOnIoSetVals
3831+ vals0 = ["z" , "m" , "x" , "a" ]
3832+ vals1 = ["w" , "n" , "y" , "d" ]
3833+ keyA = b'A'
3834+ keyB = b'B'
3835+ assert dber .putOnIoSetVals (db , keyA ) == False
3836+ assert dber .pinOnIoSetVals (db , key = '' ,vals = vals0 ) == False # empty keyA no-op
3837+ assert dber .pinOnIoSetVals (db , key = None ,vals = vals0 ) == False # None keyA no-op
3838+ assert dber .pinOnIoSetVals (db , key = None ,vals = '' ) == False # empty vals no-op
3839+ assert dber .pinOnIoSetVals (db , key = None ,vals = None ) == False # None vals no-op
3840+
3841+ assert dber .putOnIoSetVals (db , keyA , vals = vals0 ) == True
3842+ assert list (dber .getOnIoSetItemIter (db , keyA )) == [(keyA , 0 , "z" ),
3843+ (keyA , 0 , "m" ),
3844+ (keyA , 0 , "x" ),
3845+ (keyA , 0 , "a" )]
3846+ assert dber .pinOnIoSetVals (db , keyA , vals = vals0 ) == True # pinning same vals still returns True
3847+ assert list (dber .getOnIoSetItemIter (db , keyA )) == [(keyA , 0 , "z" ),
3848+ (keyA , 0 , "m" ),
3849+ (keyA , 0 , "x" ),
3850+ (keyA , 0 , "a" )]
3851+ assert dber .pinOnIoSetVals (db , keyB , vals = vals1 ) == True # pinning vals1 to a different keyB doesn't affect keyA's values
3852+ assert list (dber .getOnIoSetItemIter (db , keyA )) == [(keyA , 0 , "z" ),
3853+ (keyA , 0 , "m" ),
3854+ (keyA , 0 , "x" ),
3855+ (keyA , 0 , "a" )]
3856+ assert list (dber .getOnIoSetItemIter (db , keyB )) == [(keyB , 0 , "w" ), # vals0 replaced by vals1
3857+ (keyB , 0 , "n" ),
3858+ (keyB , 0 , "y" ),
3859+ (keyB , 0 , "d" )]
3860+
3861+ assert dber .pinOnIoSetVals (db , keyA , vals = vals1 ) == True # default on=0 so vals0 replaced by vals1
3862+ assert list (dber .getOnIoSetItemIter (db , keyA )) == [(keyA , 0 , "w" ), # vals0 replaced by vals1
3863+ (keyA , 0 , "n" ),
3864+ (keyA , 0 , "y" ),
3865+ (keyA , 0 , "d" )]
3866+
3867+ assert dber .pinOnIoSetVals (db , keyA , on = 1 , vals = vals1 ) == True # pinning on=1 vals1 should not affect on=0 vals1 so should add on=1 vals1
3868+ assert list (dber .getOnIoSetItemIter (db , keyA )) == [(keyA , 0 , "w" ),
3869+ (keyA , 0 , "n" ),
3870+ (keyA , 0 , "y" ),
3871+ (keyA , 0 , "d" )]
3872+ assert list (dber .getOnIoSetItemIter (db , keyA , on = 1 )) == [(keyA , 1 , "w" ),
3873+ (keyA , 1 , "n" ),
3874+ (keyA , 1 , "y" ),
3875+ (keyA , 1 , "d" )]
3876+
3877+ assert dber .pinOnIoSetVals (db , keyA , on = 4 , vals = vals1 ) == True # gaps between on values is allowed
3878+ assert list (dber .getOnIoSetItemIter (db , keyA , on = 4 )) == [(keyA , 4 , "w" ),
3879+ (keyA , 4 , "n" ),
3880+ (keyA , 4 , "y" ),
3881+ (keyA , 4 , "d" )]
3882+
3883+ assert list (dber .getOnAllIoSetItemIter (db , keyA )) == [
3884+ (keyA , 0 , "w" ),
3885+ (keyA , 0 , "n" ),
3886+ (keyA , 0 , "y" ),
3887+ (keyA , 0 , "d" ),
3888+ (keyA , 1 , "w" ),
3889+ (keyA , 1 , "n" ),
3890+ (keyA , 1 , "y" ),
3891+ (keyA , 1 , "d" ),
3892+ (keyA , 4 , "w" ),
3893+ (keyA , 4 , "n" ),
3894+ (keyA , 4 , "y" ),
3895+ (keyA , 4 , "d" )]
3896+
3897+ assert dber .remOnAllIoSet (db ) == True
3898+ assert [item for item in dber .getOnIoSetItemIter (db , b'A' )] == []
3899+
3900+
3901+ # test getOnTopIoSetItemIter
3902+ vals0 = ["z" , "m" , "x" , "a" ]
3903+ vals1 = ["w" , "n" , "y" , "d" ]
3904+ keyA = b'A'
3905+ keyB = b'B'
3906+
3907+ assert list (dber .getOnTopIoSetItemIter (db )) == [] # empty db
3908+ assert dber .putOnIoSetVals (db , keyA , vals = vals0 ) == True
3909+ assert list (dber .getOnTopIoSetItemIter (db , keyA )) == [(keyA , 0 , "z" ),
3910+ (keyA , 0 , "m" ),
3911+ (keyA , 0 , "x" ),
3912+ (keyA , 0 , "a" )]
3913+ assert dber .putOnIoSetVals (db , keyB , vals = vals1 ) == True
3914+ assert list (dber .getOnTopIoSetItemIter (db , keyB )) == [(keyB , 0 , "w" ),
3915+ (keyB , 0 , "n" ),
3916+ (keyB , 0 , "y" ),
3917+ (keyB , 0 , "d" )]
3918+
3919+ itemsA = list (dber .getOnTopIoSetItemIter (db , keyA ))
3920+ assert itemsA == [
3921+ (keyA , 0 , "z" ),
3922+ (keyA , 0 , "m" ),
3923+ (keyA , 0 , "x" ),
3924+ (keyA , 0 , "a" ),
3925+ ]
3926+
3927+ itemsB = list (dber .getOnTopIoSetItemIter (db , keyB ))
3928+ assert itemsB == [
3929+ (keyB , 0 , "w" ),
3930+ (keyB , 0 , "n" ),
3931+ (keyB , 0 , "y" ),
3932+ (keyB , 0 , "d" ),
3933+ ]
3934+
3935+ itemsAll = list (dber .getOnTopIoSetItemIter (db , top = b"" ))
3936+ assert itemsAll == [
3937+ (keyA , 0 , "z" ), (keyA , 0 , "m" ), (keyA , 0 , "x" ), (keyA , 0 , "a" ),
3938+ (keyB , 0 , "w" ), (keyB , 0 , "n" ), (keyB , 0 , "y" ), (keyB , 0 , "d" ),
3939+ ]
3940+
3941+ # Add additional branches under A*
3942+ keyA1 = b"A1"
3943+ keyA2 = b"A2"
3944+
3945+ valsA1 = ["p" , "q" ]
3946+ valsA2 = ["r" ]
3947+
3948+ assert dber .putOnIoSetVals (db , keyA1 , vals = valsA1 ) is True
3949+ assert dber .putOnIoSetVals (db , keyA2 , vals = valsA2 ) is True
3950+
3951+ # top=b"A" should match A, A1, A2 (because all start with b"A")
3952+ itemsAstar = list (dber .getOnTopIoSetItemIter (db , top = b"A" ))
3953+
3954+ assert itemsAstar == [
3955+ # A branch
3956+ (keyA , 0 , "z" ), (keyA , 0 , "m" ), (keyA , 0 , "x" ), (keyA , 0 , "a" ),
3957+ # A1 branch
3958+ (keyA1 , 0 , "p" ), (keyA1 , 0 , "q" ),
3959+ # A2 branch
3960+ (keyA2 , 0 , "r" ),
3961+ ]
3962+
3963+ # top=b"A1" should match ONLY A1 branch
3964+ itemsA1 = list (dber .getOnTopIoSetItemIter (db , top = b"A1" ))
3965+ assert itemsA1 == [
3966+ (keyA1 , 0 , "p" ),
3967+ (keyA1 , 0 , "q" ),
3968+ ]
3969+
3970+ # top=b"A2" should match ONLY A2 branch
3971+ itemsA2 = list (dber .getOnTopIoSetItemIter (db , top = b"A2" ))
3972+ assert itemsA2 == [
3973+ (keyA2 , 0 , "r" ),
3974+ ]
3975+ assert list (dber .getOnTopIoSetItemIter (db )) == [
3976+ (keyA , 0 , "z" ), (keyA , 0 , "m" ), (keyA , 0 , "x" ), (keyA , 0 , "a" ),
3977+ (keyA1 , 0 , "p" ), (keyA1 , 0 , "q" ), (keyA2 , 0 , "r" ), (keyB , 0 , "w" ),
3978+ (keyB , 0 , "n" ), (keyB , 0 , "y" ), (keyB , 0 , "d" ),
3979+ ] # empty top returns whole db, keyA1 and keyA2 are in correct order under keyA and before keyB
3980+
37613981 asyncio .run (_go ())
37623982
37633983
0 commit comments