# -*- coding: utf-8 -*- # This file is a part of the CaosDB Project. # # Copyright (c) 2020 - 2024 IndiScale GmbH <info@indiscale.com> # Copyright (c) 2022 Daniel Hornung <d.hornung@indiscale.com> # Copyright (c) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com> # Copyright (c) 2021 - 2024 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/>. """Tests for issues on gitlab.com, project linkahead-server.""" import math import os import tempfile import time import linkahead as db import pytest from linkahead import administration as admin from linkahead.exceptions import (TransactionError, HTTPClientError, HTTPURITooLongError) CURATOR_ROLE = "curator" def setup_module(): db.configure_connection() try: db.execute_query("FIND ENTITY WITH ID > 99").delete() except Exception as delete_exc: print(delete_exc) try: admin._delete_user("TestUser") except Exception as delete_exc: print(delete_exc) try: admin._delete_role(CURATOR_ROLE) except Exception as delete_exc: print(delete_exc) def setup_function(function): """No setup required.""" setup_module() def teardown_function(function): """Deleting entities again.""" setup_module() # ########################### Issue tests start here ##################### def test_issue_39(): """Query Language Bug - quotes around year Test for https://gitlab.com/caosdb/caosdb-server/-/issues/39 """ date_str = "2020-01-01" prop_name = "Test_Date" db.Property(name=prop_name, datatype=db.DATETIME).insert() db.RecordType(name="Test_Type").add_property(name=prop_name).insert() db.Record(name="Test_Record").add_parent(name="Test_Type").add_property( name=prop_name, value=date_str).insert() # Quotes around years should work in the query ent = db.execute_query("FIND entity WITH A Test_Date IN \"2020\"", unique=True) assert ent.get_property(prop_name).value == date_str # This should raise a transaction error with pytest.raises(TransactionError): db.execute_query("FIND entity WITH A Test_Date IN \"abcd\"") @pytest.mark.xfail(reason="to be fixed in server repo") def test_issue_62(): """datatype is not changed when recordtype name changes Tests for https://gitlab.com/caosdb/caosdb-server/-/issues/62 """ db.RecordType(name="Test_RTA").insert() db.Property(name="Test_Prop", datatype="Test_RTA").insert() db.Record(name="Test_Record").add_parent( name="Test_RTA").add_property(name="Test_Prop").insert() # rename Test_RTA to Test_RTB rtb = db.execute_query("FIND RecordType Test_RTA", unique=True) rtb.name = "Test_RTB" rtb.update() # renaming has to be reflected in Test_Record and Test_Prop rec = db.execute_query("FIND Record Test_Record", unique=True) assert rec.parents[0].name == rtb.name assert rec.get_property("Test_Prop").datatype == rtb.name prop = db.execute_query("FIND Property Test_Prop", unique=True) assert prop.datatype == rtb.name # fails; datatype not updated # Can't use Test_RTA as datatype anymore prop2 = db.Property(name="Test_Prop2", datatype="Test_RTA") with pytest.raises(TransactionError) as exc: prop2.insert() assert "Unknown data type." in str(exc.value) def test_issue_85_a(): """SQLIntegrityConstraintViolationException for special inheritance patterns. Tests for https://gitlab.com/caosdb/caosdb-server/-/issues/85 """ A = db.RecordType(name="A") B = db.RecordType(name="B") C = db.RecordType(name="C") B.add_parent(A) # This order is important for the test to fail. C.add_parent(B) C.add_parent(C) C.add_parent(A) c = db.Container() # c.extend([C, B, A]) # worked before #86 was fixed # c.extend([C, A, B]) # worked before #86 was fixed c.extend([B, C, A]) # insert() failed before #86 was fixed c.insert() # Raised java.sql.SQLIntegrityConstraintViolationException: # # Duplicate entry '12345-12346-12345' for key 'PRIMARY' def test_issue_85_b(): """SQLIntegrityConstraintViolationException for special inheritance patterns. Tests for https://gitlab.com/caosdb/caosdb-server/-/issues/85 """ A = db.RecordType(name="A") B = db.RecordType(name="B") C = db.RecordType(name="C") A.insert() B.insert() C.insert() B.add_parent(A) B.update() C.add_parent(B) C.update() C.add_parent(C) C.update() C.add_parent(A) C.update() # Failed at this step @pytest.mark.local_server def test_issue_99(): """Checksum updating failed with versioning enabled. """ # Using files in extroot, because this allows us to update the file # content from the outside. local_dir = os.path.join(db.get_config().get("IntegrationTests", "test_files.test_insert_files_in_dir.local"), "test_issue_99") docker_dir = os.path.join(db.get_config().get("IntegrationTests", "test_files.test_insert_files_in_dir.server"), "test_issue_99") os.makedirs(local_dir, exist_ok=True) with tempfile.NamedTemporaryFile(dir=local_dir) as file_99: # Create File entity in CaosDB file_99.write("test 99\n".encode()) os.fchmod(file_99.fileno(), 0o744) # make the file world readable cont = db.Container() cont.insert(unique=False, raise_exception_on_error=False, flags={"InsertFilesInDir": docker_dir}) dbfile = cont[0] # Checksum should exist after a short time time.sleep(1) dbfile.retrieve() assert dbfile.checksum is not None def test_issue_110(): """query ignores ID: FIND MusicalInstrument which is referenced by Analysis with ID=124 """ cont = db.Container() A = db.RecordType(name="TypeA") B = db.RecordType(name="TypeB") prop = db.Property(name="prop_ba", datatype=db.REFERENCE) # Referenced Records a1 = db.Record().add_parent(A) a2 = db.Record().add_parent(A) # Referencing Records b1 = db.Record().add_parent(B).add_property(prop, value=a1) b2 = db.Record().add_parent(B).add_property(prop, value=a2) cont.extend([A, B, prop, a1, a2, b1, b2]) cont.insert() id_b1 = b1.id query = "FIND TypeA WHICH IS REFERENCED BY TypeB WITH ID={}".format(id_b1) print(query) result = db.execute_query(query) print(result) assert len(result) == 1 print(result[0]) print(a1) assert result[0].id == a1.id def test_issue_120(): """Editing entities that were created with a no longer existing user leads to a server error. The server should throw an error when CHECK_ENTITY_ACL_ROLES_MODE=MUST, otherwise a warning. """ # insert an entity entity = db.RecordType("TestRT").insert(flags={"ACL": None}) db.administration.set_server_property("CHECK_ENTITY_ACL_ROLES_MODE", "SHOULD") # update with non-existing user, realm and role entity.deny( realm="CaosDB", username="NON_EXISTING_USER", permission="USE:AS_REFERENCE") entity.update(flags={"ACL": None}) assert entity.messages["Warning", 1104][0] == "User role does not exist." entity.deny( realm="NON_EXISTING_REALM", username="NON_EXISTING_USER", permission="USE:AS_REFERENCE") entity.update(flags={"ACL": None}) assert entity.messages["Warning", 1104][0] == "User role does not exist." entity.deny( role="ALSO_NON_EXISTING_ROLE", permission="USE:AS_REFERENCE") entity.update(flags={"ACL": None}) assert entity.messages["Warning", 1104][0] == "User role does not exist." def test_issue_134(): """multiple white space characters after `FROM`""" db.execute_query("SELECT pname FROM ename") def test_issue_131(): """white space before unit with strange character""" rt = db.RecordType(name="TestType").insert() prop = db.Property(name="TestProp", datatype=db.INTEGER, unit="€").insert() rec = db.Record(name="TestRecord").add_property( name=prop.name, value=101, unit="€") rec.add_parent(rt) rec.insert() result_ids = [ent.id for ent in db.execute_query( "FIND Entity WITH {} > 100 €".format(prop.name))] assert rec.id in result_ids result_ids = [ent.id for ent in db.execute_query( "FIND Entity WITH {} > 100.5 €".format(prop.name))] assert rec.id in result_ids def test_issue_154_no_versioning(): """ FIND MusicalInstrument WITH Manufacturer = "Antonio Stradivari" and FIND MusicalInstrument WITH Manufacturer != "Antonio Stradivari" """ rt_man = db.RecordType("Manufacturer") rt_inst = db.RecordType("MusicalInstrument").add_property(rt_man) rec_man = db.Record("Antonio Stradivari").add_parent("Manufacturer") rec_man2 = db.Record("The other guy").add_parent("Manufacturer") rec_inst = db.Record("Violin").add_parent( "MusicalInstrument").add_property("Manufacturer", rec_man) rec_inst2 = db.Record("Guitar").add_parent( "MusicalInstrument").add_property("Manufacturer", rec_man2) rec_inst3 = db.Record("Broken Record").add_parent("MusicalInstrument") c = db.Container().extend([rt_man, rt_inst, rec_man, rec_inst, rec_man2, rec_inst2, rec_inst3]).insert() assert "Violin" not in [e.name for e in db.execute_query( "FIND RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")] assert "Violin" not in [e.name for e in db.execute_query( "FIND RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")] assert len(db.execute_query("FIND ENTITY MusicalInstrument")) == 4 assert len(db.execute_query("FIND RECORD MusicalInstrument")) == 3 assert len(db.execute_query( "FIND ENTITY MusicalInstrument WITH Manufacturer")) == 3 assert len(db.execute_query( "FIND RECORD MusicalInstrument WITH Manufacturer")) == 2 assert rec_inst.id == db.execute_query( "FIND ENTITY MusicalInstrument WITH Manufacturer = 'Antonio Stradivari'", unique=True).id assert len(db.execute_query( "FIND ENTITY MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 3 assert len(db.execute_query( "FIND RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 2 assert len(db.execute_query( "FIND ENTITY MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 1 assert len(db.execute_query( "FIND RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 1 def test_issue_154_with_versioning(): """ FIND MusicalInstrument WITH Manufacturer = "Antonio Stradivari" and FIND MusicalInstrument WITH Manufacturer != "Antonio Stradivari" """ rt_man = db.RecordType("Manufacturer") rt_inst = db.RecordType("MusicalInstrument").add_property(rt_man) rec_man = db.Record("Antonio Stradivari").add_parent("Manufacturer") rec_man2 = db.Record("The other guy").add_parent("Manufacturer") rec_inst = db.Record("Violin").add_parent( "MusicalInstrument").add_property("Manufacturer", rec_man) rec_inst2 = db.Record("Guitar").add_parent( "MusicalInstrument").add_property("Manufacturer", rec_man2) rec_inst3 = db.Record("Broken Record").add_parent("MusicalInstrument") db.Container().extend([rt_man, rt_inst, rec_man, rec_inst, rec_man2, rec_inst2, rec_inst3]).insert() assert "Violin" not in [e.name for e in db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")] assert "Violin" not in [e.name for e in db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")] assert len(db.execute_query("FIND ANY VERSION OF ENTITY MusicalInstrument")) == 4 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument")) == 3 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer")) == 3 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer")) == 2 assert rec_inst.id == db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer = 'Antonio Stradivari'", unique=True).id assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 3 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 2 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 1 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 1 # now, some updates rt_man.description = "Updated Description" rt_inst.description = "Updated Description" rec_man.description = "Updated Description" rec_man2.description = "Updated Description" rec_inst.description = "Updated Description" rec_inst2.description = "Updated Description" rec_inst3.description = "Updated Description" db.Container().extend([rt_man, rt_inst, rec_man, rec_inst, rec_man2, rec_inst2, rec_inst3]).update() assert "Violin" not in [e.name for e in db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")] assert "Violin" not in [e.name for e in db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")] assert len(db.execute_query("FIND ANY VERSION OF ENTITY MusicalInstrument")) == 8 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument")) == 6 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer")) == 6 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer")) == 4 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer = 'Antonio Stradivari'")) == 2 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 6 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH NOT Manufacturer = 'Antonio Stradivari'")) == 4 assert len(db.execute_query( "FIND ANY VERSION OF ENTITY MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 2 assert len(db.execute_query( "FIND ANY VERSION OF RECORD MusicalInstrument WITH Manufacturer != 'Antonio Stradivari'")) == 2 def test_issue_127(): """https://gitlab.com/caosdb/caosdb-server/-/issues/127""" p = db.Property( name="TestDoubleProperty", datatype=db.LIST( db.DOUBLE)).insert() rt = db.RecordType(name="TestRecordType").add_property(name="TestDoubleProperty", value=["nan"]).insert() test1 = db.execute_query("FIND ENTITY TestRecordType", unique=True) assert math.isnan(test1.get_property("TestDoubleProperty").value[0]) test2 = db.execute_query( "FIND ENTITY TestRecordType WITH TestDoubleProperty = NaN", unique=True) assert math.isnan(test1.get_property("TestDoubleProperty").value[0]) def test_issue_170(): """update scalar data type to list data type""" p = db.Property(name="TestProp1", datatype=db.LIST(db.INTEGER)) p.value = [1, 2] p.insert() p2 = db.execute_query("FIND ENTITY TestProp1", unique=True) assert p2.datatype == db.LIST(db.INTEGER) assert p2.value == [1, 2] p.description = "TestDescription" p.update() # this failed p2 = db.execute_query("FIND ENTITY TestProp1", unique=True) assert p2.datatype == db.LIST(db.INTEGER) assert p2.value == [1, 2] assert p2.description == "TestDescription" p = db.Property(name="TestProp2", datatype=db.DOUBLE) p.insert() p.datatype = db.LIST(db.INTEGER) p.update() # this worked because no value yet p2 = db.execute_query("FIND ENTITY TestProp2", unique=True) assert p2.datatype == db.LIST(db.INTEGER) p.value = [1, 2] p.update() # this failed p2 = db.execute_query("FIND ENTITY TestProp2", unique=True) assert p2.datatype == db.LIST(db.INTEGER) assert p2.value == [1, 2] p = db.Property(name="TestProp3", datatype=db.DOUBLE) p.insert() p.datatype = db.LIST(db.INTEGER) p.value = [1, 2] p.update() # this failed p2 = db.execute_query("FIND ENTITY TestProp3", unique=True) assert p2.datatype == db.LIST(db.INTEGER) assert p2.value == [1, 2] def test_issue_181(): """https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/181""" rt = db.RecordType("TestRT").insert() assert len(db.execute_query("FIND RECORDTYPE TestRT")) == 1 assert len(db.execute_query( "FIND RECORDTYPE TestRT WHICH HAS BEEN UPDATED TODAY")) == 0 rt.description = "New description" rt.update() assert len(db.execute_query( "FIND RECORDTYPE TestRT WHICH HAS BEEN UPDATED TODAY")) == 1 def test_issue_183(): """No reasonable error when using bad datetime format. https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/183 """ # Date YYYY-MM-ddThh:mm:ss assert db.Property(name="TestDateTime", datatype=db.DATETIME, value="2015-05-05T20:15:00").insert().id is not None with pytest.raises(db.TransactionError) as cm: # Date YYYY-MM-ddThh:mm db.Property(name="TestDateTime2", datatype=db.DATETIME, value="2015-05-05T20:15").insert() assert cm.value.errors[0].msg == ("Cannot parse value to datetime format " "(yyyy-mm-dd'T'hh:mm:ss[.fffffffff][TimeZone]).") def test_issue_130(): """Test select queries where names contain spaces https://gitlab.com/caosdb/caosdb-server/-/issues/130 However, this bug was actually about quotation marks """ db.RecordType(name="TestRT_A").insert() r1 = db.Record("ReferencedRecord").add_parent("TestRT_A").insert() p1 = db.Property(name="TestWrapper", datatype="TestRT_A").insert() p2 = db.Property( name="TestWrapper With Spaces", datatype="TestRT_A").insert() db.RecordType(name="TestRT_B" ).add_property(name="TestWrapper" ).add_property("TestWrapper With Spaces" ).insert() db.Record().add_parent("TestRT_B" ).add_property("TestWrapper", value=r1 ).add_property("TestWrapper With Spaces", value=r1 ).insert() query = "SELECT TestWrapper FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper")) assert row == [(r1.id,)] query = "SELECT TestWrapper FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper", "id")) assert row == [(p1.id,)] query = "SELECT 'TestWrapper' FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper", "id")) assert row == [(p1.id,)] query = "SELECT TestWrapper FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper", "name")) assert row == [("TestWrapper",)] query = "SELECT TestWrapper.name FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper", "name")) assert row == [("ReferencedRecord",)] query = "SELECT 'TestWrapper.name' FROM RECORD TestRT_B" rec = db.execute_query(query, unique=True) assert len(rec.properties) == 0 query = "SELECT 'TestWrapper'.name FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values(("TestWrapper", "name")) assert row == [("ReferencedRecord",)] query = "SELECT TestWrapper With Spaces FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces")) assert row == [(r1.id,)] query = "SELECT TestWrapper With Spaces FROM RECORD TestRT_B" row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces", "id")) assert row == [(p2.id,)] query = 'SELECT TestWrapper With Spaces FROM RECORD TestRT_B' row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces", "name")) assert row == [("TestWrapper With Spaces",)] query = 'SELECT "TestWrapper With Spaces" FROM RECORD TestRT_B' row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces", "name")) assert row == [("TestWrapper With Spaces",)] query = 'SELECT TestWrapper With Spaces.name FROM RECORD TestRT_B' row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces", "name")) assert row == [("ReferencedRecord",)] query = 'SELECT "TestWrapper With Spaces".name FROM RECORD TestRT_B' row = db.execute_query(query).get_property_values( ("TestWrapper With Spaces", "name")) assert row == [("ReferencedRecord",)] def test_issue_132(): """Query: Parenthesis around subproperties. https://gitlab.com/caosdb/caosdb-server/-/issues/132 """ db.RecordType("TestRT").insert() db.RecordType("TestRT_Foo").insert() db.Property("TestP_Bar", datatype=db.TEXT).insert() db.Property("TestP_Baz", datatype=db.TEXT).insert() rt1 = db.Record().add_parent("TestRT_Foo").add_property( "TestP_Bar", "val1").add_property( "TestP_Baz", "the other baz").insert() rt2 = db.Record().add_parent("TestRT").add_property( "TestP_Baz", "val2").add_property( "TestRT_Foo", rt1).insert() query = "FIND RECORD TestRT_Foo" assert db.execute_query(query, unique=True).id == rt1.id query = "FIND RECORD TestRT" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH TestRT_Foo" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH TestRT_Foo.TestP_Bar" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH TestRT_Foo.TestP_Bar = val1" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH (TestRT_Foo.TestP_Bar = val1)" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH ( TestRT_Foo.TestP_Bar = val1 )" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH (TestRT_Foo.TestP_Bar = val1) AND TestP_Baz = val2" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH (TestRT_Foo WITH (TestP_Bar = val1 AND TestP_Baz = 'the other baz')) AND TestP_Baz = val2" assert db.execute_query(query, unique=True).id == rt2.id query = "FIND RECORD TestRT WITH TestRT_Foo WITH (TestP_Bar = val1 AND TestP_Baz = 'the other baz') AND TestP_Baz = val2" assert db.execute_query(query, unique=True).id == rt2.id # this one has the wront scope of the conjunction. query = "FIND RECORD TestRT WITH TestRT_Foo.TestP_Bar = val1 AND TestP_Baz = 'the other one'" assert len(db.execute_query(query)) == 0 def test_issue_217(): """Server gets list property datatype wrong if description is updated.""" # @review Florian Spreckelsen 2022-03-15 rt = db.RecordType(name="TestRT").insert() # prop = db.Property(name="LP", datatype=db.LIST("TestRT")).insert() r = db.Record().add_parent(id=rt.id).add_property(name=rt.name, datatype=db.LIST( db.INTEGER), value=[10001, 10002]) assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] r.insert() assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] r.retrieve() assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] r.retrieve(flags={"cache": "false"}) assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] r.description = "Changed description" r.update() assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] # This line fails in the bug report with invalid XML. r.retrieve() assert r.get_property(rt.name).datatype == db.LIST(db.INTEGER) assert r.get_property(rt.name).value == [10001, 10002] def test_issue_217_2(): """Server gets overridden name of property wrong when the description of record is being updated.""" # @review Florian Spreckelsen 2022-03-15 rt = db.RecordType(name="TestRT").insert() # prop = db.Property(name="LP", datatype=db.LIST("TestRT")).insert() overridden_name = "TestRT-overridden" r = db.Record().add_parent(id=rt.id).add_property(name=overridden_name, id=rt.id) assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id r.insert() assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id r.retrieve() assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id r.retrieve(flags={"cache": "false"}) assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id r.description = "Changed description" # change description of the record r.update() assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id r.retrieve() assert r.get_property(overridden_name) is not None assert r.get_property(overridden_name).id == rt.id def test_issue_217_3(): """Server gets overridden description of property wrong when the description of record is being updated.""" # @review Florian Spreckelsen 2022-03-15 rt = db.RecordType(name="TestRT", description="Desc").insert() # prop = db.Property(name="LP", datatype=db.LIST("TestRT")).insert() r = db.Record().add_parent(id=rt.id).add_property(description="Desc-overridden", id=rt.id) r.insert() assert r.get_property(rt.name).id == rt.id assert r.get_property(rt.name).description == "Desc-overridden" r.retrieve() assert r.get_property(rt.name).id == rt.id assert r.get_property(rt.name).description == "Desc-overridden" r.retrieve(flags={"cache": "false"}) assert r.get_property(rt.name).id == rt.id assert r.get_property(rt.name).description == "Desc-overridden" r.description = "Changed description" # change description of the record r.update() assert r.get_property(rt.name).id == rt.id assert r.get_property(rt.name).description == "Desc-overridden" r.retrieve() assert r.get_property(rt.name).id == rt.id assert r.get_property(rt.name).description == "Desc-overridden" def test_issue_221(): """Unknown error during update of property leaving out datatype""" rt = db.RecordType("A") r = db.Record() r.add_parent(rt) p = db.Property(name="B", datatype=db.INTEGER) r.add_property(name="B", value=5) db.Container().extend([rt, r, p]).insert() r2 = db.Record(id=r.id).retrieve() r2.remove_property("B") r2.add_property(p, value=7) r2.update() assert r2.get_property("B").value == 7 assert r2.get_property("B").datatype == db.INTEGER assert r2.get_property("B").id == p.id def test_134_1(): """CQL: Subproperties are not recognized in list of references. https://gitlab.com/caosdb/caosdb-server/-/issues/134 """ p_lng = db.Property(name="longitude", datatype=db.DOUBLE).insert() p_lat = db.Property(name="latitude", datatype=db.DOUBLE).insert() rt_ev = db.RecordType(name="Event").add_property( p_lng).add_property(p_lat).insert() p_ev = db.Property(name="events", datatype=db.LIST(rt_ev)).insert() rt_ds = db.RecordType(name="DataSet").add_property(p_ev).insert() r_ev_1 = db.Record().add_parent("Event").add_property("longitude", 0.1).add_property("latitude", 0.1).insert() r_ev_2 = db.Record().add_parent("Event").add_property("longitude", 0.2).add_property("latitude", 0.2).insert() r_ds = db.Record().add_parent("DataSet").add_property( "events", value=[r_ev_1, r_ev_2]).insert() result = db.execute_query("SELECT events.latitude FROM RECORD DataSet", unique=True) print(result) assert len(result.get_property("events").value) == 2 assert result.get_property("events").value[0].get_property( "latitude").value == 0.1 assert result.get_property("events").value[1].get_property( "latitude").value == 0.2 def test_134_2(): """CQL: Subproperties are not recognized in list of references. https://gitlab.com/caosdb/caosdb-server/-/issues/134 """ p_lng = db.Property(name="longitude", datatype=db.DOUBLE).insert() p_lat = db.Property(name="latitude", datatype=db.DOUBLE).insert() rt_ev = db.RecordType(name="Event").add_property( p_lng).add_property(p_lat).insert() rt_ds = db.RecordType(name="DataSet").add_property(name="Event", datatype=db.LIST(rt_ev)).insert() r_ev_1 = db.Record().add_parent("Event").add_property("longitude", 0.1).add_property("latitude", 0.1).insert() r_ev_2 = db.Record().add_parent("Event").add_property("longitude", 0.2).add_property("latitude", 0.2).insert() r_ds = db.Record().add_parent("DataSet").add_property( "Event", datatype=db.LIST(rt_ev), value=[r_ev_1, r_ev_2]).insert() result = db.execute_query("SELECT Event.latitude FROM RECORD DataSet", unique=True) assert len(result.get_property("Event").value) == 2 assert result.get_property("Event").value[0].get_property( "latitude").value == 0.1 assert result.get_property("Event").value[1].get_property( "latitude").value == 0.2 def test_136(): """Faulty creation of a multi-property when updating a non-list property with a list value. https://gitlab.com/caosdb/caosdb-server/-/issues/136 """ # @author Florian Spreckelsen # @date 2022-05-23 # Insert data model: rt = db.RecordType(name="TestBug") p = db.Property(name="TestBugProperty", datatype=db.INTEGER) db.Container().extend([rt, p]).insert() # Insert test record: r = db.Record(name="TestRecord") r.add_parent(rt) r.add_property(p, value=18) r.insert() # Update the record: test_r = db.Record(id=r.id).retrieve() test_r.add_parent(rt) test_r.add_property(id=p.id, value=[18, 12]) with pytest.raises(db.TransactionError) as err: test_r.update() te = err.value assert te.has_error(db.UnqualifiedPropertiesError) assert "This data type does not accept collections of values (e.g. Lists)" in str(te) @pytest.mark.xfail(reason="Fix https://gitlab.com/caosdb/caosdb-pylib/-/issues/81") def test_136_b(): """Faulty creation of a multi-property when updating a non-list property with a list value. https://gitlab.com/caosdb/caosdb-server/-/issues/136 """ # @author Florian Spreckelsen # @date 2022-05-23 # Insert data model: rt = db.RecordType(name="TestBug") p = db.Property(name="TestBugProperty", datatype=db.TEXT) db.Container().extend([rt, p]).insert() # Insert test record: r = db.Record(name="TestRecord") r.add_parent(rt) r.add_property(p, value="val1") r.insert() # Update the record: test_r = db.Record(id=r.id).retrieve() test_r.add_parent(rt) test_r.add_property(id=p.id, value=["val1", "val2"]) with pytest.raises(db.TransactionError) as err: test_r.update() te = err.value assert te.has_error(db.UnqualifiedPropertiesError) assert "This data type does not accept collections of values (e.g. Lists)" in str(te) def test_141(): """Roles with `Grant(*)P` permissions still can't update other people's entities.""" admin._insert_role(name=CURATOR_ROLE, description="Desc") perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Grant", permission="*", priority=True) d = admin.PermissionRule(action="Deny", permission="*", priority=True) if g in perms: perms.remove(g) if d in perms: perms.remove(d) perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) perms = admin._get_permissions(CURATOR_ROLE) print(perms) rt = db.RecordType(name="TestRT", description="Desc1").insert() admin._insert_user(name="TestUser", password="Password1!", status="ACTIVE") admin._set_roles(username="TestUser", roles=[CURATOR_ROLE]) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") assert db.Info().user_info.name == "TestUser" assert db.Info().user_info.roles == [CURATOR_ROLE] rt.description = "Desc2" rt.update() assert rt.description == "Desc2" # switch back to admin user db.configure_connection() assert db.execute_query("FIND ENTITY TestRT", unique=True).description == "Desc2" def test_145(): """Searching for large numbers results in wrong results if integer values are used. https://gitlab.com/caosdb/caosdb-server/-/issues/145 """ db.Property("TestProp", datatype=db.TEXT).insert() db.Property("TestPropInt", datatype=db.INTEGER).add_parent( "TestProp").insert() db.Property("TestPropDouble", datatype=db.DOUBLE).add_parent( "TestProp").insert() db.RecordType("TestRT").insert() rec1 = db.Record("TestRec1").add_parent("TestRT").add_property( "TestPropInt", 1_000_000_000).insert() assert rec1.get_property("TestPropInt").value == 1_000_000_000 assert isinstance(rec1.get_property("TestPropInt").value, int) rec2 = db.Record("TestRec2").add_parent("TestRT").add_property( "TestPropDouble", 20_000_000_000).insert() assert rec2.get_property("TestPropDouble").value == 20_000_000_000 assert isinstance(rec2.get_property("TestPropDouble").value, float) assert db.execute_query( "FIND TestRT WITH TestProp = 1000000000", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT WITH TestProp = 1000000000.0", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT WITH TestProp > 1000000000", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT WITH TestProp > 1000000000.0", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT WITH TestProp = 20000000000", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT WITH TestProp = 20000000000.0", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT WITH TestProp < 20000000000", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT WITH TestProp < 20000000000.0", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT WITH TestPropInt < 10000000000000000000000000000000000000000000000000000000000", unique=True).id == rec1.id @pytest.mark.xfail(reason="Fix https://gitlab.com/caosdb/caosdb-server/-/issues/147") def test_147(): """Searching for integer numbers results in wrong results if floats are used. https://gitlab.com/caosdb/caosdb-server/-/issues/147 """ db.Property("TestProp", datatype=db.TEXT).insert() db.Property("TestPropInt", datatype=db.INTEGER).add_parent( "TestProp").insert() db.RecordType("TestRT1").insert() db.RecordType("TestRT2").insert() rec1 = db.Record("TestRec1").add_parent("TestRT1").add_property( "TestPropInt", 1).insert() assert rec1.get_property("TestPropInt").value == 1 assert isinstance(rec1.get_property("TestPropInt").value, int) rec2 = db.Record("TestRec2").add_parent("TestRT2").add_property( "TestPropInt", -2).insert() # Find the records assert db.execute_query( "FIND TestRT1 WITH TestProp < 1.9", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp < 1.1", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp = 1.0", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp > 0.9", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp > 0.1", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp <= 1.9", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp <= 1.1", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp >= 0.9", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT1 WITH TestProp >= 0.1", unique=True).id == rec1.id assert db.execute_query( "FIND TestRT2 WITH TestProp < -1.1", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp < -1.9", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp = -2.0", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp > -2.1", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp > 2.9", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp <= -1.1", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp <= -1.9", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp >= -2.1", unique=True).id == rec2.id assert db.execute_query( "FIND TestRT2 WITH TestProp >= 2.9", unique=True).id == rec2.id # Don't find the records assert len(db.execute_query("FIND TestRT1 WITH TestProp < 0.9")) == 0 assert len(db.execute_query("FIND TestRT1 WITH TestProp <= 0.9")) == 0 assert len(db.execute_query("FIND TestRT1 WITH TestProp > 1.1")) == 0 assert len(db.execute_query("FIND TestRT1 WITH TestProp >= 1.1")) == 0 assert len(db.execute_query("FIND TestRT2 WITH TestProp > -1.9")) == 0 assert len(db.execute_query("FIND TestRT2 WITH TestProp >= -1.9")) == 0 assert len(db.execute_query("FIND TestRT2 WITH TestProp < -2.1")) == 0 assert len(db.execute_query("FIND TestRT2 WITH TestProp <= -2.1")) == 0 # Smaller numbers, but querying across number types. rec3 = db.Record("TestRec3").add_parent( "TestRT").add_property("TestPropInt", 1).insert() assert db.execute_query( "FIND TestRT WITH TestPropInt < 2", unique=True).id == rec3.id assert db.execute_query( "FIND TestRT WITH TestPropInt < 2.5", unique=True).id == rec3.id def test_140(): """https://gitlab.com/caosdb/caosdb-server/-/issues/140""" admin._insert_role(name=CURATOR_ROLE, description="Desc") perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Grant", permission="TRANSACTION:*") perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) admin._insert_user(name="TestUser", password="Password1!", status="ACTIVE") admin._set_roles(username="TestUser", roles=[CURATOR_ROLE]) core_model_deny_permissions = [ "DELETE", "UPDATE:*", "EDIT:ACL" ] core_model_grant_permissions = [ "RETRIEVE:*", "USE:*", "UPDATE:PROPERTY:ADD" ] prop = db.Property(name="TestProp", datatype=db.TEXT).insert() rt = db.RecordType(name="TestRT").insert(flags={"ACL": None}) for d in core_model_deny_permissions: # First deny s.th. later the "UPDATE:PROPERTY:ADD" permission can be granted explicitely rt.deny(role=CURATOR_ROLE, permission=d) rt.update_acl() # retrieve again to be sure rt.retrieve(flags={"ACL": None}) for g in core_model_grant_permissions: rt.grant(role=CURATOR_ROLE, permission=g) rt.update_acl() print(rt.acl) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") assert db.Info().user_info.name == "TestUser" assert db.Info().user_info.roles == [CURATOR_ROLE] rt.add_property(prop) rt.get_property("TestProp").value = "some value" # this should succeed because the curator has UPDATE:PROPERTY:ADD rt.update() assert rt.get_property("TestProp").value == "some value" rt.get_property("TestProp").value = "some other value" with pytest.raises(TransactionError) as cm: # this should fail because the curator doesn't have # UPDATE:PROPERTY:REMOVE rt.update() assert cm.value.errors[0].msg == "You are not allowed to do this." def test_142(): """https://gitlab.com/caosdb/caosdb-server/-/issues/142""" valid_names = [ "with.dot", "with-hyphen", "with_underbar", "with0number", "withAcapital", ".withleadingdot", "Bwithleadingcapital", "1withleadingnumber", "_withleadingunderbar", "withtrailingcapitalC", "withtrailingnumber2", "withtrailingunderbar_", "withtrailinghyphen-", "withtrailingdot.", "4", "_", "D", "d", ".", ] invalid_names = [ "-", "-leadinghyphen", "", "%", "/", "[asdf]", "?", '"', ] for name in valid_names: admin._insert_user( name=name, password="Password1!", status="ACTIVE", email=None, entity=None) admin._delete_user(name=name) for name in invalid_names: with pytest.raises(HTTPClientError) as cm: admin._insert_user( name=name, password="Password1!", status="ACTIVE", email=None, entity=None) admin._delete_user(name=name) assert cm.value.status == 400 assert cm.value.msg.startswith( "The user name does not comply with the current policies for user names") @pytest.mark.xfail(reason="Fix https://gitlab.com/caosdb/caosdb-server/-/issues/177") def test_177(): db.RecordType("TestRT").insert() db.RecordType("TestRT").insert(unique=False) db.Property("TestProp", datatype=db.TEXT).insert() db.RecordType("TestSubRT").add_property("TestProp").add_parent("TestRT").insert() @pytest.mark.xfail(reason="Fix https://gitlab.com/caosdb/caosdb-server/-/issues/135") def test_135(): db.RecordType("TestRT1").insert() db.Property("TestProp", datatype=db.LIST("TestRT1")).insert() r1 = db.Record().add_parent("TestRT1").insert() r2 = db.Record().add_parent("TestRT1").add_property("TestProp", r1).insert() assert len(db.execute_query("FIND ENTITY WHICH IS REFERENCED BY A TestRT1 AS TestProp")) == 1 def test_192(): """Testing queries with Property by name. See https://gitlab.com/caosdb/caosdb-server/-/issues/192 COUNT Record WHICH HAS price -> Results: 19 COUNT Record WHICH HAS Property price -> Results: 19 COUNT Record WITH price -> Results: 19 COUNT Record WITH Property price -> Results: 0 """ db.Property(name="testprop", datatype=db.DOUBLE).insert() db.RecordType(name="TestRT").add_property("testprop").insert() db.Record(name="Rec1").add_parent("TestRT").add_property("testprop", value=3.1).insert() query1 = "COUNT RECORD WHICH HAS testprop" query2 = "COUNT RECORD WHICH HAS A testprop" query3 = "COUNT RECORD WHICH HAS Property testprop" query4 = "COUNT RECORD WHICH HAS A Property testprop" query5 = "COUNT RECORD WITH testprop" query6 = "COUNT RECORD WITH A testprop" query7 = "COUNT RECORD WITH Property testprop" query8 = "COUNT RECORD WITH A Property testprop" count1 = db.execute_query(query1) count2 = db.execute_query(query2) count3 = db.execute_query(query3) count4 = db.execute_query(query4) count5 = db.execute_query(query5) count6 = db.execute_query(query6) count7 = db.execute_query(query7) count8 = db.execute_query(query8) assert count1 == 1 assert count2 == 1 assert count3 == 1 assert count4 == 1 assert count5 == 1 assert count6 == 1 assert count7 == 1 assert count8 == 1 def test_196a(): """See https://gitlab.com/caosdb/caosdb-server/-/issues/196""" admin._insert_role(name=CURATOR_ROLE, description="Desc") perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Grant", permission="TRANSACTION:*") perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) admin._insert_user(name="TestUser", password="Password1!", status="ACTIVE") admin._set_roles(username="TestUser", roles=[CURATOR_ROLE]) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") # works db.RecordType(name="TestRT1").insert() db.Property(name="TestProp1", datatype=db.TEXT).insert() # Deny TRANSACTION:INSERT:PROPERTY db.configure_connection() perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Deny", permission="TRANSACTION:INSERT:PROPERTY") perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") # it is still allowed to insert a record type... db.RecordType(name="TestRT2").insert() # fails with pytest.raises(TransactionError) as cm: # this should fail because the curator doesn't have TRANSACTION:INSERT:PROPERTY db.Property(name="TestProp2", datatype=db.TEXT).insert() assert cm.value.errors[0].msg == "You are not allowed to do this." @pytest.mark.parametrize("deny", ["TRANSACTION:INSERT:", "TRANSACTION:INSERT:*"]) def test_196b(deny): """Same as test_196a but we completely deny insertion.""" admin._insert_role(name=CURATOR_ROLE, description="Desc") perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Grant", permission="TRANSACTION:*") perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) admin._insert_user(name="TestUser", password="Password1!", status="ACTIVE") admin._set_roles(username="TestUser", roles=[CURATOR_ROLE]) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") # works db.RecordType(name="TestRT1").insert() db.Property(name="TestProp1", datatype=db.TEXT).insert() # Deny TRANSACTION:INSERT db.configure_connection() perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Deny", permission=deny) perms.add(g) admin._set_permissions(CURATOR_ROLE, permission_rules=perms) db.configure_connection(username="TestUser", password_method="plain", password="Password1!") # fails (in contrast to test_196a) with pytest.raises(TransactionError) as cm: # this should fail because the curator doesn't have TRANSACTION:INSERT:RECORDTYPE db.RecordType(name="TestRT2").insert() assert cm.value.errors[0].msg == "You are not allowed to do this." # fails with pytest.raises(TransactionError) as cm: # this should fail because the curator doesn't have TRANSACTION:INSERT:PROPERTY db.Property(name="TestProp2", datatype=db.TEXT).insert() assert cm.value.errors[0].msg == "You are not allowed to do this." @pytest.mark.parametrize("num", ["1e+23", "5e22", "2e-323", "2E-323", "5E22", "1E+23", "+1E+23"]) def test_143(num): """https://gitlab.com/caosdb/caosdb-server/-/issues/144""" db.Property(name="scientific_notation", datatype=db.DOUBLE).insert() db.RecordType(name="RT1").add_property("scientific_notation", value=num).insert() for query in [ f"FIND RECORDTYPE RT1 WITH scientific_notation={num}", f"FIND RECORDTYPE RT1 WITH scientific_notation='{num}'", f"FIND RECORDTYPE RT1 WITH scientific_notation=\"{num}\"", f"FIND RECORDTYPE RT1 WITH scientific_notation = {num}", f"FIND RECORDTYPE RT1 WITH scientific_notation = '{num}'", f"FIND RECORDTYPE RT1 WITH scientific_notation = \"{num}\"" ]: db.execute_query(query, unique=True) @pytest.mark.parametrize("num", ["1 e+23", "- 5e22", "2e -323", "2E- 323", "5 E 22", "1 E+ 23", "+ 1"]) def test_143_white_space(num): """https://gitlab.com/caosdb/caosdb-server/-/issues/144""" for query in [ f"FIND RECORDTYPE RT1 WITH scientific_notation={num}", f"FIND RECORDTYPE RT1 WITH scientific_notation='{num}'", f"FIND RECORDTYPE RT1 WITH scientific_notation=\"{num}\"", f"FIND RECORDTYPE RT1 WITH scientific_notation = {num}", f"FIND RECORDTYPE RT1 WITH scientific_notation = '{num}'", f"FIND RECORDTYPE RT1 WITH scientific_notation = \"{num}\"" ]: with pytest.raises(TransactionError) as cm: db.execute_query(query) assert cm.value.msg == (f'You typed "{num}". Empty spaces are not allowed ' + f'in numbers. Did you mean "{num.replace(" ", "")}"?') def test_144(): """https://gitlab.com/caosdb/caosdb-server/-/issues/144""" db.Property(name="scientific_notation", datatype=db.DOUBLE, value="1e23").insert() value = db.execute_query("FIND PROPERTY scientific_notation", unique=True).value assert str(value) == "1e+23" assert isinstance(value, float) assert value == 1e23 assert value == 1e+23 def test_166(): """https://gitlab.com/caosdb/caosdb-server/-/issues/166""" db.RecordType(name="exists").insert() db.Property(name="exists_property", datatype=db.INTEGER).insert() db.RecordType(name="RT1").add_parent("exists").insert() db.RecordType(name="RT2").add_parent("exists").add_property("exists_property", 32453).insert() with pytest.raises(TransactionError) as cm: db.Record(name="RT3").add_parent("notexists").insert() assert [e.msg for e in cm.value.errors] == ["Entity has unqualified parents."] with pytest.raises(TransactionError) as cm: db.Record(name="RT4").add_parent("exists").add_property("notexists", 234243).insert() assert [e.msg for e in cm.value.errors] == ["Entity has unqualified properties."] with pytest.raises(TransactionError) as cm: db.Record( name="RT5").add_parent("notexists").add_property( "exists_property", 234243).insert() assert [e.msg for e in cm.value.errors] == ["Entity has unqualified parents."] @pytest.mark.xfail(reason="fix needed") def test_195(): """https://gitlab.com/caosdb/caosdb-server/-/issues/195""" admin._insert_role(name=CURATOR_ROLE, description="Desc") perms = admin._get_permissions(CURATOR_ROLE) g = admin.PermissionRule(action="Grant", permission="INVALID_PERMISSION:*") perms.add(g) with pytest.raises(Exception): admin._set_permissions(CURATOR_ROLE, permission_rules=perms) def test_216(): """https://gitlab.com/caosdb/caosdb-server/-/issues/216""" p1 = db.Property(name='p1', datatype=db.DOUBLE).insert() cont = db.Container() cont.append(db.RecordType(name="A") .add_property(id=p1.id, name=p1.name, datatype=db.DOUBLE, unit="min", importance=db.RECOMMENDED) .add_property(id=p1.id, name=p1.name, importance=db.RECOMMENDED) ) cont.append(db.RecordType(name="B") .add_parent(name="A", inheritance=db.SUGGESTED)) cont.insert() assert db.execute_query("FIND RECORDTYPE B", unique=True).name == "B" def test_138(): """Problems with non-integer ids in query filters, see https://gitlab.com/caosdb/caosdb-server/-/issues/138 """ queries = [ "FIND ENTITY WITH ID={}", "FIND ENTITY WITH ID=None", "FIND ENTITY WITH ID=\"1 non-existing id\"" ] for query in queries: # No error, but of course also no results. results = db.execute_query(query) assert len(results) == 0 @pytest.mark.xfail(reason="Needs fix for parent name change caching, " "see https://gitlab.com/caosdb/caosdb-server/-/issues/220") def test_220(): """Caching of children is not removed. See https://gitlab.com/caosdb/caosdb-server/-/issues/220""" rectype = db.RecordType(name="OldName").insert() rec = db.Record(name="rec").add_parent(rectype).insert() query = db.Query("FIND rec") assert query.cached is None res_1 = query.execute(unique=True) assert query.cached is False, "First query should be uncached." assert res_1.id == rec.id res_2 = query.execute(unique=True) assert query.cached is True, "Second query should be cached." rectype.name = "NewName" rectype.update() res_3 = query.execute(unique=True) assert res_3.parents[0].name == rectype.name, \ "The name of the record's parent should be up-to-date." assert query.cached is False, "Query after name change of parent should not be cached." @pytest.mark.xfail(reason="Needs fix for intermediate length strings, " "see https://gitlab.com/linkahead/linkahead-server/-/issues/101") def test_101(): """Unexpected server errors in case of intermediate length strings, https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/101 """ # This is ok very_long_string = "Long string"*10000 with pytest.raises(HTTPURITooLongError): db.execute_query(f"FIND RECORD WITH test=\"{very_long_string}\"") # This is not long_string = "Long string"*100 assert len(db.execute_query(f"FIND RECORD WITH test=\"{long_string}\"")) == 0 @pytest.mark.xfail(reason="Needs fix for keeping datatype, " "see https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/106") def test_indiscale_106(): """Datatype of old properties is changed. See https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/106 """ # Create TEXT Property p = db.Property("prop", datatype=db.TEXT) p.insert() # Create Record using this Property db.RecordType("RT").insert() r = db.Record(name="rec") r.add_parent("RT") r.add_property(id=p.id, value="This is a TEXT property") r.insert() print(db.Record(id=r.id).retrieve()) assert db.Record(id=r.id).retrieve().get_property("prop").datatype == db.TEXT # Changing Property's datatype to REFERENCE p.datatype = db.REFERENCE p.update() # Existing Property should still be reported as TEXT assert db.Record(id=r.id).retrieve().get_property("prop").datatype == db.TEXT @pytest.mark.xfail(reason="https://gitlab.com/caosdb/caosdb-server/-/issues/230") def test_230_name_duplicates_in_list_datatypes(): """https://gitlab.com/linkahead/linkahead-server/-/issues/230""" prop = db.Property(name="Test", datatype=db.TEXT).insert() # RT with same name, will be used as datatype rt = db.RecordType(name="Test").insert(unique=False) rec = db.Record(name="TestRec").add_parent(id=rt.id).insert() rec.add_property(id=rt.id, datatype=rt.id).update() # This works since it's not a list rec.add_property(id=rt.id, datatype=db.LIST(rt.id)).update() # This fails @pytest.mark.xfail(reason="https://gitlab.com/caosdb/caosdb-server/-/issues/235") def test_235_long_name(): """Should give an appropriate error, not just unknown server/-/issues.""" length = 10256 name = "N" * length rt1 = db.RecordType(name=name) try: rt1.insert() except Exception as exc: assert not isinstance(exc, db.HTTPServerError) # TODO more specific error should be asserted rt2 = db.RecordType(name="Short") rt2.insert() rt2.name = name try: rt2.update() except Exception as exc: assert not isinstance(exc, db.HTTPServerError) # TODO more specific error should be asserted @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/248") def test_248(): """Querying for entities with property fails if using ID.""" rt = db.RecordType(name="RT1").insert() prop = db.Property(name="prop", datatype=db.DOUBLE).insert() rec = db.Record().add_parent(rt).add_property(prop, value=23).insert() results = db.execute_query(f"FIND Entity with {prop.id}") assert len(results) == 1 @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/253") def test_253(): """Value in string queries may not start with large number of digits.""" test_strings = [ "0123456789", "hello" + "0123456789" * 5 + "world", "0123456789" * 5 + "world", ] for string in test_strings: results = db.execute_query(f"FIND Entity with prop={string}") assert len(results) == 0 @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/260") def test_260_datetime_without_date(): """Only time value in datetime property must not cause an unexpected server error but an unqulified properties error, see https://gitlab.com/linkahead/linkahead-server/-/issues/260. """ prop = db.Property(name="TestDateTimeProp", datatype=db.DATETIME).insert() rt = db.RecordType(name="TestType").add_property(prop).insert() with pytest.raises(TransactionError) as te: rec = db.Record().add_parent(rt).add_property(name=prop.name, value="11:00") rec.insert() assert te.value.has_error(db.UnqualifiedPropertiesError) assert "Cannot parse value to datetime format" in te.value.msg @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/263") def test_263(): with pytest.raises(TransactionError) as exc: db.Record(name="1").retrieve() assert len(exc.errors) == 1 assert exc.errors[0].msg == "Entity does not exist." # TODO Or whatever the message shall be. @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/264") def test_264_accents_and_umlauts(): """Test whether queries respect accents and umlauts. https://gitlab.com/linkahead/linkahead-server/-/issues/264. """ # Insertes have to be possible with unique=True since the name differ in # accents and umlauts. rt_e = db.RecordType(name="Test").insert() rt_ë = db.RecordType(name="Tëst").insert() rt_è = db.RecordType(name="Tést").insert() # Retrieves should be unique here, too. for rt in [rt_e, rt_ë, rt_è]: retrieved = db.get_entity_by_name(rt.name) assert retrieved.id == rt.id @pytest.mark.xfail(reason="https://gitlab.com/linkahead/linkahead-server/-/issues/266 🤯") def test_266(): """Emojis as payload""" rt = db.RecordType(name="A nice 🤯 RecordType").insert() assert rt.id > 0 assert "🤯" in rt.name