diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbd092e8c4438a036c90186a330173848cb2bd86..e4288aa79c9d90de1489bff48469e33366c7b7bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added (for new features)
 
+* Tests for versioning
 * Tests for deeply nested SELECT queries
 * Tests for [#62](https://gitlab.com/caosdb/caosdb-server/-/issues/62)
 * Tests for One-time Authentication Tokens
diff --git a/setup.py b/setup.py
index f673cacea5a4b27469fc59719b83bb6124a162d9..d836a2731c2fc1da4d7515917fa21e59b8f3f6b1 100644
--- a/setup.py
+++ b/setup.py
@@ -3,5 +3,5 @@ setup(
     name="PyCaosDB Integration Tests",
     version="0.1.0",
     packages=find_packages(),
-    tests_require=["nose>=1.0"],
+    tests_require=["nose>=1.0", "python-dateutil>=2.8.1"],
 )
diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py
index 04ba6f8b2f650c5d2ca6e979050825029e90383e..16e1ec026db33e69ed525752d53dcfc211b1c9a8 100644
--- a/tests/test_inheritance.py
+++ b/tests/test_inheritance.py
@@ -187,63 +187,47 @@ def test_inheritance_all_properties():
 
 
 def test_inheritance_unit():
-    try:
-
-        p = h.Property(
-            name="SimpleIntProperty",
-            datatype="INTEGER",
-            unit="m").insert()
-        assert_true(p.is_valid())
-        assert_equal("m", p.unit)
-
-        rt = h.RecordType(
-            name="SimpleRecordType").add_property(
-            p, unit="km").insert()
-        assert_true(rt.is_valid())
-        assert_equal("km", rt.get_property("SimpleIntProperty").unit)
-
-        rt2 = h.execute_query("FIND SimpleRecordType", True)
-        assert_true(rt2.is_valid())
-        assert_equal(rt2.id, rt.id)
-        assert_equal(rt2.get_property("SimpleIntProperty").unit, "km")
-
-        rt3 = h.RecordType(
-            name="SimpleRecordType2").add_parent(
-            rt, inheritance="ALL").insert()
-        assert_true(rt3.is_valid())
-        assert_is_not_none(rt3.get_property("SimpleIntProperty"))
-        assert_equal(rt3.get_property("SimpleIntProperty").unit, "km")
-
-        rt4 = h.execute_query("FIND SimpleRecordType2", True)
-        assert_true(rt4.is_valid())
-        assert_equal(rt4.id, rt3.id)
-        assert_equal(rt4.get_property("SimpleIntProperty").unit, "km")
-
-        rec = h.Record(
-            name="SimpleRecord").add_parent(rt3).add_property(
-            name="SimpleIntProperty",
-            value=1).insert()
-        assert_true(rec.is_valid())
-        assert_is_not_none(rec.get_property("SimpleIntProperty"))
-        assert_equal(rec.get_property("SimpleIntProperty").unit, "km")
-
-    finally:
-        try:
-            rec.delete()
-        except BaseException:
-            pass
-        try:
-            rt3.delete()
-        except BaseException:
-            pass
-        try:
-            rt.delete()
-        except BaseException:
-            pass
-        try:
-            p.delete()
-        except BaseException:
-            pass
+    p = h.Property(
+        name="SimpleIntProperty",
+        datatype="INTEGER",
+        unit="m")
+    p.insert()
+    assert p.is_valid()
+    assert p.unit == "m"
+
+    rt = h.RecordType(
+        name="SimpleRecordType")
+    rt.add_property(p, unit="km")
+    rt.insert()
+
+    assert rt.is_valid()
+    assert rt.get_property("SimpleIntProperty").unit == "km"
+
+    rt2 = h.execute_query("FIND SimpleRecordType", True)
+    assert rt2.id == rt.id
+    assert rt2.get_property("SimpleIntProperty").unit == "km"
+
+    rt3 = h.RecordType(
+        name="SimpleRecordType2")
+    rt3.add_parent(rt, inheritance="ALL")
+    rt3.insert()
+    assert rt3.is_valid()
+    assert rt3.get_property("SimpleIntProperty") is not None
+    assert rt3.get_property("SimpleIntProperty").unit == "km"
+
+    rt4 = h.execute_query("FIND SimpleRecordType2", True)
+    assert rt4.is_valid()
+    assert rt4.id == rt3.id
+    assert rt4.get_property("SimpleIntProperty").unit == "km"
+
+    rec = h.Record(
+        name="SimpleRecord")
+    rec.add_parent(rt3)
+    rec.add_property(name="SimpleIntProperty", value=1)
+    rec.insert()
+    assert rec.is_valid()
+    assert rec.get_property("SimpleIntProperty") is not None
+    assert rec.get_property("SimpleIntProperty").unit == "km"
 
 
 _ENTITIES = [
diff --git a/tests/test_issues_mysqlbackend.py b/tests/test_issues_mysqlbackend.py
index 6b61f0254401ca684feae66b3302898793ef4fe9..214a5c38254c864d5f3677302b8abeab3c0228e3 100644
--- a/tests/test_issues_mysqlbackend.py
+++ b/tests/test_issues_mysqlbackend.py
@@ -24,9 +24,6 @@
 """Tests for issues on gitlab.com, project caosdb-mysqlbackend."""
 
 import caosdb as db
-from nose import with_setup
-from nose.tools import assert_equal
-import pytest
 
 
 def setup_module():
@@ -48,7 +45,6 @@ def teardown():
 
 # ########################### Issue tests start here #####################
 
-@with_setup(setup, teardown)
 def test_issue_18():
     """Duplicate parents were returned in some cases of self-hineritance.
 
@@ -69,4 +65,4 @@ def test_issue_18():
 
     C1 = db.Entity(name="C").retrieve()
     pids = [p.id for p in C1.parents]
-    assert_equal(len(set(pids)), len(pids), "Duplicate parents.")
+    assert len(set(pids)) == len(pids), "Duplicate parents."
diff --git a/tests/test_records.py b/tests/test_records.py
index 851090efd01a1daec03a23b1a769af2a7e7777fb..011ebef67568f7b4bd6ff0097e601cef414c5fdb 100644
--- a/tests/test_records.py
+++ b/tests/test_records.py
@@ -24,11 +24,19 @@
 from random import randint
 from sys import maxsize as maxint
 from nose.tools import nottest, assert_is_not_none, assert_equal, assert_true, assert_false
+from pytest import raises
 from caosdb import Record
 from caosdb.exceptions import CaosDBException
 import caosdb
 
 
+def teardown():
+    try:
+        caosdb.execute_query("FIND Test*").delete()
+    except Exception as e:
+        print(e)
+
+
 def test_records():
 
     try:
@@ -481,3 +489,16 @@ def test_non_existent():
             found = True
 
     assert_true(found)
+
+
+def test_insertion_with_ambiguous_parent_name():
+    rt1 = caosdb.RecordType("TestParent").insert()
+    rt2 = caosdb.RecordType("TestParent").insert(unique=False)
+
+    rec = caosdb.Record("TestRec").add_parent(
+        "TestParent").insert(raise_exception_on_error=False)
+    assert rec.has_errors()
+    assert rec.get_errors()[
+        "Error", 116][0] == "Entity has unqualified parents."
+    assert rec.get_parents()[0].get_errors()[
+        "Error", 0][0] == "Entity can not be identified due to name duplicates."
diff --git a/tests/test_recursive_parents.py b/tests/test_recursive_parents.py
index 01bc489b573b7cec187a90245c8ad188b8fd5c92..1efd9390d9a01a417f91bca1cce7f0f76c154e4b 100644
--- a/tests/test_recursive_parents.py
+++ b/tests/test_recursive_parents.py
@@ -131,14 +131,14 @@ def test_entity_has_parent():
     assert not c.has_parent(fake_C_name, check_name=True)
 
     fake_B_id = db.RecordType(id=B.id)
-    fake_C_id = db.RecordType(id=C.id*5)
+    fake_C_id = db.RecordType(id=C.id * 5)
 
     assert c.has_parent(fake_B_id, check_name=False, check_id=True)
     assert not c.has_parent(fake_C_id, check_name=False,
                             check_id=True)
 
     fake_B_name_id = RecordType(name="TestTypeB", id=B.id)
-    fake_C_name_id = RecordType(name="not C", id=C.id*5)
+    fake_C_name_id = RecordType(name="not C", id=C.id * 5)
 
     assert c.has_parent(fake_B_name_id, check_name=True,
                         check_id=True)
diff --git a/tests/test_tickets.py b/tests/test_tickets.py
index 8c370cba33624e93220a171a22845f4b2eddfb3e..3afd753beae2673eba0387e657a2292f127097bb 100644
--- a/tests/test_tickets.py
+++ b/tests/test_tickets.py
@@ -25,33 +25,23 @@
 
 @author: tf
 """
-from __future__ import absolute_import, print_function, unicode_literals
-
 import caosdb as db
 from caosdb.exceptions import (AmbiguityException, CaosDBException,
                                EntityDoesNotExistError, EntityError,
                                TransactionError, UniqueNamesError)
-from nose import with_setup
 from nose.tools import (assert_equal, assert_false, assert_is_none,
                         assert_is_not_none, assert_raises, assert_true,
                         nottest)
 
-from tests import test_misc
-
-
-def setup_module():
-    try:
-        db.execute_query("FIND ENTITY WITH ID > 100").delete()
-    except Exception as e:
-        print(e)
-
 
 def setup():
-    pass
+    d = db.execute_query("FIND ENTITY WITH ID > 99")
+    if len(d) > 0:
+        d.delete()
 
 
 def teardown():
-    setup_module()
+    setup()
 
 
 def test_ticket_103a():
@@ -294,83 +284,72 @@ def test_ticket_114():
 
 
 def test_ticket_120():
-
-    try:
-        p = db.Property(
-            name="SimpleDoubleProperty", datatype="DOUBLE").insert()
-        rt1 = db.RecordType(name="SimpleRT1").insert()
-        rt2 = db.RecordType(name="SimpleRT2").add_parent(rt1).insert()
-        rt3 = db.RecordType(name="SimpleRT3").add_parent(rt2).insert()
-        r1 = db.Record().add_parent(rt1).add_property(
-            id=p.id, value=3.14).insert()
-        r2 = db.Record().add_parent(rt2).add_property(
-            id=rt1.id, value=r1).insert()
-        r3 = db.Record().add_parent(rt3).add_property(
-            id=rt2.id, value=r2).insert()
-
-        cp = db.Query("FIND PROPERTY SimpleDoubleProperty").execute(
-            unique=True)
-        assert_equal(p.id, cp.id)
-
-        crt123 = db.Query("FIND RECORDTYPE SimpleRT1").execute(unique=False)
-        assert_true(crt123.get_entity_by_id(rt1.id).is_valid())
-        assert_true(crt123.get_entity_by_id(rt2.id).is_valid())
-        assert_true(crt123.get_entity_by_id(rt3.id).is_valid())
-
-        cr1 = db.Query("FIND RECORD . SimpleDoubleProperty='3.14'").execute(
-            unique=True)
-        assert_equal(r1.id, cr1.id)
-
-        cr23 = db.Query("FIND RECORD . SimpleRT1").execute(unique=False)
-        assert_true(cr23.get_entity_by_id(r2.id).is_valid())
-        assert_true(cr23.get_entity_by_id(r3.id).is_valid())
-
-        cr3 = db.Query("FIND RECORD . SimpleRT2").execute(unique=True)
-        assert_equal(r3.id, cr3.id)
-
-        cr2 = db.Query("FIND RECORD . SimpleRT1->" + str(r1.id)).execute(
-            unique=True)
-        assert_equal(r2.id, cr2.id)
-
-        cr3 = db.Query("FIND RECORD . SimpleRT1->" + str(r2.id)).execute(
-            unique=True)
-        assert_equal(r3.id, cr3.id)
-
-        cr3 = db.Query(
-            "FIND RECORD WHICH HAS A PROPERTY blabla=4 OR SimpleRT1->SimpleRT2"
-            " WHICH HAS A PROPERTY SimpleRT1->" +
-            str(
-                r1.id) +
-            "").execute(
-            unique=True)
-        assert_equal(r3.id, cr3.id)
-
-        cr3 = db.Query(
-            "FIND SimpleRT1 . SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
-        ).execute(unique=True)
-        assert_equal(r3.id, cr3.id)
-
-        cr3 = db.Query(
-            "FIND RECORD SimpleRT1 . "
-            "SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
-        ).execute(unique=True)
-        assert_equal(r3.id, cr3.id)
-
-        cr3 = db.Query(
-            "FIND RECORD . SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
-        ).execute(unique=True)
-        assert_equal(r3.id, cr3.id)
-    finally:
-        r3.delete()
-        r2.delete()
-        r1.delete()
-        rt3.delete()
-        rt2.delete()
-        rt1.delete()
-        p.delete()
+    p = db.Property(
+        name="SimpleDoubleProperty", datatype="DOUBLE").insert()
+    rt1 = db.RecordType(name="SimpleRT1").insert()
+    rt2 = db.RecordType(name="SimpleRT2").add_parent(rt1).insert()
+    rt3 = db.RecordType(name="SimpleRT3").add_parent(rt2).insert()
+    r1 = db.Record().add_parent(rt1).add_property(
+        id=p.id, value=3.14).insert()
+    r2 = db.Record().add_parent(rt2).add_property(
+        id=rt1.id, value=r1).insert()
+    r3 = db.Record().add_parent(rt3).add_property(
+        id=rt2.id, value=r2).insert()
+
+    cp = db.Query("FIND PROPERTY SimpleDoubleProperty").execute(
+        unique=True)
+    assert p.id == cp.id
+
+    crt123 = db.Query("FIND RECORDTYPE SimpleRT1").execute(unique=False)
+    assert crt123.get_entity_by_id(rt1.id).is_valid()
+    assert crt123.get_entity_by_id(rt2.id).is_valid()
+    assert crt123.get_entity_by_id(rt3.id).is_valid()
+
+    cr1 = db.Query("FIND RECORD . SimpleDoubleProperty='3.14'").execute(
+        unique=True)
+    assert r1.id == cr1.id
+
+    cr23 = db.Query("FIND RECORD . SimpleRT1").execute(unique=False)
+    assert cr23.get_entity_by_id(r2.id).is_valid()
+    assert cr23.get_entity_by_id(r3.id).is_valid()
+
+    cr3 = db.Query("FIND RECORD . SimpleRT2").execute(unique=True)
+    assert r3.id == cr3.id
+
+    cr2 = db.Query("FIND RECORD . SimpleRT1->" + str(r1.id)).execute(
+        unique=True)
+    assert r2.id == cr2.id
+
+    cr3 = db.Query("FIND RECORD . SimpleRT1->" + str(r2.id)).execute(
+        unique=True)
+    assert r3.id == cr3.id
+
+    cr3 = db.Query(
+        "FIND RECORD WHICH HAS A PROPERTY blabla=4 OR SimpleRT1->SimpleRT2"
+        " WHICH HAS A PROPERTY SimpleRT1->" +
+        str(
+            r1.id) +
+        "").execute(
+        unique=True)
+    assert r3.id == cr3.id
+
+    cr3 = db.Query(
+        "FIND SimpleRT1 . SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
+    ).execute(unique=True)
+    assert r3.id == cr3.id
+
+    cr3 = db.Query(
+        "FIND RECORD SimpleRT1 . "
+        "SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
+    ).execute(unique=True)
+    assert r3.id == cr3.id
+
+    cr3 = db.Query(
+        "FIND RECORD . SimpleRT1.SimpleRT1.SimpleDoubleProperty='3.14'"
+    ).execute(unique=True)
+    assert r3.id == cr3.id
 
 
-@with_setup(setup, teardown)
 def test_ticket_120a():
 
     p = db.Property(name="TestSimpleDoubleProperty", datatype="DOUBLE")
@@ -384,7 +363,6 @@ def test_ticket_120a():
     c.delete(raise_exception_on_error=True)
 
 
-@with_setup(setup, teardown)
 def test_ticket_117():
     p = db.Property(
         name="TestwaveVelocity",
@@ -493,7 +471,6 @@ def test_ticket_86():
         f.delete()
 
 
-@with_setup(setup, teardown)
 def test_ticket_83():
     rt1 = db.RecordType(name="TestRT1").insert()
     rt2 = db.RecordType(name="TestRT2").insert()
@@ -559,7 +536,6 @@ def test_ticket_138():
         rt_person.delete()
 
 
-@with_setup(setup, teardown)
 def test_ticket_137():
     # insert RecordType
     rt1 = db.RecordType("TestRT1").insert()
@@ -580,7 +556,6 @@ def test_ticket_137():
         print(e)
 
 
-@with_setup(setup, teardown)
 def test_ticket_132():
     # insert RecordType
     rt1 = db.RecordType("TestRT1").insert()
@@ -598,7 +573,6 @@ def test_ticket_132():
     assert_true(isinstance(p2, db.Property))
 
 
-@with_setup(setup, teardown)
 @nottest
 def test_ticket_39():
     import os
diff --git a/tests/test_version.py b/tests/test_version.py
new file mode 100644
index 0000000000000000000000000000000000000000..40f0cc6bfc3045dd007631bfcf728cbbe093cc5f
--- /dev/null
+++ b/tests/test_version.py
@@ -0,0 +1,724 @@
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+from pytest import mark, raises
+from dateutil.parser import parse
+import caosdb as c
+
+
+def setup():
+    d = c.execute_query("FIND Test*")
+    if len(d) > 0:
+        d.delete()
+
+
+def teardown():
+    setup()
+
+
+def test_version_object():
+    from caosdb.common.versioning import Version
+
+
+def insertion(name="TestRT"):
+    rt = c.RecordType(name, description="TestDescription1").insert()
+    assert rt.version is not None
+    assert rt.version.id is not None
+    assert rt.version.date is not None
+    assert len(rt.version.predecessors) == 0
+    assert len(rt.version.successors) == 0
+    return rt
+
+
+def test_retrieve():
+    rt = insertion()
+    version = rt.version
+
+    rt2 = c.execute_query("FIND RecordType TestRT", unique=True)
+    assert parse(rt2.version.date) == parse(version.date)
+    assert rt2.version == version
+
+
+def test_update_description():
+    rt = insertion()
+    old_version = rt.version
+    old_desc = rt.description
+    new_desc = "TestDescription2"
+    rt.description = new_desc
+    rt.update()
+    assert rt.description == new_desc
+    assert rt.version is not None
+    assert rt.version.id is not None
+    assert rt.version.date is not None
+    assert rt.version != old_version
+    assert rt.version.date != old_version.date
+    assert parse(rt.version.date) > parse(old_version.date)
+
+    rt2 = c.execute_query("FIND RecordType TestRT", unique=True)
+    assert rt2.version.id == rt.version.id
+    assert rt2.version == rt.version
+    assert rt2.description == new_desc
+
+    rt3 = c.Container().retrieve(query=str(rt.id), sync=False)[0]
+    assert rt3.version.id == rt.version.id
+    assert rt3.version == rt.version
+    assert rt3.description == new_desc
+
+    # retrieve old version
+    rt_old = c.Container().retrieve(query=str(rt.id) +
+                                    "@" + old_version.id, sync=False)[0]
+    assert rt_old.version.id == old_version.id
+    assert rt_old.description == old_desc
+
+
+def test_update_parent():
+    par1 = insertion("TestRTParent1")
+    par2 = insertion("TestRTParent2")
+    rt = insertion("TestRTChild")
+
+    assert len(rt.get_parents()) == 0
+    first_version = rt.version
+    rt.add_parent(par1)
+    rt.update()
+
+    assert len(rt.get_parents()) == 1
+    assert rt.get_parent("TestRTParent1") is not None
+    second_version = rt.version
+
+    rt.remove_parent(par1)
+    assert len(rt.get_parents()) == 0
+    rt.add_parent(par2)
+    rt.update()
+    assert len(rt.get_parents()) == 1
+    assert rt.get_parent("TestRTParent1") is None
+    assert rt.get_parent("TestRTParent2") is not None
+    third_version = rt.version
+
+    # now retrieve and look again
+    assert c.execute_query("FIND TestRTParent1", unique=True).id == par1.id
+    assert len(c.execute_query("FIND TestRTParent2")) == 2
+    assert c.execute_query("FIND TestRTChild", unique=True).id == rt.id
+
+    rt_head = c.Container().retrieve(query=str(rt.id), sync=False)[0]
+    rt_v1 = c.Container().retrieve(query=str(rt.id) + "@" + first_version.id,
+                                   sync=False)[0]
+    rt_v2 = c.Container().retrieve(query=str(rt.id) + "@" + second_version.id,
+                                   sync=False)[0]
+    rt_v3 = c.Container().retrieve(query=str(rt.id) + "@" + third_version.id,
+                                   sync=False)[0]
+
+    assert rt_head.version == third_version
+    assert rt_v1.version.id == first_version.id
+    assert rt_v2.version.id == second_version.id
+    assert rt_v3.version.id == third_version.id
+
+    assert len(rt_v3.get_parents()) == 1
+    assert rt_v3.get_parent("TestRTParent1") is None
+    assert rt_v3.get_parent("TestRTParent2") is not None
+
+    assert len(rt_v2.get_parents()) == 1
+    assert rt_v2.get_parent("TestRTParent1") is not None
+    assert rt_v2.get_parent("TestRTParent2") is None
+
+    assert len(rt_v1.get_parents()) == 0
+
+
+def test_retrieve_old_version():
+    rt = insertion()
+    old_version = rt.version
+    old_description = rt.description
+
+    rt.description = "TestDescription3"
+    rt.update()
+
+    rt2 = c.execute_query("FIND RecordType TestRT", unique=True)
+    assert rt2.description == "TestDescription3"
+
+    rt_old = c.Container().retrieve(query=str(rt.id) + "@" + old_version.id,
+                                    sync=False)[0]
+    assert rt_old.id == rt.id
+    assert rt_old.description == old_description
+
+
+def test_successor():
+    rt = insertion()
+    old_version = rt.version
+
+    rt.description = "TestDescription5"
+    rt.update()
+
+    rt2 = c.execute_query("FIND RecordType TestRT", unique=True)
+
+    rt_old = c.Container().retrieve(query=str(rt.id) + "@" + old_version.id,
+                                    sync=False)[0]
+
+    assert rt_old.version.successors[0].id == rt2.version.id, (
+        "old version has successor after retrieval")
+
+
+def test_predecessor():
+    rt = insertion()
+    old_version = rt.version
+    old_description = rt.description
+
+    rt.description = "TestDescription6"
+    rt.update()
+
+    assert rt.version.predecessors[0].id == old_version.id, (
+        "latest version has predecessor directly after update")
+
+    rt2 = c.execute_query("FIND RecordType TestRT", unique=True)
+
+    assert rt2.version.predecessors[0].id == old_version.id, (
+        "latest version has predecessor after retrieval")
+
+
+def test_retrieve_relative_to_head():
+    rt = insertion()
+    first_version = rt.version
+
+    # retrieve HEAD
+    rt_head = c.Container().retrieve(query=str(rt.id) + "@HEAD",
+                                     sync=False)[0]
+    rt_head2 = c.Container().retrieve(query=str(rt.id), sync=False)[0]
+    rt_head2.version = rt_head.version
+    assert first_version == rt_head.version, "head is first version"
+
+    # no HEAD~1 before first update
+    with raises(c.EntityDoesNotExistError) as exc:
+        # no head~2
+        c.Container().retrieve(query=str(rt.id) + "@HEAD~1", sync=False)
+
+    # update
+    rt.description = "TestDescription4"
+    rt.update()
+
+    new_head_version = rt.version
+    assert first_version != new_head_version, (
+        "first version is not head anymore")
+    assert new_head_version.predecessors[0] == first_version, (
+        "first version is predessor of head")
+
+    # retrieve HEAD (which should have changed after the update)
+    rt_new_head = c.Container().retrieve(query=str(rt.id) + "@HEAD",
+                                         sync=False)[0]
+    rt_new_head2 = c.Container().retrieve(query=str(rt.id), sync=False)[0]
+    assert rt_new_head2.version == rt_new_head.version
+    assert rt_new_head.version == new_head_version, (
+        "head is version after update")
+    assert rt_new_head.version.predecessors[0] == first_version, (
+        "predecessor of head is first version (after update)")
+
+    # retrieve HEAD~1 (the pre-update version)
+    rt_pre_head = c.Container().retrieve(query=str(rt.id) + "@HEAD~1",
+                                         sync=False)[0]
+    assert rt_pre_head.version.id == first_version.id, (
+        "head~1 is first version (after update)")
+    assert rt_pre_head.version.successors[0].id == rt_new_head.version.id, (
+        "successor of head~1 is head")
+
+    with raises(c.EntityDoesNotExistError) as exc:
+        # no head~2
+        c.Container().retrieve(query=str(rt.id) + "@HEAD~2", sync=False)
+
+
+@mark.xfail(reason="bug fix needed")
+def test_bug_cached_delete():
+    rt = insertion()
+    old_version = rt.version.id
+
+    rt.description = "UpdatedDesc"
+    rt.update()
+
+    # now id@old_version is cached...
+    rt2 = c.Container().retrieve(query=str(rt.id) + "@" + old_version,
+                                 sync=False)[0]
+
+    c.execute_query("FIND RecordType TestRT").delete()
+
+    with raises(c.EntityDoesNotExistError) as exc:
+        c.Container().retrieve(query=str(rt.id) + "@" + old_version,
+                               sync=False)[0]
+
+
+@mark.xfail(reason=("TODO: What is the desired behavior? "
+                    "Resolve in versioning phase 10"))
+def test_delete_property_used_in_old_version():
+    p = c.Property(name="TestProp1", datatype=c.TEXT).insert()
+    del_p = c.Property(name="TestProp2", datatype=c.TEXT).insert()
+    rt = c.RecordType(name="TestRT")
+    rt.add_property(del_p, "blubblub")
+    rt.insert()
+
+    # can't delete the property used by rt@HEAD
+    with raises(c.TransactionError) as exc:
+        del_p.delete()
+    assert "Entity is required by other entities" in str(exc.value)
+
+    # now update rt and remove the property which is to be deleted
+    rt.remove_property(del_p)
+    rt.add_property(p, "blablabla")
+    rt.update()
+
+    # retrieve and check old version
+    old_rt = c.Container().retrieve(str(rt.id) + "@HEAD~1",
+                                    sync=False)[0]
+    assert old_rt.get_property(p) is None
+    assert old_rt.get_property(del_p).value == "blubblub"
+
+    # delete the property use by old_rt
+    del_p_id = del_p.id
+    del_p_name = del_p.name
+    del_p.delete()
+
+    # retrieve old version again
+    old_rt = c.Container().retrieve(str(rt.id) + "@HEAD~1",
+                                    sync=False,
+                                    raise_exception_on_error=False)[0]
+    assert old_rt.get_property(p) is None
+
+    # the value is still there (and the property has nothing but an id)
+    assert old_rt.get_property(del_p_name) is None
+    assert old_rt.get_property(del_p_id).value == "blubblub"
+
+    # TODO: What is the desired behavior?
+    # Currently, the server throws an error.
+    # Should we make this a warning?
+    assert "Entity has unqualified properties" in str(old_rt.get_errors()[0])
+
+    # fails until resolved!
+    assert len(old_rt.get_errors()) == 0
+
+
+@mark.xfail(reason=("TODO: What is the desired behavior? "
+                    "Resolve in versioning phase 10"))
+def test_delete_parent_used_in_old_version():
+    del_rt = c.RecordType(name="TestRT1").insert()
+    rt = c.RecordType(name="TestRT2").insert()
+
+    rec = c.Record(name="TestRec").add_parent(del_rt)
+    rec.insert()
+
+    # can't delete the parent used by rec@HEAD
+    with raises(c.TransactionError) as exc:
+        del_rt.delete()
+    assert "Entity is required by other entities" in str(exc.value)
+
+    # update rec and change parent
+    rec.remove_parent(del_rt)
+    rec.add_parent(rt)
+    rec.update()
+
+    # retrieve old version
+    old_rec = c.Container().retrieve(str(rec.id) + "@HEAD~1",
+                                     sync=False)[0]
+    assert old_rec.get_parent(rt) is None
+    assert old_rec.get_parent(del_rt) is not None
+
+    del_rt_id = del_rt.id
+    del_rt_name = del_rt.name
+    del_rt.delete()
+
+    # retrieve old version, again
+    old_rec = c.Container().retrieve(str(rec.id) + "@HEAD~1",
+                                     sync=False,
+                                     raise_exception_on_error=False)[0]
+
+    assert old_rec.get_parent(rt) is None
+    assert old_rec.get_parent(del_rt_id) is not None
+
+    # TODO: What is the desired behavior?
+    # Currently, the server doesn't report anything
+    # Should we issue a warning?
+    assert len(old_rec.get_messages()) == 0
+
+    # fail until resolved
+    assert len(old_rec.get_errors()) > 0
+    assert "Entity has unqualified parents" in str(old_rec.get_errors()[0])
+
+    # Another problem is caching. This fails because the parent name is still
+    # in the cache.
+    assert old_rec.get_parent(del_rt_name) is None
+
+
+@mark.xfail(reason="bug fix needed")
+def test_bug_cached_parent_name_in_old_version():
+    del_rt = c.RecordType(name="TestRT1").insert()
+    rt = c.RecordType(name="TestRT2").insert()
+
+    rec = c.Record(name="TestRec").add_parent(del_rt)
+    rec.insert()
+
+    # update rec and change parent
+    rec.remove_parent(del_rt)
+    rec.add_parent(rt)
+    rec.update()
+
+    # delete old parent
+    del_rt_name = del_rt.name
+    del_rt.delete()
+
+    # retrieve old version
+    old_rec = c.Container().retrieve(str(rec.id) + "@HEAD~1",
+                                     sync=False,
+                                     raise_exception_on_error=False)[0]
+
+    assert old_rec.get_parent(rt) is None
+
+    # This fails because the parent name is still in the cache.
+    # The name should be forgotten.
+    assert old_rec.get_parent(del_rt_name) is None
+
+
+def test_reference_deleted_in_old_version():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+    p = c.Property(name="TestProp", datatype=c.TEXT).insert()
+
+    referenced_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    referenced_rec.insert()
+
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(p, "blablabla")
+    rec.add_property(ref_rt, referenced_rec)
+    rec.insert()
+    old_version = rec.version.id
+
+    test_rec = c.execute_query(
+        "FIND RECORD TestRec2 WHICH REFERENCES {}".format(referenced_rec.id),
+        unique=True)
+    assert test_rec.get_property(p).value == "blablabla"
+    assert test_rec.get_property(ref_rt).value == referenced_rec.id
+
+    # deletion of the referenced_rec not possible because rec@HEAD is
+    # still pointing at it
+    with raises(c.TransactionError) as exc:
+        referenced_rec.delete()
+    assert "Entity is required by other entities" in str(exc.value)
+
+    # update rec
+    rec.remove_property(ref_rt)
+    rec.update()
+
+    with raises(c.EntityDoesNotExistError) as exc:
+        c.execute_query(
+            "FIND RECORD TestRec2 WHICH REFERENCES {}".format(
+                referenced_rec.id),
+            unique=True)
+
+    test_rec = c.execute_query("FIND RECORD WITH TestProp = blablabla",
+                               unique=True)
+    assert test_rec.get_property(p).value == "blablabla"
+    assert test_rec.get_property(ref_rt) is None
+    assert test_rec.version.predecessors[0].id == old_version
+
+    # retrieve old version
+    old_rec = c.Container().retrieve(str(test_rec.id) + "@HEAD~1",
+                                     sync=False)[0]
+    assert old_rec.version.id == old_version
+    assert old_rec.version.successors[0].id == test_rec.version.id
+    assert old_rec.get_property(p).value == "blablabla"
+    assert old_rec.get_property(ref_rt).value == referenced_rec.id
+
+    # deletion of the referenced_rec now possible because rec@HEAD is not
+    # pointing at it anymore
+    referenced_id = referenced_rec.id
+    referenced_rec.delete()
+
+    # still everything ok
+    test_rec = c.execute_query("FIND RECORD WITH TestProp = blablabla",
+                               unique=True)
+    assert test_rec.get_property(p).value == "blablabla"
+    assert test_rec.get_property(ref_rt) is None
+    assert test_rec.version.predecessors[0].id == old_version
+
+    # retrieve old version again. the reference (to the now deleted entity)
+    # is still there.
+    old_rec = c.Container().retrieve(str(test_rec.id) + "@HEAD~1",
+                                     sync=False)[0]
+    assert old_rec.version.id == old_version
+    assert old_rec.version.successors[0].id == test_rec.version.id
+    assert old_rec.get_property(p).value == "blablabla"
+    assert old_rec.get_property(ref_rt).value == referenced_id
+
+    with raises(c.EntityDoesNotExistError) as exc:
+        c.execute_query("FIND ENTITY WITH ID = {}".format(referenced_id),
+                        unique=True)
+
+    with raises(c.EntityDoesNotExistError) as exc:
+        c.Record(id=referenced_id).retrieve()
+
+
+def test_reference_version_head():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    versioned_rec.insert()
+    version = versioned_rec.version.id
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(ref_rt, str(versioned_rec.id) + "@HEAD")
+    rec = rec.insert(sync=False)
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=version)
+
+    test_rec = c.execute_query(
+        "FIND TestRec2 WHICH HAS A TestReferencedObject", unique=True)
+    assert test_rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=version)
+
+    # now update versioned_rec
+    old_head = versioned_rec.version.id
+    versioned_rec.description = "new desc"
+    versioned_rec.update()
+
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=old_head), "after update still old head"
+
+    test_rec = c.execute_query(
+        "FIND TestRec2 WHICH HAS A TestReferencedObject", unique=True)
+    assert test_rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=old_head), "after query old head"
+
+
+def test_insert_reference_to_head_in_same_container():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    versioned_rec.id = -1
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.id = -2
+    rec.add_property(ref_rt, str(versioned_rec.id) + "@HEAD")
+    container = c.Container()
+    container.extend([versioned_rec, rec])
+    container.insert()
+
+    version_id = c.execute_query("FIND Record TestReferencedObject",
+                                 unique=True).version.id
+    test_rec = c.execute_query("FIND Record TestRT WHICH REFERENCES {}".format(versioned_rec.id),
+                               unique=True)
+    assert test_rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=version_id)
+
+
+def test_update_reference_to_head_minus_one_in_same_container():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    versioned_rec.insert()
+    old_head = versioned_rec.version.id
+
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(ref_rt, str(versioned_rec.id))
+    rec.insert()
+
+    # now update both
+    versioned_rec.description = "new description"
+
+    assert rec.get_property(ref_rt).value == versioned_rec.id, "ref to entity"
+    rec.get_property(ref_rt).value = str(versioned_rec.id) + "@HEAD~1"
+
+    container = c.Container()
+    container.extend([versioned_rec, rec])
+    container.update()
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=versioned_rec.id, ver=old_head), "ref to old_head"
+
+    test_rec = c.execute_query("FIND RECORD TestRT WHICH REFERENCES {}".format(versioned_rec.id),
+                               unique=True)
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=versioned_rec.id, ver=old_head), "after query ref to old_head"
+
+
+def test_update_reference_to_head_minus_one_in_same_container_2():
+    """ This is identical to the previous one with one exception: The
+    referenced entity is not being updated during the transaction but it is
+    included in the update container. This has caused some buggy behavior in
+    the past.
+    """
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(
+        name="TestRec1",
+        description="v1").add_parent(ref_rt)
+    versioned_rec.insert()
+    old_head = versioned_rec.version.id
+
+    # update versioned
+    versioned_rec.description = "v2"
+    versioned_rec.update()
+
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(ref_rt, str(versioned_rec.id))
+    rec.insert()
+
+    # now update only the referencing entity.
+    assert rec.get_property(ref_rt).value == versioned_rec.id, "ref to entity"
+    rec.get_property(ref_rt).value = str(versioned_rec.id) + "@HEAD~1"
+
+    container = c.Container()
+    container.extend([versioned_rec, rec])
+    container.update()
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=versioned_rec.id, ver=old_head), "ref to old_head"
+
+    test_rec = c.execute_query("FIND RECORD TestRT WHICH REFERENCES {}".format(versioned_rec.id),
+                               unique=True)
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=versioned_rec.id, ver=old_head), "after query ref to old_head"
+
+
+def test_reference_version_old():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    versioned_rec.insert()
+    version = versioned_rec.version.id
+
+    # now update versioned_rec
+    old_head = versioned_rec.version.id
+    versioned_rec.description = "new desc"
+    versioned_rec.update()
+
+    # insert rec which references an old version of versioned_rec
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(ref_rt, str(versioned_rec.id) + "@" + old_head)
+    rec = rec.insert(sync=False)
+
+    assert rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=old_head)
+
+    test_rec = c.execute_query(
+        "FIND TestRec2 WHICH HAS A TestReferencedObject", unique=True)
+    assert test_rec.get_property(ref_rt).value == "{id}@{ver}".format(
+        id=str(versioned_rec.id), ver=old_head)
+
+
+def test_reference_no_version():
+    ref_rt = insertion("TestReferencedObject")
+    rt = insertion("TestRT")
+
+    versioned_rec = c.Record(name="TestRec1").add_parent(ref_rt)
+    versioned_rec.insert()
+    rec = c.Record(name="TestRec2").add_parent(rt)
+    rec.add_property(ref_rt, versioned_rec.id)
+    rec.insert()
+    assert rec.get_property(ref_rt).value == versioned_rec.id
+
+    test_rec = c.execute_query(
+        "FIND TestRec2 WHICH HAS A TestReferencedObject", unique=True)
+    assert test_rec.get_property(ref_rt).value == versioned_rec.id
+
+
+def test_reference_head_minus_in_separate_container():
+    ref_rt = insertion("TestRT")
+
+    rec1 = c.Record("TestRecord1-firstVersion").add_parent("TestRT")
+    rec1.description = "This is the first version."
+    rec1.insert()
+    v1 = rec1.version.id
+
+    rec1.name = "TestRecord1-secondVersion"
+    rec1.description = "This is the second version."
+    rec1.update()
+    v2 = rec1.version.id
+
+    rec1.name = "TestRecord1-thirdVersion"
+    rec1.description = "This is the third version."
+    rec1.update()
+    v3 = rec1.version.id
+
+    rec2 = c.Record("TestRecord2").add_parent("TestRT")
+    rec2.description = ("This record has a list of references to several "
+                        "versions of TestRecord1. The first references the "
+                        "record without specifying the version, the other "
+                        "each reference a different version of that record.")
+    rec2.add_property("TestRT", datatype=c.LIST("TestRT"),
+                      value=[rec1.id,
+                             str(rec1.id) + "@HEAD",
+                             str(rec1.id) + "@HEAD~1",
+                             str(rec1.id) + "@HEAD~2"])
+    rec2.insert()
+
+    test_rec = c.execute_query("FIND TestRecord2", unique=True)
+    assert test_rec.get_property("TestRT").value == [rec1.id,
+                                                     str(rec1.id) + "@" + v3,
+                                                     str(rec1.id) + "@" + v2,
+                                                     str(rec1.id) + "@" + v1]
+
+
+def test_properties_no_version():
+    c.Property("TestProperty", datatype=c.TEXT).insert()
+    c.RecordType("TestRT").add_property("TestProperty").insert()
+
+    rt = c.execute_query("FIND TestRT", unique=True)
+    p = rt.get_property("TestProperty")
+    assert p.version is None
+
+
+def test_update_name():
+    old_name = "TestRTOldName"
+    new_name = "TestRTNewName"
+
+    rt = insertion(old_name)
+    old_version = rt.version
+
+    assert len(c.execute_query("FIND RecordType {}".format(new_name))) == 0
+    rt2 = c.execute_query("FIND RecordType {}".format(old_name), unique=True)
+    assert rt2.version.id == rt.version.id
+    assert rt2.version == old_version
+    assert rt2.name == old_name
+
+    # do the update, run checks again
+    rt.name = new_name
+    rt.update()
+
+    assert rt.name == new_name
+    assert rt.version is not None
+    assert rt.version.id is not None
+    assert rt.version.date is not None
+    assert rt.version != old_version
+    assert rt.version.date != old_version.date
+    assert parse(rt.version.date) > parse(old_version.date)
+
+    assert len(c.execute_query("FIND RecordType {}".format(old_name))) == 0
+    rt2 = c.execute_query("FIND RecordType {}".format(new_name), unique=True)
+    assert rt2.version.id == rt.version.id
+    assert rt2.version == rt.version
+    assert rt2.name == new_name
+
+    # retrieve once again, via id
+    rt3 = c.Container().retrieve(query=str(rt.id), sync=False)[0]
+    assert rt3.version.id == rt.version.id
+    assert rt3.version == rt.version
+    assert rt3.name == new_name
+
+    # retrieve old version
+    rt_old = c.Container().retrieve(query=str(rt.id) +
+                                    "@" + old_version.id, sync=False)[0]
+    assert rt_old.version.id == old_version.id
+    assert rt_old.name == old_name
diff --git a/tox.ini b/tox.ini
index 4592bd284267db972fe2f3a6f4520ff125331dd0..babdf47e254755b64599e386c96c708bca2b043c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,6 +6,9 @@ setenv = PASSWORD_STORE_DIR = {env:HOME}/.password-store
 deps=pytest
     nose
     pytest-cov
+    python-dateutil
 commands_pre=pip install ../caosdb-pylib/
+    python --version
+    python -c "import caosdb; print(caosdb.version.version)"
 # Add "-x" to stop at first error.
 commands=pytest --cov=caosdb -vv {posargs}