From 86ca7fe241b54455fa6f4a4670e765f9a1d1d770 Mon Sep 17 00:00:00 2001
From: Alexander Schlemmer <alexander@mail-schlemmer.de>
Date: Tue, 18 Jan 2022 13:55:54 +0100
Subject: [PATCH 1/7] ENH: new module which allows to register integration
 tests in caosdb instances

---
 src/caosadvancedtools/testutils/__init__.py   |   0
 .../testutils/register_tests.py               | 124 ++++++++++++++++++
 .../testutils/test_model.yml                  |   6 +
 src/caosadvancedtools/testutils/utils.py      |  31 +++++
 4 files changed, 161 insertions(+)
 create mode 100644 src/caosadvancedtools/testutils/__init__.py
 create mode 100644 src/caosadvancedtools/testutils/register_tests.py
 create mode 100644 src/caosadvancedtools/testutils/test_model.yml
 create mode 100644 src/caosadvancedtools/testutils/utils.py

diff --git a/src/caosadvancedtools/testutils/__init__.py b/src/caosadvancedtools/testutils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
new file mode 100644
index 00000000..928d54d3
--- /dev/null
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2022 Alexander Schlemmer <alexander.schlemmer@ds.mpg.de>
+#
+# 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
+
+
+import caosdb as db
+import pytest
+from .utils import rfp
+from caosadvancedtools.models.parser import parse_model_from_yaml
+from caosdb import EmptyUniqueQueryError
+
+"""
+This module implements a registration procedure for integration tests which need
+a running CaosDB instance or a docker container.
+
+It ensures that tests do not accidently overwrite data in real CaosDB instances,
+as it checks whether the running CaosDB instance is actually the correct one, that
+should be used for these tests.
+
+The test files have to define a global variable TEST_KEY which must be unique for each test.
+# TEST_KEY = "ABCDE"
+
+The test procedure (invoked by pytest) checks whether a registration information record exists
+in the running CaosDB or otherwise:
+- offers to register this test in the currently running database ONLY if this is empty.
+- fails otherwise with a RuntimeError
+
+NOTE: you probably need to use pytest with the -s option to be able to
+      register the test interactively.
+
+This module is intended to be used with pytest.
+
+There is a pytest fixture "clear_database" that performs the above mentioned checks and clears
+the database in case of success.
+"""
+
+global TEST_KEY
+# Check whether the key is defined
+try:
+    TEST_KEY
+except NameError:
+    raise RuntimeError("TEST_KEY is not defined.")
+
+def not_test_record(r: db.Record):
+    global TEST_KEY
+    if r.name == "PyTestInfo" or r.name == "TestIdentification":
+        return False
+    if r.get_property("TestIdentification") is not None:
+        if (r.get_property("TestIdentification").value ==
+            TEST_KEY):
+            return False
+    return True
+
+def register_test():
+    res = db.execute_query("COUNT Entity")
+    if not isinstance(res, int):
+        raise RuntimeError("Response from server for Info could not be interpreted.")
+    if res > 0:
+        raise RuntimeError("This instance of CaosDB contains entities already."
+                           "It must be empty in order to gegister a new test.")
+
+    print("Current host of CaosDB instance is: {}".format(
+        db.connection.connection.get_connection()._delegate_connection.setup_fields["host"]))
+    answer = input("This method will register your current test with key {} with the currently"
+                   " running instance of CaosDB. Do you want to continue (y/N)?".format(
+                       TEST_KEY))
+    if answer != "y":
+        raise RuntimeError("Test registration aborted by user.")
+    
+    model = parse_model_from_yaml(rfp("test_model.yml"))
+    model.sync_data_model(noquestion=True, verbose=False)
+
+    r = db.Record()
+    r.add_parent("PyTestInfo")
+    r.add_property("TestIdentification", TEST_KEY)
+    r.insert()
+
+@pytest.fixture
+def clear_database():
+    """First remove Records, then RecordTypes, then Properties, finally
+    files. Since there may be no entities, execute all deletions
+    without raising errors.
+
+    """
+    global TEST_KEY
+    try:
+        test_record = db.execute_query("FIND Record PyTestInfo", unique=True)
+    except EmptyUniqueQueryError:
+        answer = input("Do you want to register this instance of CaosDB"
+                       " with the current test? Do you want to continue (y/N)?")
+        if answer == "y":
+            register_test()
+            raise RuntimeError("Test has been registered. Please rerun tests.")
+        else:
+            raise RuntimeError("The database has not been setup for this test.")
+    if test_record.get_property("TestIdentification").value != TEST_KEY:
+        raise RuntimeError("The database has been setup for a different test.")
+    
+    c = db.execute_query("FIND Record")
+    c.extend(db.execute_query("FIND RecordType"))
+    c.extend(db.execute_query("FIND Property"))
+    c.extend(db.execute_query("FIND File"))
+
+    c = db.Container().extend([r for r in c if not_test_record(r)])
+    c.delete(raise_exception_on_error=False)
diff --git a/src/caosadvancedtools/testutils/test_model.yml b/src/caosadvancedtools/testutils/test_model.yml
new file mode 100644
index 00000000..2537f85e
--- /dev/null
+++ b/src/caosadvancedtools/testutils/test_model.yml
@@ -0,0 +1,6 @@
+TestIdentification:
+    description: "This is a unique key which should be only known to the pytest file that is used to run tests within this instance of CaosDB."
+    datatype: TEXT
+PyTestInfo:
+    obligatory_properties:
+        TestIdentification:
\ No newline at end of file
diff --git a/src/caosadvancedtools/testutils/utils.py b/src/caosadvancedtools/testutils/utils.py
new file mode 100644
index 00000000..7ff121b3
--- /dev/null
+++ b/src/caosadvancedtools/testutils/utils.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2022 Alexander Schlemmer <alexander.schlemmer@ds.mpg.de>
+#
+# 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
+
+import os
+
+def rfp(*pathcomponents):
+    """
+    Return full path.
+    Shorthand convenience function.
+    """
+    return os.path.join(os.path.dirname(__file__), *pathcomponents)
-- 
GitLab


