diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py index 2ebdf95a3aa5ce76b983b2c3c47630e1a8884705..d86b7523fd32f6ac935fc8c27bfaa4014054abbb 100644 --- a/unittests/test_apiutils.py +++ b/unittests/test_apiutils.py @@ -30,7 +30,7 @@ import pytest import caosdb as db import caosdb.apiutils from caosdb.apiutils import (apply_to_ids, compare_entities, create_id_query, - resolve_reference, merge_entities) + empty_diff, resolve_reference, merge_entities) from caosdb.common.models import SPECIAL_ATTRIBUTES @@ -272,8 +272,10 @@ def test_copy_entities(): for i in [0, 1]: assert c.properties[i] is not r.properties[i] for special in SPECIAL_ATTRIBUTES: - assert getattr(c.properties[i], special) == getattr(r.properties[i], special) - assert c.get_importance(c.properties[i]) == r.get_importance(r.properties[i]) + assert getattr(c.properties[i], special) == getattr( + r.properties[i], special) + assert c.get_importance( + c.properties[i]) == r.get_importance(r.properties[i]) def test_merge_entities(): @@ -326,10 +328,12 @@ def test_merge_bug_109(): assert r_a.get_property("test_bug_property").value == [18, 19] assert "<Value>18</Value>\n <Value>19</Value>" in str(r_b) - assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str(r_b) + assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str( + r_b) assert "<Value>18</Value>\n <Value>19</Value>" in str(r_a) - assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str(r_a) + assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str( + r_a) @pytest.mark.xfail @@ -349,7 +353,98 @@ def test_bug_109(): assert r_a.get_property("test_bug_property").value == [18, 19] assert "<Value>18</Value>\n <Value>19</Value>" in str(r_b) - assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str(r_b) + assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str( + r_b) assert "<Value>18</Value>\n <Value>19</Value>" in str(r_a) - assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str(r_a) + assert "<Value>18</Value>\n <Value>19</Value>\n <Value>18</Value>\n <Value>19</Value>" not in str( + r_a) + + +def test_wrong_merge_conflict_reference(): + """Test a wrongly detected merge conflict in case of two records referencing + two different, but identical objects. + + """ + # Two identical license records will be referenced from both records to be + # merged + license_rt = db.RecordType(name="license") + license_rec_a = db.Record(name="CC-BY-3.0").add_parent(license_rt) + license_rec_b = db.Record(name="CC-BY-3.0").add_parent(license_rt) + + # two referencing records + dataset_rt = db.RecordType(name="Dataset") + title_prop = db.Property(name="title", datatype=db.TEXT) + doi_prop = db.Property(name="DOI", datatype=db.TEXT) + rec_a = db.Record().add_parent(dataset_rt) + rec_a.add_property(name=license_rt.name, + datatype=license_rt.name, value=license_rec_a) + rec_a.add_property(name=title_prop.name, value="Some dataset title") + + rec_b = db.Record().add_parent(dataset_rt) + rec_b.add_property(name=license_rt.name, + datatype=license_rt.name, value=license_rec_b) + rec_b.add_property(name=doi_prop.name, value="https://doi.org/12345.678") + + print(compare_entities(rec_a, rec_b)) + print(compare_entities(license_rec_a, license_rec_b)) + print(license_rec_b == license_rec_a) + merge_entities(rec_a, rec_b) + assert rec_a.get_property(license_rt.name) is not None + assert rec_a.get_property(license_rt.name).value is not None + assert isinstance(rec_a.get_property(license_rt.name).value, db.Record) + + +def test_empty_diff(): + + rec_a = db.Record(name="A") + rec_b = db.Record(name="B") + + assert empty_diff(rec_a, rec_a) + assert not empty_diff(rec_a, rec_b) + + rec_a.add_parent(name="RT") + rec_b.add_parent(name="RT") + assert empty_diff(rec_a, rec_a) + assert not empty_diff(rec_a, rec_b) + + rec_b.name = "A" + assert empty_diff(rec_a, rec_b) + + rec_a.add_property(name="some_prop", value=1) + assert not empty_diff(rec_a, rec_b) + + rec_b.add_property(name="some_prop", value=1) + assert empty_diff(rec_a, rec_b) + + rec_b.get_property("some_prop").value = 2 + assert not empty_diff(rec_a, rec_b) + + rec_b.get_property("some_prop").value = 1 + rec_b.add_property(name="some_other_prop", value="Test") + assert not empty_diff(rec_a, rec_b) + + rec_a.add_property(name="some_other_prop", value="Test") + assert empty_diff(rec_a, rec_b) + + # reference identical records, but different Python Record objects + ref_rec_a = db.Record(name="Ref").add_parent(name="RefType") + ref_rec_b = db.Record(name="Ref").add_parent(name="RefType") + rec_a.add_property(name="RefType", datatype="RefType", value=ref_rec_a) + rec_b.add_property(name="RefType", datatype="RefType", value=ref_rec_b) + # the default is `compare_referenced_records=False`, so the diff shouldn't + # be empty (different Python objects are referenced.) + assert not empty_diff(rec_a, rec_b) + # when looking into the referenced record, the diffs should be empty again + assert empty_diff(rec_a, rec_b, compare_referenced_records=True) + + # The same for lists of references + rec_a.remove_property("RefType") + rec_b.remove_property("RefType") + assert empty_diff(rec_a, rec_b) + rec_a.add_property(name="RefType", datatype=db.LIST( + "RefType"), value=[ref_rec_a, ref_rec_a]) + rec_b.add_property(name="RefType", datatype=db.LIST( + "RefType"), value=[ref_rec_b, ref_rec_b]) + assert not empty_diff(rec_a, rec_b) + assert empty_diff(rec_a, rec_b, compare_referenced_records=True)