From 130380d6ceb810712b449ed183cd2abe03129bf9 Mon Sep 17 00:00:00 2001
From: Daniel <d.hornung@indiscale.com>
Date: Thu, 13 Apr 2023 13:55:27 +0200
Subject: [PATCH] TEST: Added and improved unit tests.

---
 unittests/test_cached.py | 103 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 95 insertions(+), 8 deletions(-)

diff --git a/unittests/test_cached.py b/unittests/test_cached.py
index 6e87c12a..ce302d67 100644
--- a/unittests/test_cached.py
+++ b/unittests/test_cached.py
@@ -21,7 +21,7 @@
 
 """ Test the caosdb.cached module """
 
-from caosdb.cached import (cached_get_entity_by, cache_clear, cache_info, fill_cache,
+from caosdb.cached import (cached_get_entity_by, cache_clear, cache_info, cache_fill,
                            AccessType, cache_initialize, cached_query)
 from unittest.mock import patch
 import caosdb as db
@@ -60,11 +60,17 @@ def mocked_path_query(path):
     return deepcopy([el for el in DUMMY_SERVER_CONTENT if el.path == path][0])
 
 
-def mocked_gen_query(q):
-    if q == 'a':
-        return db.Container().extend([DUMMY_SERVER_CONTENT[0]])
+def mocked_gen_query(q, unique):
+    if unique:
+        if q == 'a':
+            return DUMMY_SERVER_CONTENT[0]
+        else:
+            return None
     else:
-        return db.Container().extend(DUMMY_SERVER_CONTENT)
+        if q == 'a':
+            return db.Container().extend([DUMMY_SERVER_CONTENT[0]])
+        else:
+            return db.Container().extend(DUMMY_SERVER_CONTENT)
 
 
 @patch("caosdb.utils.get_entity.get_entity_by_name")
@@ -87,7 +93,7 @@ def test_get_by_name(mocked_get_by_name):
     cached_get_entity_by(name='a')
     assert mocked_get_by_name.call_count == 2
     # we fill the cache manually and make sure the element is used
-    fill_cache({'lol': db.Entity(id=10001, name='lol')}, AccessType.NAME, unique=True)
+    cache_fill({'lol': db.Entity(id=10001, name='lol')}, AccessType.NAME, unique=True)
     # there are now two elements in the cache: a and lol
     assert cache_info().currsize == 2
     # we can retrieve the inserted element
@@ -122,7 +128,7 @@ def test_get_by_id(mocked_get_by_id):
     cached_get_entity_by(eid=102)
     assert mocked_get_by_id.call_count == 2
     # we fill the cache manually and make sure the element is used
-    fill_cache({10001: db.Entity(id=10001, name='lol')}, AccessType.EID, unique=True)
+    cache_fill({10001: db.Entity(id=10001, name='lol')}, AccessType.EID, unique=True)
     # there are now two elements in the cache: a and lol
     assert cache_info().currsize == 2
     # we can retrieve the inserted element
@@ -156,7 +162,7 @@ def test_get_by_path(mocked_get_by_path):
     cached_get_entity_by(path='p')
     assert mocked_get_by_path.call_count == 2
     # we fill the cache manually and make sure the element is used
-    fill_cache({'lol': db.File(id=10001, path='lol')}, AccessType.PATH, unique=True)
+    cache_fill({'lol': db.File(id=10001, path='lol')}, AccessType.PATH, unique=True)
     # there are now two elements in the cache: a and lol
     assert cache_info().currsize == 2
     # we can retrieve the inserted element
@@ -170,6 +176,35 @@ def test_get_by_path(mocked_get_by_path):
     assert c.id == 105
 
 
+@patch("caosdb.cached.execute_query")
+def test_get_by_query(mocked_query):
+    mocked_query.side_effect = mocked_gen_query
+    # test cache initialization
+    cache_initialize(maxsize=10)
+    assert cache_info().currsize == 0
+
+    # Non-existent entity
+    res = cached_get_entity_by(query='stuff')
+    assert res is None
+    assert cache_info().currsize == 1
+    assert cache_info().hits == 0
+    assert cache_info().misses == 1
+
+    res = cached_get_entity_by(query='stuff')
+    assert res is None
+    assert cache_info().currsize == 1
+    assert cache_info().hits == 1
+    assert cache_info().misses == 1
+
+    # Existent entity
+    a = cached_get_entity_by(query='a')
+    assert a is not None
+    assert a.id == 101
+    assert cache_info().currsize == 2
+    assert cache_info().hits == 1
+    assert cache_info().misses == 2
+
+
 @patch("caosdb.cached.execute_query")
 def test_cached_query(mocked_query):
     mocked_query.side_effect = mocked_gen_query
@@ -206,3 +241,55 @@ def test_cached_query(mocked_query):
     c = cached_query('a')
     assert mocked_query.call_count == 3
     assert c[0].id == 101
+
+
+@patch("caosdb.utils.get_entity.get_entity_by_name")
+def test_cache_size(mocked_get_by_name):
+    mocked_get_by_name.side_effect = lambda x: x
+    # first call; not in cache -> mocked_execute is touched
+    maxsize = 5
+    cache_initialize(maxsize=maxsize)
+    assert cache_info().currsize == 0
+
+    names_first = ("a", "b", "c", "d", "e")
+    names_later = ("A", "B", "C", "D", "E")
+    names_fill = {"X": None, "Y": None, "Z": None}
+
+    # Use the first batch of names
+    for ii, name in enumerate(names_first, start=1):
+        cached_get_entity_by(name=name)
+        assert cache_info().currsize == ii
+        assert cache_info().hits == 0
+        assert cache_info().misses == ii
+    for ii, name in enumerate(names_first, start=1):
+        cached_get_entity_by(name=name)
+        assert cache_info().currsize == maxsize
+        assert cache_info().hits == ii
+        assert cache_info().misses == maxsize
+
+    # use the second batch of names
+    for ii, name in enumerate(names_later, start=1):
+        cached_get_entity_by(name=name)
+        assert cache_info().currsize == maxsize
+        assert cache_info().hits == len(names_first)
+        assert cache_info().misses == len(names_first) + ii
+    for ii, name in enumerate(names_later, start=1):
+        cached_get_entity_by(name=name)
+        assert cache_info().currsize == maxsize
+        assert cache_info().hits == len(names_first) + ii
+        assert cache_info().misses == len(names_first) + len(names_later)
+
+    # The cache is now filled with A,B,C,D,E (oldest to least recently used).
+    # Let's fill it with X,Y,Z.
+    cache_fill(names_fill, kind=AccessType.NAME)
+
+    # Now, the cache should be: D,E,X,Y,Z
+    current_misses = cache_info().misses
+
+    for name in ("Z", "Y", "X", "E", "D"):
+        cached_get_entity_by(name=name)
+        assert cache_info().misses == current_misses
+
+    for ii, name in enumerate(("A", "B", "C"), start=1):
+        cached_get_entity_by(name=name)
+        assert cache_info().misses == current_misses + ii
-- 
GitLab