diff --git a/capella2polarion/converters/link_converter.py b/capella2polarion/converters/link_converter.py index bf8bb511..13af59a0 100644 --- a/capella2polarion/converters/link_converter.py +++ b/capella2polarion/converters/link_converter.py @@ -71,6 +71,13 @@ def create_links_for_work_item( new: cabc.Iterable[str] if isinstance(refs, common.ElementList): new = refs.by_uuid # type: ignore[assignment] + elif refs is None: + warning = make_link_logging_message( + "No model element behind that attribute", + link_config.capella_attr, + ) + link_errors.extend(warning) + continue else: assert hasattr(refs, "uuid"), "No 'uuid' on value" new = [refs.uuid] @@ -82,14 +89,11 @@ def create_links_for_work_item( self._create(work_item.id, role_id, new, {}) ) except Exception as error: - error_text = f"{type(error).__name__} {str(error)}" - link_errors.extend( - [ - f"Requested attribute: {link_config.capella_attr}", - error_text, - "--------", - ] + error = make_link_logging_message( + f"{type(error).__name__} {str(error)}", + link_config.capella_attr, ) + link_errors.extend(error) if link_errors: for link_error in link_errors: @@ -380,7 +384,7 @@ def _sorted_unordered_html_list( def _resolve_attribute( obj: common.GenericElement, attr_id: str -) -> common.ElementList[common.GenericElement] | common.GenericElement: +) -> common.ElementList[common.GenericElement] | common.GenericElement | None: attr_name, _, map_id = attr_id.partition(".") objs = getattr(obj, attr_name) if map_id: @@ -388,3 +392,12 @@ def _resolve_attribute( return _resolve_attribute(objs, map_id) objs = objs.map(map_id) return objs + + +def make_link_logging_message(message: str, capella_attr: str) -> list[str]: + """Return a list of strings for a link logging message.""" + return [ + f"Requested attribute: {capella_attr!r}", + message, + "--------", + ] diff --git a/tests/test_elements.py b/tests/test_elements.py index fc19f1c5..0fefa265 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -459,17 +459,45 @@ def test_create_links_custom_exchanges_resolver( assert links == [expected] @staticmethod - def test_create_links_missing_attribute( + def test_create_links_logs_error_when_no_uuid_is_found_on_value( base_object: BaseObjectContainer, caplog: pytest.LogCaptureFixture ): expected = ( "Link creation for \"\" failed:" - "\n\tRequested attribute: attribute" + "\n\tRequested attribute: 'attribute'" "\n\tAssertionError No 'uuid' on value" "\n\t--------" ) + no_uuid = FakeModelObject("") + del no_uuid.uuid + base_object.mc.converter_session["uuid1"].capella_element.attribute = ( + no_uuid + ) + + with caplog.at_level(logging.ERROR): + link_serializer = link_converter.LinkSerializer( + base_object.pw.polarion_data_repo, + base_object.mc.converter_session, + base_object.pw.polarion_params.project_id, + base_object.c2pcli.capella_model, + ) + links = link_serializer.create_links_for_work_item("uuid1") + + assert not links + assert caplog.messages[0] == expected - with caplog.at_level(logging.DEBUG): + @staticmethod + def test_create_links_logs_warning_model_element_behind_attribute_is_none( + base_object: BaseObjectContainer, caplog: pytest.LogCaptureFixture + ): + expected = ( + "Link creation for \"\" failed:" + "\n\tRequested attribute: 'attribute'" + "\n\tNo model element behind that attribute" + "\n\t--------" + ) + + with caplog.at_level(logging.WARNING): link_serializer = link_converter.LinkSerializer( base_object.pw.polarion_data_repo, base_object.mc.converter_session, @@ -487,7 +515,7 @@ def test_create_links_no_new_links_with_errors( ): expected = ( "Link creation for \"\" failed:" - "\n\tRequested attribute: non_existent_attr" + "\n\tRequested attribute: 'non_existent_attr'" "\n\t" ) @@ -538,7 +566,7 @@ def test_create_links_with_new_links_and_errors( expected = ( "Link creation for \"\" " "partially successful. Some links were not created:" - "\n\tRequested attribute: non_existent_attr" + "\n\tRequested attribute: 'non_existent_attr'" "\n\t" )