@@ -150,6 +150,49 @@ func Test_CacheOnDeleteCallbackCalled(t *testing.T) {
150150 assert .Equal (t , atomic .LoadInt32 (& onDeleteFnCalled ), 1 )
151151}
152152
153+ func Test_CacheSingleItemSizeAccounting (t * testing.T ) {
154+ cache := New (Configure [* SizedItem ]().GetsPerPromote (1 ))
155+ defer cache .Stop ()
156+
157+ // Add a single item to the cache
158+ cache .Set ("only" , & SizedItem {1 , 10 }, time .Minute )
159+ cache .SyncUpdates ()
160+ assert .Equal (t , cache .GetSize (), 10 )
161+
162+ // Get the item to trigger promotion
163+ // With a single item, next==nil && prev==nil, so it may be
164+ // incorrectly treated as a "new" item and size double-counted
165+ cache .Get ("only" )
166+ cache .Get ("only" )
167+ cache .SyncUpdates ()
168+ assert .Equal (t , cache .GetSize (), 10 ) // Should still be 10, not 20 or 30
169+ }
170+
171+ func Test_CacheSingleItemDelete (t * testing.T ) {
172+ onDeleteCalled := int32 (0 )
173+ onDeleteFn := func (item * Item [string ]) {
174+ atomic .AddInt32 (& onDeleteCalled , 1 )
175+ }
176+
177+ cache := New (Configure [string ]().OnDelete (onDeleteFn ))
178+ defer cache .Stop ()
179+
180+ // Add a single item
181+ cache .Set ("only" , "value" , time .Minute )
182+ cache .SyncUpdates ()
183+ assert .Equal (t , cache .GetSize (), 1 )
184+
185+ // Delete the only item in the cache
186+ // With a single item, next==nil && prev==nil, so the delete
187+ // path may incorrectly skip size decrement and onDelete callback
188+ cache .Delete ("only" )
189+ cache .SyncUpdates ()
190+
191+ assert .Equal (t , cache .Get ("only" ), nil )
192+ assert .Equal (t , atomic .LoadInt32 (& onDeleteCalled ), 1 ) // onDelete should be called
193+ assert .Equal (t , cache .GetSize (), 0 ) // Size should be 0
194+ }
195+
153196func Test_CacheFetchesExpiredItems (t * testing.T ) {
154197 cache := New (Configure [string ]())
155198 defer cache .Stop ()
0 commit comments