From daf7db904b68f8bc258728348d90f90d05b4da77 Mon Sep 17 00:00:00 2001
From: Alexander Schlemmer <alexander@mail-schlemmer.de>
Date: Tue, 18 Jan 2022 14:05:54 +0100
Subject: [PATCH 2/7] DOC: updated changelog

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44629bd9..8f68822c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - CFood that creates a Record for each line in a csv file
 - `generic_analysis.py` allows to easily call scripts to perform analyses in
   server side scripting [EXPERIMENTAL]
+- New module `register_tests.py` in `caosadvancedtools/testutils` that allows
+  registering integration tests in their CaosDB instance used for testing.
 
 ### Changed ###
 
-- 
GitLab


From fe111723b52f17f118135a60f3ae109a0975ef5c Mon Sep 17 00:00:00 2001
From: Alexander Schlemmer <alexander@mail-schlemmer.de>
Date: Wed, 19 Jan 2022 16:51:34 +0100
Subject: [PATCH 3/7] FIX: refactored usage of global variables and rfp
 function

---
 src/caosadvancedtools/testutils/__init__.py   |  2 ++
 .../testutils/register_tests.py               | 25 ++++++++++-----
 src/caosadvancedtools/testutils/utils.py      | 31 -------------------
 3 files changed, 20 insertions(+), 38 deletions(-)
 delete mode 100644 src/caosadvancedtools/testutils/utils.py

diff --git a/src/caosadvancedtools/testutils/__init__.py b/src/caosadvancedtools/testutils/__init__.py
index e69de29b..a3f004ae 100644
--- a/src/caosadvancedtools/testutils/__init__.py
+++ b/src/caosadvancedtools/testutils/__init__.py
@@ -0,0 +1,2 @@
+from .register_tests import clear_database
+from .register_tests import set_test_key
diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
index 928d54d3..765b06e3 100644
--- a/src/caosadvancedtools/testutils/register_tests.py
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -24,9 +24,9 @@
 
 import caosdb as db
 import pytest
-from .utils import rfp
 from caosadvancedtools.models.parser import parse_model_from_yaml
 from caosdb import EmptyUniqueQueryError
+import os
 
 """
 This module implements a registration procedure for integration tests which need
@@ -53,12 +53,21 @@ There is a pytest fixture "clear_database" that performs the above mentioned che
 the database in case of success.
 """
 
-global TEST_KEY
-# Check whether the key is defined
-try:
-    TEST_KEY
-except NameError:
-    raise RuntimeError("TEST_KEY is not defined.")
+TEST_KEY = None
+
+
+def rfp(*pathcomponents):
+    """
+    Return full path.
+    Shorthand convenience function.
+    """
+    return os.path.join(os.path.dirname(__file__), *pathcomponents)
+
+
+def set_test_key(KEY):
+    global TEST_KEY
+    TEST_KEY = KEY
+
 
 def not_test_record(r: db.Record):
     global TEST_KEY
