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