diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 33312d11be1f95bb9b968e688b60133ad83921b8..f030fecb9abec503dcbcfc7dac5409a4291eade8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,17 +82,31 @@ test: stage: test image: $CI_REGISTRY_IMAGE_BASE script: + - echo $F_BRANCH + - echo $CAOSDB_TAG + - echo $CI_COMMIT_REF_NAME + - F_BRANCH=${F_BRANCH:-$CI_COMMIT_REF_NAME} + - echo $F_BRANCH + - echo $CAOSDB_TAG + - echo $CI_COMMIT_REF_NAME + - if ! echo "$F_BRANCH" | grep -c "^f-" ; then + F_BRANCH = dev ; + fi - if [[ "$CAOSDB_TAG" == "" ]]; then + if echo "$F_BRANCH" | grep -c "^f-" ; then + CAOSDB_TAG=f-feature-branch-pipeline_F_${F_BRANCH}-latest; + else CAOSDB_TAG=dev-latest; + fi; fi + # TODO default $CAOSDB_TAG to dev_F_$F_BRANCH-latest + - echo $F_BRANCH - echo $CAOSDB_TAG - #- KNOWN_TAGS=$(curl -u gitlab+deploy-token-ci-pull:$TOKEN_CI_PULL -X GET https://$CI_REGISTRY_INDISCALE/v2/caosdb/src/caosdb-deploy/tags/list) - # - echo $KNOWN_TAGS - # test if the caosdb registry knows our current tag - # - echo $KNOWN_TAGS | grep "$CAOSDB_TAG" - - time docker load < /image-cache/caosdb-pyint-testenv.tar || true - - time docker load < /image-cache/mariadb.tar || true - - time docker load < /image-cache/caosdb.tar || true + - echo $CI_COMMIT_REF_NAME + + - time docker load < /image-cache/caosdb-pyint-testenv-${CI_COMMIT_REF_NAME}.tar || true + - time docker load < /image-cache/mariadb-${F_BRANCH}.tar || true + - time docker load < /image-cache/caosdb-${F_BRANCH}.tar || true - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab+deploy-token-ci-pull -p $TOKEN_CI_PULL $CI_REGISTRY_INDISCALE - docker pull $CI_REGISTRY_IMAGE @@ -122,6 +136,18 @@ build-testenv: stage: setup script: - df -h + - command -v wget + - if [ -z "$PYLIB" ]; then + if echo "$CI_COMMIT_REF_NAME" | grep -c "^f-" ; then + echo "Check if pylib has branch $CI_COMMIT_REF_NAME" ; + if wget https://gitlab.com/api/v4/projects/13656973/repository/branches/${CI_COMMIT_REF_NAME} ; then + PYLIB=$CI_COMMIT_REF_NAME ; + fi; + fi; + fi; + - PYLIB=${PYLIB:-dev} + - echo $PYLIB + - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY # use here general latest or specific branch latest... - docker build @@ -129,7 +155,7 @@ build-testenv: --file .docker/Dockerfile -t $CI_REGISTRY_IMAGE . - docker push $CI_REGISTRY_IMAGE - - docker save $CI_REGISTRY_IMAGE > /image-cache/caosdb-pyint-testenv.tar + - docker save $CI_REGISTRY_IMAGE > /image-cache/caosdb-pyint-testenv-${CI_COMMIT_REF_NAME}.tar - cd .docker-base - docker build -t $CI_REGISTRY_IMAGE_BASE . diff --git a/CHANGELOG.md b/CHANGELOG.md index 0027b3fa813fbab623e2da696ef8cd38da47c784..5919bd25f0a537915c9b1ebba301f3e754f0f7d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed (for any bug fixes) - Tests for NaN Double Values (see https://gitlab.com/caosdb/caosdb-server/issues/41) +* Tests for name queries. [caosdb-server#51](https://gitlab.com/caosdb/caosdb-server/-/issues/51) ### Security (in case of vulnerabilities) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 37173ccd1040626b4ce6ebcb1103aa67e179dbad..dc862c4e1c358376a039a8a50d0863fca2d5a112 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -50,7 +50,8 @@ def setup(): def test_pass(): - if not h.get_config().has_option("Connection", "password_method") or not h.get_config().get("Connection", "password_method") == "pass": + if not h.get_config().has_option("Connection", "password_method") or not h.get_config( + ).get("Connection", "password_method") == "pass": skip() assert call(["pass", h.get_config().get("Connection", "password_identifier")]) == 0 diff --git a/tests/test_empty_text_value.py b/tests/test_empty_text_value.py index b5d6165b6ffe359e9f7394f342488f24917890d9..fe705d6deadc7ce57e0a12c3d5492c3ab9b7d87e 100644 --- a/tests/test_empty_text_value.py +++ b/tests/test_empty_text_value.py @@ -75,7 +75,7 @@ def test_null_value(): # value was stored correctly assert db.execute_query("FIND Record TestRT", - unique=True).get_property("TestProp").value == None + unique=True).get_property("TestProp").value is None # query language works with null value assert db.execute_query( "FIND TestRT WHERE TestProp IS NULL", unique=True).id == r1.id @@ -127,7 +127,7 @@ def test_null_list(): # null list was stored correctly assert db.execute_query("FIND Record TestRT", - unique=True).get_property("TestProp").value == None + unique=True).get_property("TestProp").value is None assert db.execute_query( "FIND TestRT WHERE TestProp IS NULL", unique=True).id == r1.id diff --git a/tests/test_file.py b/tests/test_file.py index fb8d6568a9341512cc7eccf154cf1c9443386815..4dbf4fd9a25e20378294c6f519154c1b25a16c54 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -36,7 +36,7 @@ from lxml import etree from pytest import raises from nose.tools import (assert_equal, assert_false, # @UnresolvedImport assert_is_not_none, assert_raises, assert_true, - nottest, with_setup) + nottest) from caosdb import Info from caosdb import administration as admin @@ -51,6 +51,7 @@ def setup_module(): def setup(): + teardown() with open("test.dat", "w") as upload_file: upload_file.write("hello world\n") os.makedirs("testfolder/subfolder") @@ -61,10 +62,9 @@ def setup(): def teardown(): - try: - execute_query("FIND ENTITY WHICH HAS AN ID >= 100").delete() - except Exception as e: - print(e) + d = execute_query("FIND ENTITY WHICH HAS AN ID >= 100") + if len(d) > 0: + d.delete() try: shutil.rmtree("testfolder") except Exception as e: @@ -79,7 +79,6 @@ def teardown(): print(e) -@with_setup(setup, teardown) def test_file_with_space(): file_ = models.File(name="TestFile", description="Testfile Desc", @@ -93,66 +92,51 @@ def test_file_with_space(): qfile.download("test2.dat") -@with_setup(setup, teardown) def test_pickup_file(): + d = models.DropOffBox() + d.sync() try: - d = models.DropOffBox() - d.sync() - try: - pickup_file = open(os.path.join(d.path, "testpickup.dat"), "w") - except BaseException: - print("drop off box not on this system.") - else: - pickup_file.write("hello world\n") - pickup_file.close() - file_ = models.File(name="PickupTestfile", - description="Pickup test file desc", - path="testfiles/pickuptestfile.dat" + - hex(randint(0, maxint)), - pickup="testpickup.dat") - file_.insert() - assert_is_not_none(file_.id) - finally: - try: - file_.delete() - except BaseException: - pass + pickup_file = open(os.path.join(d.path, "testpickup.dat"), "w") + except BaseException: + print("drop off box not on this system.") + else: + pickup_file.write("hello world\n") + pickup_file.close() + file_ = models.File(name="PickupTestfile", + description="Pickup test file desc", + path="testfiles/pickuptestfile.dat" + + hex(randint(0, maxint)), + pickup="testpickup.dat") + file_.insert() + assert_is_not_none(file_.id) -@with_setup(setup, teardown) def test_pickup_folder(): + # pickup_folder + d = models.DropOffBox() + d.sync() try: - # pickup_folder - d = models.DropOffBox() - d.sync() - try: - os.mkdir(d.path + "/testfolder") - except BaseException: - print("drop off box not on this system.") - else: - os.mkdir(d.path + "/testfolder/subfolder") - pickup_file = open(d.path + "/testfolder/testpickup1.dat", "w") - pickup_file.write("hello world\n") - pickup_file.close() - pickup_file = open( - d.path + "/testfolder/subfolder/testpickup2.dat", "w") - pickup_file.write("hello world\n") - pickup_file.close() - - file_ = models.File(name="PickupTestfolder", - description="Pickup test folder desc", - path="testfiles/pickuptestfolder" + - hex(randint(0, maxint)) + "/", - pickup="testfolder/") - file_.insert() - finally: - try: - file_.delete() - except BaseException: - pass + os.mkdir(d.path + "/testfolder") + except BaseException: + print("drop off box not on this system.") + else: + os.mkdir(d.path + "/testfolder/subfolder") + pickup_file = open(d.path + "/testfolder/testpickup1.dat", "w") + pickup_file.write("hello world\n") + pickup_file.close() + pickup_file = open( + d.path + "/testfolder/subfolder/testpickup2.dat", "w") + pickup_file.write("hello world\n") + pickup_file.close() + + file_ = models.File(name="PickupTestfolder", + description="Pickup test folder desc", + path="testfiles/pickuptestfolder" + + hex(randint(0, maxint)) + "/", + pickup="testfolder/") + file_.insert() -@with_setup(setup, teardown) def test_file4(): try: d = models.DropOffBox() @@ -187,7 +171,6 @@ def test_file4(): pass -@with_setup(setup, teardown) def test_upload_complete_folder(): file1_ = models.File(name="Testfile1", description="Testfile Desc", @@ -218,7 +201,6 @@ def test_upload_complete_folder(): c.delete() -@with_setup(setup, teardown) def test_file6(): try: # upload file to testfiles2/testfile... @@ -278,7 +260,6 @@ def test_file6(): pass -@with_setup(setup, teardown) def test_file7(): try: # upload file to testfiles2/testsub/testfile... @@ -335,7 +316,6 @@ def test_file7(): pass -@with_setup(setup, teardown) def test_consistency_file_was_modified(): try: @@ -401,7 +381,6 @@ def test_consistency_file_was_modified(): pass -@with_setup(setup, teardown) def test_consistency_file_does_not_exist(): try: with open("test.dat", "w") as upload_file: @@ -457,7 +436,6 @@ def test_consistency_file_does_not_exist(): pass -@with_setup(setup, teardown) def test_consistency_unknown_file(): c = runCheck(None, None) assert c.messages["Info", 0] is not None @@ -477,7 +455,6 @@ def test_consistency_unknown_file(): assert c.messages["Info", 0][0] == "File system is consistent." -@with_setup(setup, teardown) def test_insert_files_in_dir_error1(): c = models.Container() @@ -513,7 +490,6 @@ def test_insert_files_in_dir_error1(): assert_true(c.messages["Error", 0][0].startswith("Dir is not allowed")) -@with_setup(setup, teardown) def test_insert_files_in_dir_with_symlink(): path = get_config().get("IntegrationTests", "test_files.test_insert_files_in_dir.local") + "testfolder/" @@ -590,7 +566,6 @@ def test_insert_files_in_dir_with_symlink(): pass -@with_setup(None, teardown) def test_insert_files_in_dir(): """ test if files in directories can be inserted as symlinks via the InsertFilesInDir flag. This tests also verifies that the job can be @@ -690,7 +665,6 @@ def test_insert_files_in_dir(): pass -@with_setup(setup, teardown) def test_insert_files_in_dir_regex(): path = get_config().get("IntegrationTests", "test_files.test_insert_files_in_dir.local") + "testfolder/" @@ -733,7 +707,6 @@ def test_insert_files_in_dir_regex(): pass -@with_setup(setup, teardown) def test_thumbnails(): file_ = models.File(name="TestFile", description="Testfile Desc", diff --git a/tests/test_issues_mysqlbackend.py b/tests/test_issues_mysqlbackend.py index c276f6a7249036247a3b7e3764fa2721f9704318..6b61f0254401ca684feae66b3302898793ef4fe9 100644 --- a/tests/test_issues_mysqlbackend.py +++ b/tests/test_issues_mysqlbackend.py @@ -46,7 +46,7 @@ def teardown(): setup_module() -# ########################### Issue tests start here ########################### +# ########################### Issue tests start here ##################### @with_setup(setup, teardown) def test_issue_18(): diff --git a/tests/test_issues_server.py b/tests/test_issues_server.py index 88f9134068921aeffb12c5d7ebf6d15866a68937..2bc35a4eaed3ae883a2a416cfa41730574b7cb12 100644 --- a/tests/test_issues_server.py +++ b/tests/test_issues_server.py @@ -46,7 +46,7 @@ def teardown(): setup_module() -# ########################### Issue tests start here ########################### +# ########################### Issue tests start here ##################### @pytest.mark.xfail(reason="to be fixed in server repo") def test_issue_62(): diff --git a/tests/test_name_properties.py b/tests/test_name_properties.py index c88748ec291618659f0ce210d3ae2763226a31c6..e386a4e4b6974154f8fdbf5ee85513cef12cd39c 100644 --- a/tests/test_name_properties.py +++ b/tests/test_name_properties.py @@ -27,6 +27,7 @@ """ import caosdb as db +from pytest import raises from nose.tools import nottest, assert_true, assert_raises, assert_equal, with_setup, assert_is_not_none # @UnresolvedImport @@ -208,8 +209,44 @@ def test_recordtype_query(): "FIND TestRecordType", "FIND TestRT"]) +def test_query_name_property(): + """ Insert a Property which has a name parent. Add to Record and query the + record.""" + # test behavior without the name parent + db.RecordType("TestPerson").insert() + db.Property("TestGivenName", datatype=db.TEXT).insert() + rec = db.Record("TestJohnDoe").add_parent("TestPerson") + rec.add_property("TestGivenName", "John") + rec.insert() + + assert db.execute_query("FIND TestPerson WITH TestGivenName='John'", + unique=True).id == rec.id + with raises(db.EntityDoesNotExistError): + assert db.execute_query("FIND John", unique=True) + + teardown() + + # test behavior WITH the name parent + db.RecordType("TestPerson").insert() + db.Property("TestGivenName").add_parent("name").insert() + rec = db.Record("TestJohnDoe").add_parent("TestPerson") + rec.add_property("TestGivenName", "John") + rec.insert() + + assert db.execute_query("FIND TestPerson WITH TestGivenName='John'", + unique=True).id == rec.id + assert db.execute_query("FIND John", + unique=True).id == rec.id + + @with_setup(setup, teardown) -def test_query_with_pov(): +def test_query_property_with_pov(): + """ Insert a Record with a property which can be searched using two + different names. + + The TestRating is the primary name of the property while TestRating is a + Synonym. + """ db.RecordType(name="TestExperiment").insert() db.Property( name="TestSynonym", @@ -225,6 +262,7 @@ def test_query_with_pov(): name="TestExperiment").add_property( name="TestRating", value=4).insert() + assert_equal( db.execute_query( "FIND TestExperiment WHICH HAS A TestRating=4", @@ -249,6 +287,11 @@ def test_query_with_pov(): @with_setup(setup, teardown) def test_query_with_reference(): + """ Insert a Record with two names. Both work in a reference query. + + The record has a primary name ("TestJohnDoe") and a property + `TestGivenName` (="John") with also works with the query. + """ db.RecordType(name="TestExperiment").insert() db.RecordType(name="TestPerson").insert() db.Property( @@ -280,6 +323,11 @@ def test_query_with_reference(): @with_setup(setup, teardown) def test_query_with_back_reference(): + """ Insert a Record with two names. Both work in a back-ref query. + + The record has a primary name ("TestMeasurement") and a property + `TestSynomym` (="TestObservation") with also works with the query. + """ db.RecordType(name="TestPerson").insert() db.Property( name="TestSynonym", diff --git a/tests/test_query.py b/tests/test_query.py index 301b57a2655d9c284fecacf25fb00b4037fb9d39..5a28d20309ded0b725dea84904bcff70de76a6eb 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -29,10 +29,9 @@ import caosdb as h -# @UnresolvedImport -from nose.tools import assert_true, assert_equal, assert_is_not_none, with_setup +from nose.tools import assert_true, assert_equal, assert_is_not_none from caosdb.connection.connection import get_connection -from lxml import etree # @UnresolvedImport +from lxml import etree def setup(): @@ -43,10 +42,7 @@ def setup(): def teardown(): - try: - h.execute_query("FIND Test*").delete() - except Exception as e: - print(e) + setup() try: import os os.remove("test.dat") @@ -54,7 +50,6 @@ def teardown(): print(e) -@with_setup(setup, teardown) def test_query_with_reference_by_parent(): h.RecordType("TestExperiment").insert() h.RecordType("TestProtocolLog").insert() @@ -113,7 +108,6 @@ def test_query_with_reference_by_parent(): "FIND TestExperiment.TestProtocolLog1"))) -@with_setup(setup, teardown) def test_query_with_domains(): person = h.RecordType("TestPerson").insert() h.Property("TestFirstName", datatype=h.TEXT).insert() @@ -156,7 +150,6 @@ def test_query_with_domains(): exp.id) -@with_setup(setup, teardown) def test_query1(): p = ( h.Property( @@ -174,7 +167,6 @@ def test_query1(): unique=True).id) -@with_setup(setup, teardown) def test_query2(): # create testfile f = open("test.dat", "w") @@ -209,7 +201,6 @@ def test_query3(): assert_equal(3, len(xml)) -@with_setup(setup, teardown) def test_conjunction(): rt = h.RecordType(name="TestConjunctionTest").insert() assert_true(rt.is_valid()) @@ -300,7 +291,6 @@ def test_conjunction(): "FIND RECORD . TestConjunctionTestPropertyA=1 AND TestConjunctionTestPropertyB=0"))) -@with_setup(setup, teardown) def test_disjunction(): rt = h.RecordType(name="TestDisjunctionTest").insert() assert_true(rt.is_valid()) @@ -385,7 +375,6 @@ def test_disjunction(): "FIND TestDisjunctionTest . TestDisjunctionTestPropertyA=1 OR ( TestDisjunctionTestPropertyB=0 AND TestDisjunctionTestPropertyA=1)"))) -@with_setup(setup, teardown) def test_greatest(): pAB = h.Property(name="TestPropertyAB", datatype=h.DOUBLE).insert() assert_true(pAB.is_valid()) @@ -572,7 +561,6 @@ def test_greatest(): assert_equal(c[1].id, rec6.id) -@with_setup(setup, teardown) def test_wildcard_values(): ptext = h.Property(name="TestTextProperty", datatype=h.TEXT).insert() rt = h.RecordType( @@ -860,7 +848,6 @@ def test_stored_at_wildcards(): assert c.get_entity_by_id(file8.id) is not None -@with_setup(setup, teardown) def test_int(): pint = h.Property(name="TestIntegerProperty", datatype=h.INTEGER).insert() pdouble = h.Property(name="TestDoubleProperty", datatype=h.DOUBLE).insert() @@ -939,7 +926,6 @@ def test_int(): "FIND TestRecordType WITH TestDoubleProperty<=50"))) -@with_setup(setup, teardown) def test_query_benchmark(): h.Property("TestProperty", datatype=h.TEXT).insert() @@ -970,7 +956,6 @@ def test_like_query(): h.execute_query("FIND box with number = '7'") -@with_setup(setup, teardown) def test_backref_like(): h.RecordType("TestData").insert() other = h.Record("other").add_parent("TestData").insert() @@ -981,3 +966,38 @@ def test_backref_like(): h.execute_query("FIND ENTITY WHICH IS REFERENCED BY *m*", unique=True) h.execute_query("FIND ENTITY WHICH IS REFERENCED BY s*", unique=True) h.execute_query("FIND ENTITY WHICH IS REFERENCED BY *e", unique=True) + + +def test_query_by_name(): + rt = h.RecordType("TestRT").insert() + rec1 = h.Record("TestRec1").add_parent("TestRT").insert() + rec2 = h.Record("TestRec2").add_parent("TestRT").insert() + + assert len(h.execute_query("FIND TestRT")) == 3 + assert len(h.execute_query("FIND RECORD TestRT")) == 2 + assert len(h.execute_query("FIND RECORDTYPE TestRT")) == 1 + + # find rt via name + assert h.execute_query("FIND RECORDTYPE WITH name = 'TestRT'", + unique=True).id == rt.id + assert h.execute_query("FIND RECORDTYPE WITH NAME = 'TestRT'", + unique=True).id == rt.id + assert h.execute_query("FIND RECORDTYPE WITH NAme = 'TestRT'", + unique=True).id == rt.id + assert h.execute_query("FIND TestRT WITH NAme = 'TestRT'", + unique=True).id == rt.id + assert h.execute_query("FIND RECORDTYPE WITH name LIKE Test*", + unique=True).id == rt.id + + # find rec1 via name + assert h.execute_query("FIND TestRT WITH name = 'TestRec1'", + unique=True).id == rec1.id + assert h.execute_query("FIND RECORD TestRT WITH name = 'TestRec1'", + unique=True).id == rec1.id + assert h.execute_query("FIND RECORD TestRT WITH name LIKE '*Rec1'", + unique=True).id == rec1.id + + # find both records via name + assert len(h.execute_query("FIND TestRT WITH name LIKE 'TestRec*'")) == 2 + assert len(h.execute_query("FIND TestRT WITH name LIKE '*Rec*'")) == 2 + assert len(h.execute_query("FIND ENTITY WITH name LIKE 'TestRec*'")) == 2 diff --git a/tests/test_server_side_scripting.py b/tests/test_server_side_scripting.py index 7a5e595786349c372b72c6e51c47bc31d746ba87..672f50a4c39378a05460e6f93f50568fb6df3e05 100644 --- a/tests/test_server_side_scripting.py +++ b/tests/test_server_side_scripting.py @@ -102,7 +102,7 @@ def test_call_ok(): xml = etree.parse(r) assert xml.xpath("/Response/script/call")[0].text == "ok" assert xml.xpath("/Response/script/stdout")[0].text == "ok" - assert xml.xpath("/Response/script/stderr")[0].text == None + assert xml.xpath("/Response/script/stderr")[0].text is None assert xml.xpath("/Response/script/@code")[0] == "0" @@ -113,7 +113,7 @@ def test_call_err(): xml = etree.parse(r) assert xml.xpath("/Response/script/@code")[0] == "1" assert xml.xpath("/Response/script/call")[0].text == "err" - assert xml.xpath("/Response/script/stdout")[0].text == None + assert xml.xpath("/Response/script/stdout")[0].text is None assert xml.xpath("/Response/script/stderr")[0].text == "err"