WIP: tests
diff --git a/tests/integration/test_sqlite.py b/tests/integration/test_sqlite.py
index 51b174e..9dc069c 100644
--- a/tests/integration/test_sqlite.py
+++ b/tests/integration/test_sqlite.py
@@ -5,6 +5,7 @@
 from os.path import join
 from tempfile import NamedTemporaryFile, gettempdir
 from threading import Thread
+from time import sleep
 from unittest.mock import patch
 
 import pytest
@@ -14,6 +15,7 @@
 from requests_cache.backends.sqlite import MEMORY_URI
 from requests_cache.models import CachedResponse
 from requests_cache.policy import utcnow
+from requests_cache.serializers import utf8_serializer
 from tests.conftest import skip_pypy
 from tests.integration.base_cache_test import BaseCacheTest
 from tests.integration.base_storage_test import CACHE_NAME, BaseStorageTest
@@ -178,20 +180,6 @@
                 pass
             assert mock_connection.execute.call_count == 11
 
-    def test_write_retry__other_errors(self):
-        """Errors other than 'OperationalError: database is locked' should not be retried"""
-        cache = self.init_cache()
-
-        error_1 = sqlite3.OperationalError('no more rows available')
-        with patch.object(cache, '_write', side_effect=error_1):
-            with pytest.raises(sqlite3.OperationalError):
-                cache['key_1'] = 'value_1'
-
-        error_2 = sqlite3.DatabaseError('hard drive is on fire')
-        with patch.object(cache, '_write', side_effect=error_2):
-            with pytest.raises(sqlite3.DatabaseError):
-                cache['key_1'] = 'value_1'
-
     @skip_pypy
     @pytest.mark.parametrize('limit', [None, 50])
     def test_sorted__by_size(self, limit):
@@ -313,9 +301,200 @@
 
 class TestLRUSQLiteDict(TestSQLiteDict):
     storage_class = LRUSQLiteDict