@@ -102,6 +111,8 @@ def clear_database():
 
     """
     global TEST_KEY
+    if TEST_KEY is None:
+        raise RuntimeError("TEST_KEY is not defined.")
     try:
         test_record = db.execute_query("FIND Record PyTestInfo", unique=True)
     except EmptyUniqueQueryError:
diff --git a/src/caosadvancedtools/testutils/utils.py b/src/caosadvancedtools/testutils/utils.py
deleted file mode 100644
index 7ff121b3..00000000
--- a/src/caosadvancedtools/testutils/utils.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-#
-# ** header v3.0
-# This file is a part of the CaosDB Project.
-#
-# Copyright (C) 2022 Alexander Schlemmer <alexander.schlemmer@ds.mpg.de>
-#
-# 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
-
-import os
-
-def rfp(*pathcomponents):
-    """
-    Return full path.
-    Shorthand convenience function.
-    """
-    return os.path.join(os.path.dirname(__file__), *pathcomponents)
-- 
GitLab


From 5b29ad905429ea5a8ceb9bf17eeca059e2deb9ea Mon Sep 17 00:00:00 2001
From: Alexander Schlemmer <alexander@mail-schlemmer.de>
Date: Mon, 4 Apr 2022 09:54:02 +0200
Subject: [PATCH 4/7] STY: fixed style

---
 src/caosadvancedtools/testutils/register_tests.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
index 765b06e3..cad1d10c 100644
--- a/src/caosadvancedtools/testutils/register_tests.py
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -75,10 +75,11 @@ def not_test_record(r: db.Record):
         return False
     if r.get_property("TestIdentification") is not None:
         if (r.get_property("TestIdentification").value ==
-            TEST_KEY):
+                TEST_KEY):
             return False
     return True
 
+
 def register_test():
     res = db.execute_query("COUNT Entity")
     if not isinstance(res, int):
@@ -94,7 +95,7 @@ def register_test():
                        TEST_KEY))
     if answer != "y":
         raise RuntimeError("Test registration aborted by user.")
-    
+
     model = parse_model_from_yaml(rfp("test_model.yml"))
     model.sync_data_model(noquestion=True, verbose=False)
 
@@ -103,6 +104,7 @@ def register_test():
     r.add_property("TestIdentification", TEST_KEY)
     r.insert()
 
+
 @pytest.fixture
 def clear_database():
     """First remove Records, then RecordTypes, then Properties, finally
@@ -125,7 +127,7 @@ def clear_database():
             raise RuntimeError("The database has not been setup for this test.")
     if test_record.get_property("TestIdentification").value != TEST_KEY:
         raise RuntimeError("The database has been setup for a different test.")
-    
+
     c = db.execute_query("FIND Record")
     c.extend(db.execute_query("FIND RecordType"))
     c.extend(db.execute_query("FIND Property"))
-- 
GitLab


From ceeb48de9b814de42c71c80bf6a60736c1142c62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com>
Date: Thu, 5 May 2022 15:11:23 +0200
Subject: [PATCH 5/7] MAINT: minor refactoring and documentation

---
 .../testutils/register_tests.py               | 37 ++++++++-----------
 .../testutils/test_model.yml                  |  7 ++--
 2 files changed, 18 insertions(+), 26 deletions(-)

diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
index cad1d10c..c9ee99b9 100644
--- a/src/caosadvancedtools/testutils/register_tests.py
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -30,7 +30,7 @@ import os
 
 """
 This module implements a registration procedure for integration tests which need
-a running CaosDB instance or a docker container.
+a running CaosDB instance.
 
 It ensures that tests do not accidently overwrite data in real CaosDB instances,
 as it checks whether the running CaosDB instance is actually the correct one, that
@@ -56,28 +56,21 @@ the database in case of success.
 TEST_KEY = None
 
 
-def rfp(*pathcomponents):
-    """
-    Return full path.
-    Shorthand convenience function.
-    """
-    return os.path.join(os.path.dirname(__file__), *pathcomponents)
-
-
 def set_test_key(KEY):
     global TEST_KEY
     TEST_KEY = KEY
 
 
-def not_test_record(r: db.Record):
+def belongs_to_test_identification(r: db.Record):
     global TEST_KEY
-    if r.name == "PyTestInfo" or r.name == "TestIdentification":
-        return False
-    if r.get_property("TestIdentification") is not None:
-        if (r.get_property("TestIdentification").value ==
-                TEST_KEY):
-            return False
-    return True
+    # RecordType and Property
+    if r.name == "PyTestInfo" or r.name == "testIdentification":
+        return True
+    if (
+            r.get_property("testIdentification") is not None and
+            r.get_property("testIdentification").value == TEST_KEY):
+        return True
+    return False
 
 
 def register_test():
@@ -101,16 +94,16 @@ def register_test():
 
     r = db.Record()
     r.add_parent("PyTestInfo")
-    r.add_property("TestIdentification", TEST_KEY)
+    r.add_property("testIdentification", TEST_KEY)
     r.insert()
 
 
 @pytest.fixture
 def clear_database():
-    """First remove Records, then RecordTypes, then Properties, finally
-    files. Since there may be no entities, execute all deletions
-    without raising errors.
+    """Remove Records, RecordTypes, Properties, and Files ONLY IF the CaosDB server the current
+    connection points to was registered with the appropriate key.
 
+    PyTestInfo Records and the corresponding RecordType and Property are preserved.
     """
     global TEST_KEY
     if TEST_KEY is None:
