474 lines
14 KiB
Python
474 lines
14 KiB
Python
import json
|
|
import os
|
|
import shutil
|
|
from datetime import date, datetime
|
|
from os import path
|
|
|
|
import pytest
|
|
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
|
|
from sdp.sheerkaSerializer import PickleSerializer
|
|
|
|
tests_root = path.abspath("../../build/tests")
|
|
evt_digest = "3a571cb6034ef6fc8d7fe91948d0d29728eed74de02bac7968b0e9facca2c2d7"
|
|
|
|
|
|
def read_json_file(sdp, file_name):
|
|
with sdp.io.open(file_name, "r") as f:
|
|
return json.load(f)
|
|
|
|
|
|
class ObjNoKey:
|
|
"""
|
|
Object with no key, they won't be ordered
|
|
Not suitable for Json dump as there is no to_dict() method
|
|
"""
|
|
|
|
def __init__(self, a, b):
|
|
self.a = a
|
|
self.b = b
|
|
|
|
def __hash__(self):
|
|
return hash((self.a, self.b))
|
|
|
|
def __eq__(self, obj):
|
|
return isinstance(obj, ObjNoKey) and \
|
|
self.a == obj.a and \
|
|
self.b == obj.b
|
|
|
|
def __repr__(self):
|
|
return f"ObjNoKey({self.a}, {self.b})"
|
|
|
|
|
|
class ObjWithDigestWithKey:
|
|
"""
|
|
Object with a key that can compute its digest.
|
|
It can be used to test objects sharing the same key (but that are different)
|
|
Not suitable for Json dump as there is no to_dict() method
|
|
"""
|
|
|
|
def __init__(self, a, b):
|
|
self.a = a
|
|
self.b = b
|
|
|
|
def __hash__(self):
|
|
return hash((self.a, self.b))
|
|
|
|
def __eq__(self, obj):
|
|
return isinstance(obj, ObjWithDigestWithKey) and \
|
|
self.a == obj.a and \
|
|
self.b == obj.b
|
|
|
|
def __repr__(self):
|
|
return f"ObjWithDigestWithKey({self.a}, {self.b})"
|
|
|
|
def get_key(self):
|
|
return self.a
|
|
|
|
def get_digest(self):
|
|
return str(self.a) + str(self.b)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def init_test():
|
|
if path.exists(tests_root):
|
|
shutil.rmtree(tests_root)
|
|
|
|
if not path.exists(tests_root):
|
|
os.makedirs(tests_root)
|
|
current_pwd = os.getcwd()
|
|
os.chdir(tests_root)
|
|
|
|
yield None
|
|
|
|
os.chdir(current_pwd)
|
|
|
|
|
|
@pytest.mark.parametrize("root, expected", [
|
|
(".sheerka", path.abspath(path.join(tests_root, ".sheerka"))),
|
|
("mem://", "")
|
|
])
|
|
def test_i_can_init_the_data_provider(root, expected):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
assert sdp.io.root == expected
|
|
assert sdp.io.exists(sdp.io.root)
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_save_and_load_an_event(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
event = Event("hello world", date=date(year=2007, month=9, day=10), user_id="kodjo")
|
|
|
|
evt_digest = sdp.save_event(event)
|
|
evt = sdp.load_event(evt_digest)
|
|
|
|
assert evt.version == 1
|
|
assert evt.date == datetime(year=2007, month=9, day=10)
|
|
assert evt.user_id == "kodjo"
|
|
assert evt.message == "hello world"
|
|
assert evt.parents is None
|
|
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.EventFolder, evt_digest[0:24], evt_digest))
|
|
|
|
# I can get the last event
|
|
evt = sdp.load_event()
|
|
assert evt.message == "hello world"
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_get_event_history(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
event = Event("hello world", date=date(year=2007, month=9, day=10), user_id="kodjo")
|
|
event2 = Event("hello world 2", date=date(year=2007, month=9, day=10), user_id="kodjo")
|
|
|
|
evt_digest1 = sdp.save_event(event)
|
|
evt_digest2 = sdp.save_event(event2)
|
|
|
|
evt = sdp.load_event(evt_digest2)
|
|
assert evt.version == 1
|
|
assert evt.date == datetime(year=2007, month=9, day=10)
|
|
assert evt.user_id == "kodjo"
|
|
assert evt.message == "hello world 2"
|
|
assert evt.parents == [evt_digest1]
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_load_events(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
for i in range(15):
|
|
sdp.save_event(Event(f"Hello {i}"))
|
|
|
|
events = list(sdp.load_events(10)) # first ten
|
|
assert len(events) == 10
|
|
assert events[0].message == "Hello 14"
|
|
assert events[9].message == "Hello 5"
|
|
|
|
events = list(sdp.load_events(10, 5)) # skip first 5, then take 10
|
|
assert len(events) == 10
|
|
assert events[0].message == "Hello 9"
|
|
assert events[9].message == "Hello 0"
|
|
|
|
events = list(sdp.load_events(20, 10)) # skip first 10, take 20,(but only 5 remaining)
|
|
assert len(events) == 5
|
|
assert events[0].message == "Hello 4"
|
|
assert events[4].message == "Hello 0"
|
|
|
|
events = list(sdp.load_events(1, 20)) # skip first 20, take one
|
|
assert len(events) == 0
|
|
|
|
events = list(sdp.load_events(0)) # all
|
|
assert len(events) == 15
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_load_events_when_no_event(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
events = list(sdp.load_events(1))
|
|
assert len(events) == 0
|
|
|
|
events = list(sdp.load_events(1, 5))
|
|
assert len(events) == 0
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_and_reload_one_item(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
event = Event("hello world", date=date(year=2007, month=9, day=10), user_id="kodjo")
|
|
with sdp.get_transaction(event) as transaction:
|
|
transaction.add("entry", "key", "foo => bar")
|
|
transaction.add("entry", "key2", ObjNoKey("a", "b"))
|
|
transaction.add("entry2", "key", "value2")
|
|
|
|
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
|
state = sdp.load_state(last_commit)
|
|
loaded1 = sdp.get("entry", "key")
|
|
loaded2 = sdp.get("entry", "key2")
|
|
loaded3 = sdp.get("entry2", "key")
|
|
|
|
load_entry = sdp.get("entry")
|
|
|
|
# check that the event is saved
|
|
evt_digest = event.get_digest()
|
|
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.EventFolder, evt_digest[0:24], evt_digest))
|
|
|
|
# check the values
|
|
assert loaded1 == "foo => bar"
|
|
assert loaded2 == ObjNoKey("a", "b")
|
|
assert loaded3 == "value2"
|
|
|
|
assert load_entry == {
|
|
"key": "foo => bar",
|
|
"key2": ObjNoKey("a", "b")
|
|
}
|
|
|
|
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit))
|
|
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.HeadFile))
|
|
|
|
assert state.date is not None
|
|
assert state.parents == []
|
|
assert state.events == [evt_digest]
|
|
assert state.data == {"entry": {'key': 'foo => bar', 'key2': ObjNoKey("a", "b")},
|
|
'entry2': {'key': 'value2'}}
|
|
|
|
assert sdp.io.read_text(path.join(sdp.io.root, SheerkaDataProvider.HeadFile)) == last_commit
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_load_an_entry(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key1", "foo")
|
|
transaction.add("entry", "key2", "bar")
|
|
transaction.add("entry", "key3", "baz")
|
|
|
|
load_entry = sdp.get("entry")
|
|
|
|
assert load_entry == {
|
|
"key1": "foo",
|
|
"key2": "bar",
|
|
"key3": "baz",
|
|
}
|
|
|
|
# load entry was a copy
|
|
load_entry["key1"] = "another foo"
|
|
assert sdp.get("entry", "key1") == "foo"
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_and_reload_a_list_of_items(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", ["foo => bar", ObjNoKey("a", "b")])
|
|
|
|
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
|
state = sdp.load_state(last_commit)
|
|
loaded = sdp.get("entry", "key")
|
|
|
|
# check the values
|
|
assert loaded == ["foo => bar", ObjNoKey("a", "b")]
|
|
|
|
assert state.date is not None
|
|
assert state.parents == []
|
|
assert state.events == [evt_digest]
|
|
assert state.data == {"entry": {'key': ['foo => bar', ObjNoKey('a', 'b')]}}
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_and_reload_a_set_of_items(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", {"foo => bar", ObjNoKey("a", "b")})
|
|
|
|
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
|
|
state = sdp.load_state(last_commit)
|
|
loaded = sdp.get("entry", "key")
|
|
|
|
# check the values
|
|
assert loaded == {"foo => bar", ObjNoKey("a", "b")}
|
|
|
|
assert state.date is not None
|
|
assert state.parents == []
|
|
assert state.events == [evt_digest]
|
|
assert state.data == {"entry": {'key': {'foo => bar', ObjNoKey('a', 'b')}}}
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_and_reload_an_entry(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry1", None, "foo")
|
|
transaction.add("entry2", None, {"key": "foo", "key1": "bar"})
|
|
transaction.add("entry3", None, {"foo", "bar"})
|
|
transaction.add("entry4", None, ["foo", "bar"])
|
|
|
|
loaded_entry1 = sdp.get("entry1")
|
|
loaded_entry2 = sdp.get("entry2")
|
|
loaded_entry3 = sdp.get("entry3")
|
|
loaded_entry4 = sdp.get("entry4")
|
|
|
|
assert loaded_entry1 == "foo"
|
|
assert loaded_entry2 == {"key": "foo", "key1": "bar"}
|
|
assert loaded_entry3 == {"foo", "bar"}
|
|
assert loaded_entry4 == ["foo", "bar"]
|
|
|
|
# loaded values are copies
|
|
loaded_entry2["key"] = "foo2"
|
|
assert sdp.get("entry2", "key") == "foo"
|
|
|
|
loaded_entry3.remove("foo")
|
|
assert sdp.get("entry3") == {"foo", "bar"}
|
|
|
|
loaded_entry4[0] = "foo2"
|
|
assert sdp.get("entry4")[0] == "foo"
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_override_values(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", {"foo => bar", ObjNoKey("a", "b")})
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", "new_value")
|
|
|
|
loaded = sdp.get("entry", "key")
|
|
assert loaded == "new_value"
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_an_object_and_save_it_as_a_reference(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjNoKey)))
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key1", ObjNoKey("a", "b"), use_ref=True)
|
|
transaction.add("entry", "key2", [ObjNoKey("a", "b"), ObjNoKey("c", "d")], use_ref=True)
|
|
transaction.add("entry", "key3", {ObjNoKey("a", "b"), ObjNoKey("c", "d")}, use_ref=True)
|
|
|
|
assert sdp.get("entry", "key1") == ObjNoKey("a", "b")
|
|
assert sdp.get("entry", "key2") == [ObjNoKey("a", "b"), ObjNoKey("c", "d")]
|
|
assert sdp.get("entry", "key3") == {ObjNoKey("a", "b"), ObjNoKey("c", "d")}
|
|
|
|
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
|
|
assert state.data == {
|
|
"entry": {'key1': '##REF##:8fac7e801d08361c3449c594b4261ab9c45ef47f1a08df68eb717db2b6919774',
|
|
'key2': ['##REF##:8fac7e801d08361c3449c594b4261ab9c45ef47f1a08df68eb717db2b6919774',
|
|
'##REF##:2a07d90eefd71a1fc5fae4d4745ab969b2d9a3e7dd159da6d47ec69630b2acf2'],
|
|
'key3': {'##REF##:2a07d90eefd71a1fc5fae4d4745ab969b2d9a3e7dd159da6d47ec69630b2acf2',
|
|
'##REF##:8fac7e801d08361c3449c594b4261ab9c45ef47f1a08df68eb717db2b6919774'}}
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_add_an_object_as_a_reference_using_its_own_digest(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey)))
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key1", ObjWithDigestWithKey("a", "b"), use_ref=True)
|
|
|
|
assert sdp.get("entry", "key1") == ObjWithDigestWithKey("a", "b")
|
|
|
|
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
|
|
assert state.data == {
|
|
"entry": {'key1': '##REF##:ab'}
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_remove_elements(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", "value")
|
|
transaction.add("entry", "key2", "value2")
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.remove("entry", "key")
|
|
|
|
assert sdp.get("entry", "key") is None
|
|
|
|
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
|
|
assert state.data == {
|
|
"entry": {'key2': 'value2'}
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("root", [
|
|
".sheerka",
|
|
"mem://"
|
|
])
|
|
def test_i_can_keep_state_history(root):
|
|
sdp = SheerkaDataProvider(root)
|
|
|
|
with sdp.get_transaction(Event("first event")) as transaction:
|
|
transaction.add("entry", "key", "value")
|
|
state_digest1 = transaction.snapshot
|
|
|
|
with sdp.get_transaction(Event("second event")) as transaction:
|
|
transaction.add("entry", "key2", "value2")
|
|
state_digest2 = transaction.snapshot
|
|
|
|
with sdp.get_transaction(Event("third event")) as transaction:
|
|
transaction.add("entry", "key2", "value2")
|
|
state_digest3 = transaction.snapshot
|
|
|
|
state = sdp.load_state(state_digest3)
|
|
assert state.parents == [state_digest2]
|
|
|
|
state = sdp.load_state(state_digest2)
|
|
assert state.parents == [state_digest1]
|
|
|
|
state = sdp.load_state(state_digest1)
|
|
assert state.parents == []
|
|
|
|
|
|
def test_i_can_remove_even_if_not_exist():
|
|
sdp = SheerkaDataProvider("mem://")
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.remove("entry", None)
|
|
transaction.remove(None, "key")
|
|
transaction.remove("entry", "key")
|
|
|
|
|
|
def test_i_get_default_value_if_entry_is_missing():
|
|
sdp = SheerkaDataProvider("mem://")
|
|
assert sdp.get("fake_entry", "fake_key", "default_value") == "default_value"
|
|
|
|
|
|
def test_exists():
|
|
sdp = SheerkaDataProvider("mem://")
|
|
|
|
with sdp.get_transaction(evt_digest) as transaction:
|
|
transaction.add("entry", "key", "value")
|
|
|
|
assert not sdp.exists("entry2")
|
|
assert not sdp.exists("entry", "key2")
|
|
assert sdp.exists("entry", "key")
|