-    init_kwargs = {'use_temp': True}
-    # TODO: tests for LRUSQLiteDict; see TestLRUFileDict + TestLRUDict in test_filesystem.py
-    #   for examples, but adapt to differences in LRUSQLiteDict
+    init_kwargs = {'use_temp': True, 'max_cache_bytes': 10 * 1024 * 1024}
+
+    def test_get_set_with_lru_tracking(self):
+        """Test basic get/set operations with LRU access time tracking."""
+        cache = self.init_cache()
+        cache['key1'] = 'value1'
+        cache['key2'] = 'value2'
+        cache['key3'] = 'value3'
+
+        assert cache['key1'] == 'value1'
+        assert cache['key2'] == 'value2'
+        assert cache['key3'] == 'value3'
+
+        # Check that LRU metadata is stored
+        with cache.connection() as con:
+            rows = con.execute(
+                f'SELECT key, access_time, size FROM {cache.table_name} ORDER BY key'
+            ).fetchall()
+            assert len(rows) == 3
+            for row in rows:
+                assert row[1] is not None  # access_time should be set
+                assert row[2] > 0  # size should be positive
+
+    def test_access_time_update(self):
+        """Test that access times are updated on reads."""
+        cache = self.init_cache()
+        cache['key1'] = 'value1'
+        cache['key2'] = 'value2'
+
+        # Get initial access times
+        with cache.connection() as con:
+            initial_times = {
+                row[0]: row[1]
+                for row in con.execute(
+                    f'SELECT key, access_time FROM {cache.table_name}'
+                ).fetchall()
+            }
+
+        sleep(0.001)  # Ensure time difference
+        _ = cache['key1']  # This should update access_time for key1
+
+        with cache.connection() as con:
+            updated_times = {
+                row[0]: row[1]
+                for row in con.execute(
+                    f'SELECT key, access_time FROM {cache.table_name}'
+                ).fetchall()
+            }
+
+        assert updated_times['key1'] > initial_times['key1']
+        assert updated_times['key2'] == initial_times['key2']
+
+    def test_eviction_on_size_limit(self):
+        """Test that items are evicted when cache size limit is reached."""
+        cache = self.init_cache(max_cache_bytes=40000, serializer=utf8_serializer)
+        cache['key1'] = 'x' * 2000
+        sleep(0.001)
+        cache['key2'] = 'y' * 2000
+        sleep(0.001)
+        cache['key3'] = 'z' * 2000
+
+        # key1 should be evicted (least recently used)
+        assert 'key1' not in cache
+        assert 'key2' in cache
+        assert 'key3' in cache
+
+    def test_do_not_store_items_too_big(self):
+        """Test that items larger than max_cache_bytes are not stored."""
+        cache = self.init_cache(max_cache_bytes=25000, serializer=utf8_serializer)
+        large_value = 'x' * 26000
+
+        cache['small_key'] = 'small_value'
+        cache['large_key'] = large_value
+
+        assert 'small_key' in cache
+        assert 'large_key' not in cache
+        assert cache['small_key'] == 'small_value'
+
+    def test_lru_eviction_order(self):
+        """Test that eviction follows LRU order."""
+        cache = self.init_cache(max_cache_bytes=58000, serializer=utf8_serializer)
+        cache['key1'] = 'x' * 2000
+        sleep(0.001)
+        cache['key2'] = 'x' * 2000
+        sleep(0.001)
+        cache['key3'] = 'x' * 2000
+        sleep(0.001)
+
+        # Make key1 the most recently used
+        _ = cache['key1']
+        sleep(0.001)
+
+        # Add a new item that should evict key2 (least recently used after key1 access)
+        cache['key4'] = 'x' * 2000
+        assert 'key1' in cache
+        assert 'key2' not in cache
+        assert 'key3' in cache
+        assert 'key4' in cache
+
+    def test_get_lru(self):
+        """Test the get_lru method."""
+        cache = self.init_cache(serializer=utf8_serializer)
+
+        cache['key1'] = 'x' * 10
+        sleep(0.001)
+        cache['key2'] = 'x' * 20
+        sleep(0.001)
+        cache['key3'] = 'x' * 30
+
+        # Should return keys in LRU order until total size >= requested
+        lru_keys = cache.get_lru(25)  # Should get key1 (10) + key2 (20) = 30 >= 25
+        assert lru_keys == ['key1', 'key2']
+
+        lru_keys = cache.get_lru(55)  # Should get all keys (10 + 20 + 30 = 60 >= 55)
+        assert lru_keys == ['key1', 'key2', 'key3']
+
+        lru_keys = cache.get_lru(5)  # Should get just key1 (10 >= 5)
+        assert lru_keys == ['key1']
+
+    def test_update_existing_item(self):
+        """Test updating an existing item updates size and access_time."""
+        cache = self.init_cache()
+
+        cache['key1'] = 'small'
+        original_time = None
+        original_size = None
+
+        with cache.connection() as con:
+            row = con.execute(
+                f'SELECT access_time, size FROM {cache.table_name} WHERE key=?', ('key1',)
+            ).fetchone()
+            original_time = row[0]
+            original_size = row[1]
+
+        sleep(0.001)
+        cache['key1'] = 'much_larger_value'
+
+        with cache.connection() as con:
+            row = con.execute(
+                f'SELECT access_time, size FROM {cache.table_name} WHERE key=?', ('key1',)
+            ).fetchone()
+            new_time = row[0]
+            new_size = row[1]
+
+        assert new_time > original_time
+        assert new_size > original_size
+
+    def test_delete_item(self):
+        """Test that deleting items works correctly."""
+        cache = self.init_cache()
+
+        cache['key1'] = 'value1'
+        cache['key2'] = 'value2'
+
+        assert len(cache) == 2
+        del cache['key1']
+        assert len(cache) == 1
+        assert 'key1' not in cache
+        assert 'key2' in cache
+
+    def test_multiple_cache_instances(self):
+        """Test that multiple cache instances see the same data."""
+        cache1 = self.init_cache(index=1)
+        cache2 = self.init_cache(index=1, clear=False)
+
+        cache1['key1'] = 'value1'
+        assert cache2['key1'] == 'value1'
+
+        del cache1['key1']
+        assert 'key1' not in cache2
+
+    def test_bulk_operations_with_eviction(self):
+        """Test bulk operations trigger appropriate evictions."""
+        cache = self.init_cache(max_cache_bytes=26000, serializer=utf8_serializer)
+
+        with cache.bulk_commit():
+            for i in range(20):
+                cache[f'key_{i}'] = 'x' * 150
+
+        assert len(cache) < 20
+        assert len(cache) > 0
+
+    def test_eviction_with_update(self):
+        """Test eviction calculation when updating existing keys."""
+        # Use max_cache_bytes that accounts for SQLite database overhead (~24KB minimum)
+        cache = self.init_cache(max_cache_bytes=25200, serializer=utf8_serializer)
+
+        cache['key1'] = 'x' * 400
+        cache['key2'] = 'x' * 400
+
+        # Update key1 with larger value - should trigger eviction of key2
+        cache['key1'] = 'x' * 800
+        assert 'key1' in cache
+        assert 'key2' not in cache
 
 
 class TestSQLiteCache(BaseCacheTest):