@@ -125,7 +118,7 @@ def clear_database():
             raise RuntimeError("Test has been registered. Please rerun tests.")
         else:
             raise RuntimeError("The database has not been setup for this test.")
-    if test_record.get_property("TestIdentification").value != TEST_KEY:
+    if test_record.get_property("testIdentification").value != TEST_KEY:
         raise RuntimeError("The database has been setup for a different test.")
 
     c = db.execute_query("FIND Record")
diff --git a/src/caosadvancedtools/testutils/test_model.yml b/src/caosadvancedtools/testutils/test_model.yml
index 2537f85e..2f00c873 100644
--- a/src/caosadvancedtools/testutils/test_model.yml
+++ b/src/caosadvancedtools/testutils/test_model.yml
@@ -1,6 +1,5 @@
-TestIdentification:
-    description: "This is a unique key which should be only known to the pytest file that is used to run tests within this instance of CaosDB."
-    datatype: TEXT
 PyTestInfo:
     obligatory_properties:
-        TestIdentification:
\ No newline at end of file
+        testIdentification:
+            description: "This is a unique key which should be only known to the pytest file that is used to run tests within this instance of CaosDB."
+            datatype: TEXT
-- 
GitLab


From c371bf3a41ac817a5dc69faac6023652f90d07c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com>
Date: Thu, 5 May 2022 15:16:30 +0200
Subject: [PATCH 6/7] resources

---
 setup.py                                          | 1 +
 src/caosadvancedtools/testutils/register_tests.py | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 929613de..2bf268e0 100755
--- a/setup.py
+++ b/setup.py
@@ -157,6 +157,7 @@ def setup_package():
         install_requires=["caosdb>=0.7.0",
                           "jsonschema>=4.4.0",
                           "numpy>=1.17.3",
+                          "importlib-resources",
                           "openpyxl>=3.0.0",
                           "pandas>=1.2.0",
                           "xlrd>=2.0",
diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
index c9ee99b9..48ce789d 100644
--- a/src/caosadvancedtools/testutils/register_tests.py
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -27,6 +27,7 @@ import pytest
 from caosadvancedtools.models.parser import parse_model_from_yaml
 from caosdb import EmptyUniqueQueryError
 import os
+from importlib_resources import files
 
 """
 This module implements a registration procedure for integration tests which need
@@ -89,7 +90,7 @@ def register_test():
     if answer != "y":
         raise RuntimeError("Test registration aborted by user.")
 
-    model = parse_model_from_yaml(rfp("test_model.yml"))
+    model = parse_model_from_yaml(files("caosadvancedtools.testutils").joinpath("test_model.yml"))
     model.sync_data_model(noquestion=True, verbose=False)
 
     r = db.Record()
-- 
GitLab


From 0d5502df490b0d8eaabaa87d52186c798cd782bd Mon Sep 17 00:00:00 2001
From: Alexander Schlemmer <alexander@mail-schlemmer.de>
Date: Tue, 24 May 2022 12:51:05 +0200
Subject: [PATCH 7/7] FIX: added missing function

---
 src/caosadvancedtools/testutils/register_tests.py | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/caosadvancedtools/testutils/register_tests.py b/src/caosadvancedtools/testutils/register_tests.py
index 48ce789d..298b8bfa 100644
--- a/src/caosadvancedtools/testutils/register_tests.py
+++ b/src/caosadvancedtools/testutils/register_tests.py
@@ -62,6 +62,17 @@ def set_test_key(KEY):
     TEST_KEY = KEY
 
 
+def not_test_record(r: db.Record):
+    global TEST_KEY
+    if r.name == "PyTestInfo" or r.name == "TestIdentification":
+        return False
+    if r.get_property("TestIdentification") is not None:
+        if (r.get_property("TestIdentification").value ==
+                TEST_KEY):
+            return False
+    return True
+
+
 def belongs_to_test_identification(r: db.Record):
     global TEST_KEY
     # RecordType and Property
-- 
GitLab