diff --git a/capella2polarion/__main__.py b/capella2polarion/__main__.py index 8f924768..77a136d7 100644 --- a/capella2polarion/__main__.py +++ b/capella2polarion/__main__.py @@ -84,8 +84,8 @@ def print_cli_state(capella2polarion_cli: Capella2PolarionCli) -> None: @click.pass_context def synchronize( ctx: click.core.Context, - force_update: bool, synchronize_config: typing.TextIO, + force_update: bool, type_prefix: str, role_prefix: str, ) -> None: @@ -95,16 +95,14 @@ def synchronize( "Synchronising model elements to Polarion project with id %s...", capella_to_polarion_cli.polarion_params.project_id, ) - capella_to_polarion_cli.load_synchronize_config(synchronize_config) + capella_to_polarion_cli.load_synchronize_config( + synchronize_config, type_prefix, role_prefix + ) capella_to_polarion_cli.force_update = force_update - capella_to_polarion_cli.type_prefix = type_prefix - capella_to_polarion_cli.role_prefix = role_prefix converter = model_converter.ModelConverter( capella_to_polarion_cli.capella_model, capella_to_polarion_cli.polarion_params.project_id, - type_prefix=capella_to_polarion_cli.type_prefix, - role_prefix=capella_to_polarion_cli.role_prefix, ) converter.read_model(capella_to_polarion_cli.config) diff --git a/capella2polarion/cli.py b/capella2polarion/cli.py index 038e8d98..f0c0d1e8 100644 --- a/capella2polarion/cli.py +++ b/capella2polarion/cli.py @@ -28,8 +28,6 @@ def __init__( polarion_delete_work_items: bool, capella_model: capellambse.MelodyModel, force_update: bool = False, - type_prefix: str = "", - role_prefix: str = "", ) -> None: self.debug = debug self.polarion_params = pw.PolarionWorkerParams( @@ -42,8 +40,6 @@ def __init__( self.capella_model = capella_model self.config = converter_config.ConverterConfig() self.force_update = force_update - self.type_prefix = type_prefix - self.role_prefix = role_prefix def _none_save_value_string(self, value: str | None) -> str | None: return "None" if value is None else value @@ -97,7 +93,10 @@ def setup_logger(self) -> None: logging.getLogger("httpcore").setLevel("WARNING") def load_synchronize_config( - self, synchronize_config_io: typing.TextIO + self, + synchronize_config_io: typing.TextIO, + type_prefix: str = "", + role_prefix: str = "", ) -> None: """Read the sync config into SynchronizeConfigContent. @@ -107,4 +106,6 @@ def load_synchronize_config( raise RuntimeError("synchronize config io stream is closed ") if not synchronize_config_io.readable(): raise RuntimeError("synchronize config io stream is not readable") - self.config.read_config_file(synchronize_config_io) + self.config.read_config_file( + synchronize_config_io, type_prefix, role_prefix + ) diff --git a/capella2polarion/converters/converter_config.py b/capella2polarion/converters/converter_config.py index d81ab52c..2725d73e 100644 --- a/capella2polarion/converters/converter_config.py +++ b/capella2polarion/converters/converter_config.py @@ -39,6 +39,8 @@ class LinkConfig: capella_attr: str polarion_role: str include: dict[str, str] = dataclasses.field(default_factory=dict) + link_field: str = "" + reverse_field: str = "" @dataclasses.dataclass @@ -70,18 +72,25 @@ def __init__(self): self.diagram_config: CapellaTypeConfig | None = None self.__global_config = CapellaTypeConfig() - def read_config_file(self, synchronize_config: t.TextIO): + def read_config_file( + self, + synchronize_config: t.TextIO, + type_prefix: str = "", + role_prefix: str = "", + ): """Read a given yaml file as config.""" config_dict = yaml.safe_load(synchronize_config) # We handle the cross layer config separately as global_configs global_config_dict = config_dict.pop("*", {}) all_type_config = global_config_dict.pop("*", {}) global_links = all_type_config.get("links", []) - self.__global_config.links = _force_link_config(global_links) + self.__global_config.links = self._force_link_config( + global_links, role_prefix + ) if "Diagram" in global_config_dict: diagram_config = global_config_dict.pop("Diagram") or {} - self.set_diagram_config(diagram_config) + self.set_diagram_config(diagram_config, type_prefix) for c_type, type_config in global_config_dict.items(): type_config = type_config or {} @@ -91,7 +100,9 @@ def read_config_file(self, synchronize_config: t.TextIO): type_configs = type_configs or {} self.add_layer(layer) for c_type, c_type_config in type_configs.items(): - self.set_layer_config(c_type, c_type_config, layer) + self.set_layer_config( + c_type, c_type_config, layer, type_prefix, role_prefix + ) def add_layer(self, layer: str): """Add a new layer without configuring any types.""" @@ -105,6 +116,8 @@ def set_layer_config( c_type: str, c_type_config: dict[str, t.Any] | list[dict[str, t.Any]] | None, layer: str, + type_prefix: str = "", + role_prefix: str = "", ): """Set one or multiple configs for a type to an existing layer.""" type_configs = _read_capella_type_configs(c_type_config) @@ -122,21 +135,23 @@ def set_layer_config( # As we set up all types this way, we can expect that all # non-compliant links are coming from global context here closest_links = _filter_links(c_type, closest_config.links, True) - p_type = ( - type_config.get("polarion_type") - or closest_config.p_type - or _default_type_conversion(c_type) + p_type = add_prefix( + ( + type_config.get("polarion_type") + or closest_config.p_type + or _default_type_conversion(c_type) + ), + type_prefix, ) self.polarion_types.add(p_type) + links = self._force_link_config( + type_config.get("links", []), role_prefix + ) self._layer_configs[layer][c_type].append( CapellaTypeConfig( p_type, type_config.get("serializer") or closest_config.converters, - _filter_links( - c_type, - _force_link_config(type_config.get("links", [])), - ) - + closest_links, + _filter_links(c_type, links) + closest_links, type_config.get("is_actor", _C2P_DEFAULT), type_config.get("nature", _C2P_DEFAULT), ) @@ -152,27 +167,61 @@ def set_global_config(self, c_type: str, type_config: dict[str, t.Any]): p_type, type_config.get("serializer"), _filter_links( - c_type, _force_link_config(type_config.get("links", [])) + c_type, self._force_link_config(type_config.get("links", [])) ) + self._get_global_links(c_type), type_config.get("is_actor", _C2P_DEFAULT), type_config.get("nature", _C2P_DEFAULT), ) - def set_diagram_config(self, diagram_config: dict[str, t.Any]): + def set_diagram_config( + self, diagram_config: dict[str, t.Any], type_prefix: str = "" + ): """Set the diagram config.""" c_type = "diagram" p_type = diagram_config.get("polarion_type") or "diagram" self.polarion_types.add(p_type) links = _filter_links( - c_type, _force_link_config(diagram_config.get("links", [])) + c_type, self._force_link_config(diagram_config.get("links", [])) ) self.diagram_config = CapellaTypeConfig( - p_type, + add_prefix(p_type, type_prefix), diagram_config.get("serializer") or "diagram", links + self._get_global_links(c_type), ) + def _force_link_config( + self, links: t.Any, role_prefix: str = "" + ) -> list[LinkConfig]: + result: list[LinkConfig] = [] + for link in links: + if isinstance(link, str): + config = LinkConfig( + capella_attr=link, + polarion_role=add_prefix(link, role_prefix), + link_field=link, + reverse_field=f"{link}_reverse", + ) + elif isinstance(link, dict): + config = LinkConfig( + capella_attr=(lid := link["capella_attr"]), + polarion_role=add_prefix( + (pid := link.get("polarion_role", lid)), + role_prefix, + ), + include=link.get("include", {}), + link_field=(lf := link.get("link_field", pid)), + reverse_field=link.get("reverse_field", f"{lf}_reverse"), + ) + else: + logger.error( + "Link not configured correctly: %r", + link, + ) + continue + result.append(config) + return result + def get_type_config( self, layer: str, c_type: str, **attributes: t.Any ) -> CapellaTypeConfig | None: @@ -252,25 +301,11 @@ def _force_dict( raise TypeError("Unsupported Type") -def _force_link_config(links: t.Any) -> list[LinkConfig]: - result: list[LinkConfig] = [] - for link in links: - if isinstance(link, str): - config = LinkConfig(capella_attr=link, polarion_role=link) - elif isinstance(link, dict): - config = LinkConfig( - capella_attr=(lid := link["capella_attr"]), - polarion_role=link.get("polarion_role", lid), - include=link.get("include", {}), - ) - else: - logger.error( - "Link not configured correctly: %r", - link, - ) - continue - result.append(config) - return result +def add_prefix(polarion_type: str, prefix: str) -> str: + """Add a prefix to the given ``polarion_type``.""" + if prefix: + return f"{prefix}_{polarion_type}" + return polarion_type def _filter_links( diff --git a/capella2polarion/converters/element_converter.py b/capella2polarion/converters/element_converter.py index 99c04000..fdf1662b 100644 --- a/capella2polarion/converters/element_converter.py +++ b/capella2polarion/converters/element_converter.py @@ -85,13 +85,11 @@ def __init__( capella_polarion_mapping: polarion_repo.PolarionDataRepository, converter_session: data_session.ConverterSession, generate_attachments: bool, - type_prefix: str = "", ): self.model = model self.capella_polarion_mapping = capella_polarion_mapping self.converter_session = converter_session self.generate_attachments = generate_attachments - self.type_prefix = type_prefix self.jinja_envs: dict[str, jinja2.Environment] = {} def serialize_all(self) -> list[data_models.CapellaWorkItem]: @@ -125,11 +123,6 @@ def serialize(self, uuid: str) -> data_models.CapellaWorkItem | None: ) converter_data.work_item = None - if self.type_prefix and converter_data.work_item is not None: - converter_data.work_item.type = ( - f"{self.type_prefix}_{converter_data.work_item.type}" - ) - if converter_data.errors: log_args = ( converter_data.capella_element._short_repr_(), diff --git a/capella2polarion/converters/link_converter.py b/capella2polarion/converters/link_converter.py index 2632edb5..d5f185a3 100644 --- a/capella2polarion/converters/link_converter.py +++ b/capella2polarion/converters/link_converter.py @@ -36,13 +36,11 @@ def __init__( converter_session: data_session.ConverterSession, project_id: str, model: capellambse.MelodyModel, - role_prefix: str = "", ): self.capella_polarion_mapping = capella_polarion_mapping self.converter_session = converter_session self.project_id = project_id self.model = model - self.role_prefix = role_prefix self.serializers: dict[str, _Serializer] = { converter_config.DESCRIPTION_REFERENCE_SERIALIZER: self._handle_description_reference_links, # pylint: disable=line-too-long @@ -63,8 +61,6 @@ def create_links_for_work_item( for link_config in converter_data.type_config.links: serializer = self.serializers.get(link_config.capella_attr) role_id = link_config.polarion_role - if self.role_prefix: - role_id = f"{self.role_prefix}_{role_id}" try: if serializer: new_links.extend( @@ -225,16 +221,11 @@ def create_grouped_link_fields( key = link.secondary_work_item_id back_links.setdefault(key, []).append(link) - role_id = self._remove_prefix(role) - config: converter_config.LinkConfig | None = None - for link_config in data.type_config.links: - if link_config.polarion_role == role_id: - config = link_config - break - - self._create_link_fields( - work_item, role_id, grouped_links, config=config - ) + config = find_link_config(data, role) + if config is not None and config.link_field: + self._create_link_fields( + work_item, config.link_field, grouped_links, config=config + ) def _create_link_fields( self, @@ -246,7 +237,6 @@ def _create_link_fields( ): link_map: dict[str, dict[str, list[str]]] if reverse: - role = f"{role}_reverse" link_map = {link.primary_work_item_id: {} for link in links} else: link_map = {link.secondary_work_item_id: {} for link in links} @@ -303,26 +293,41 @@ def _create_link_fields( def create_grouped_back_link_fields( self, - work_item: data_models.CapellaWorkItem, + data: data_session.ConverterData, links: list[polarion_api.WorkItemLink], ): """Create fields for the given WorkItem using a list of backlinks. Parameters ---------- - work_item - WorkItem to create the fields for + data + The ConverterData that stores the WorkItem to create the + fields for. links - List of links referencing work_item as secondary + List of links referencing work_item as secondary. """ + work_item = data.work_item + assert work_item is not None + wi = f"[{work_item.id}]({work_item.type} {work_item.title})" + logger.debug("Building grouped back links for work item %r...", wi) for role, grouped_links in _group_by("role", links).items(): - role_id = self._remove_prefix(role) - self._create_link_fields(work_item, role_id, grouped_links, True) + config = find_link_config(data, role) + if config is not None and config.reverse_field: + self._create_link_fields( + work_item, config.reverse_field, grouped_links, True + ) + + +def find_link_config( + data: data_session.ConverterData, role: str +) -> converter_config.LinkConfig | None: + """Search for LinkConfig with matching polarion_role in ``data``.""" + for link_config in data.type_config.links: + if link_config.polarion_role == role: + return link_config - def _remove_prefix(self, role: str) -> str: - if self.role_prefix: - return role.removeprefix(f"{self.role_prefix}_") - return role + logger.error("No LinkConfig found for %r", role) + return None def _group_by( diff --git a/capella2polarion/converters/model_converter.py b/capella2polarion/converters/model_converter.py index bc25897c..4336966b 100644 --- a/capella2polarion/converters/model_converter.py +++ b/capella2polarion/converters/model_converter.py @@ -29,13 +29,9 @@ def __init__( self, model: capellambse.MelodyModel, project_id: str, - type_prefix: str = "", - role_prefix: str = "", ): self.model = model self.project_id = project_id - self.type_prefix = type_prefix - self.role_prefix = role_prefix self.converter_session: data_session.ConverterSession = {} @@ -110,7 +106,6 @@ def generate_work_items( polarion_data_repo, self.converter_session, generate_attachments, - self.type_prefix, ) work_items = serializer.serialize_all() for work_item in work_items: @@ -133,7 +128,6 @@ def generate_work_item_links( self.converter_session, self.project_id, self.model, - self.role_prefix, ) for uuid, converter_data in self.converter_session.items(): if converter_data.work_item is None: @@ -158,7 +152,8 @@ def generate_work_item_links( ) continue + assert converter_data.work_item.id is not None if local_back_links := back_links.get(converter_data.work_item.id): link_serializer.create_grouped_back_link_fields( - converter_data.work_item, local_back_links + converter_data, local_back_links ) diff --git a/tests/data/model_elements/config.yaml b/tests/data/model_elements/config.yaml index 49d426c4..eaa72952 100644 --- a/tests/data/model_elements/config.yaml +++ b/tests/data/model_elements/config.yaml @@ -55,6 +55,8 @@ sa: capella_attr: inputs.exchanges include: Exchange Items: exchange_items + link_field: inputExchanges + reverse_field: inputExchangesReverse - polarion_role: output_exchanges capella_attr: outputs.exchanges include: diff --git a/tests/test_elements.py b/tests/test_elements.py index e46e501c..602fbd1f 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -165,8 +165,6 @@ class GroupedLinksBaseObject(t.TypedDict): link_serializer: link_converter.LinkSerializer work_items: dict[str, data_models.CapellaWorkItem] - back_links: dict[str, list[polarion_api.WorkItemLink]] - reverse_polarion_id_map: dict[str, str] config: converter_config.CapellaTypeConfig @@ -176,13 +174,14 @@ def grouped_links_base_object( base_object: BaseObjectContainer, dummy_work_items: dict[str, data_models.CapellaWorkItem], ) -> GroupedLinksBaseObject: - reverse_polarion_id_map = {v: k for k, v in POLARION_ID_MAP.items()} - back_links: dict[str, list[polarion_api.WorkItemLink]] = {} config = converter_config.CapellaTypeConfig( "fakeModelObject", links=[ converter_config.LinkConfig( - capella_attr="attribute", polarion_role="attribute" + capella_attr="attribute", + polarion_role="attribute", + link_field="attribute", + reverse_field="attribute_reverse", ) ], ) @@ -205,8 +204,6 @@ def grouped_links_base_object( return { "link_serializer": link_serializer, "work_items": dummy_work_items, - "back_links": back_links, - "reverse_polarion_id_map": reverse_polarion_id_map, "config": config, } @@ -718,8 +715,10 @@ def test_create_link_from_single_attribute_with_role_prefix( ) base_object.pw.polarion_data_repo.update_work_items([work_item_2]) - base_object.mc.converter_session["uuid2"].work_item = work_item_2 - + converter_data = base_object.mc.converter_session["uuid2"] + converter_data.work_item = work_item_2 + link_config = converter_data.type_config.links[0] + link_config.polarion_role = f"_C2P_{link_config.polarion_role}" expected = polarion_api.WorkItemLink( "Obj-2", "Obj-1", @@ -731,8 +730,8 @@ def test_create_link_from_single_attribute_with_role_prefix( base_object.mc.converter_session, base_object.pw.polarion_params.project_id, base_object.c2pcli.capella_model, - role_prefix="_C2P", ) + links = link_serializer.create_links_for_work_item("uuid2") assert links == [expected] @@ -1132,7 +1131,10 @@ def test_maintain_grouped_links_attributes( "fakeModelObject", links=[ converter_config.LinkConfig( - capella_attr="attribute", polarion_role="attribute" + capella_attr="attribute", + polarion_role="attribute", + link_field="attribute", + reverse_field="attribute_reverse", ) ], ) @@ -1185,7 +1187,10 @@ def test_maintain_grouped_links_attributes_with_role_prefix( "fakeModelObject", links=[ converter_config.LinkConfig( - capella_attr="attribute", polarion_role="attribute" + capella_attr="attribute", + polarion_role="_C2P_attribute", + link_field="attribute", + reverse_field="attribute_reverse", ) ], ) @@ -1206,7 +1211,6 @@ def test_maintain_grouped_links_attributes_with_role_prefix( base_object.mc.converter_session, base_object.pw.polarion_params.project_id, mock_model, - role_prefix="_C2P", ) for work_item in dummy_work_items.values(): @@ -1216,14 +1220,13 @@ def test_maintain_grouped_links_attributes_with_role_prefix( link_serializer.create_grouped_link_fields(converter_data) assert "attribute" in dummy_work_items["uuid0"].additional_attributes - assert "attribute" in dummy_work_items["uuid1"].additional_attributes + assert ( # Link Role on links were not prefixed + "attribute" not in dummy_work_items["uuid1"].additional_attributes + ) @staticmethod - @pytest.mark.parametrize("role_prefix", ["", "_C2P"]) def test_grouped_links_attributes_with_includes( - base_object: BaseObjectContainer, - model: capellambse.MelodyModel, - role_prefix: str, + base_object: BaseObjectContainer, model: capellambse.MelodyModel ): fnc = model.by_uuid(TEST_SYS_FNC) ex = model.by_uuid(TEST_SYS_FNC_EX) @@ -1231,16 +1234,23 @@ def test_grouped_links_attributes_with_includes( "systemFunction", links=[ converter_config.LinkConfig( - "inputs.exchanges", - "input_exchanges", + capella_attr="inputs.exchanges", + polarion_role="input_exchanges", include={"Exchange Items": "exchange_items"}, + link_field="input_exchanges", + reverse_field="input_exchanges_reverse", ) ], ) ex_config = converter_config.CapellaTypeConfig( "systemFunctionalExchange", links=[ - converter_config.LinkConfig("exchange_items", "exchange_items") + converter_config.LinkConfig( + capella_attr="exchange_items", + polarion_role="exchange_items", + link_field="exchange_items", + reverse_field="exchange_items_reverse", + ) ], ) ex_item_config = converter_config.CapellaTypeConfig("exchangeItem") @@ -1260,7 +1270,6 @@ def test_grouped_links_attributes_with_includes( converter = model_converter.ModelConverter( base_object.c2pcli.capella_model, base_object.c2pcli.polarion_params.project_id, - role_prefix=role_prefix, ) converter.converter_session = base_object.mc.converter_session work_items = converter.generate_work_items( @@ -1278,7 +1287,6 @@ def test_grouped_links_attributes_with_includes( base_object.mc.converter_session, base_object.pw.polarion_params.project_id, base_object.c2pcli.capella_model, - role_prefix=role_prefix, ) backlinks: dict[str, list[polarion_api.WorkItemLink]] = {} work_item = ( @@ -1309,22 +1317,21 @@ def test_maintain_reverse_grouped_links_attributes( ): link_serializer = grouped_links_base_object["link_serializer"] dummy_work_items = grouped_links_base_object["work_items"] - reverse_polarion_id_map = grouped_links_base_object[ - "reverse_polarion_id_map" - ] - back_links = grouped_links_base_object["back_links"] config = grouped_links_base_object["config"] + back_links: dict[str, list[polarion_api.WorkItemLink]] = {} + data = {} for work_item in dummy_work_items.values(): - converter_data = data_session.ConverterData( + data[work_item.id] = converter_data = data_session.ConverterData( "test", config, [], work_item ) link_serializer.create_grouped_link_fields( converter_data, back_links ) for work_item_id, links in back_links.items(): - work_item = dummy_work_items[reverse_polarion_id_map[work_item_id]] - link_serializer.create_grouped_back_link_fields(work_item, links) + link_serializer.create_grouped_back_link_fields( + data[work_item_id], links + ) assert ( dummy_work_items["uuid0"].additional_attributes.pop( @@ -1351,25 +1358,27 @@ def test_maintain_reverse_grouped_links_attributes_with_role_prefix( ): link_serializer = grouped_links_base_object["link_serializer"] dummy_work_items = grouped_links_base_object["work_items"] - reverse_polarion_id_map = grouped_links_base_object[ - "reverse_polarion_id_map" - ] - back_links = grouped_links_base_object["back_links"] config = grouped_links_base_object["config"] - for link in dummy_work_items["uuid0"].linked_work_items: + config.links[0].polarion_role = f"_C2P_{config.links[0].polarion_role}" + back_links: dict[str, list[polarion_api.WorkItemLink]] = {} + data = {} + for link in ( + dummy_work_items["uuid0"].linked_work_items + + dummy_work_items["uuid1"].linked_work_items + ): link.role = f"_C2P_{link.role}" - link_serializer.role_prefix = "_C2P" for work_item in dummy_work_items.values(): - converter_data = data_session.ConverterData( + data[work_item.id] = converter_data = data_session.ConverterData( "test", config, [], work_item ) link_serializer.create_grouped_link_fields( converter_data, back_links ) for work_item_id, links in back_links.items(): - work_item = dummy_work_items[reverse_polarion_id_map[work_item_id]] - link_serializer.create_grouped_back_link_fields(work_item, links) + link_serializer.create_grouped_back_link_fields( + data[work_item_id], links + ) assert ( "attribute_reverse" @@ -1390,12 +1399,24 @@ def test_grouped_linked_work_items_order_consistency( base_object.pw.polarion_params.project_id, base_object.c2pcli.capella_model, ) + config = converter_config.CapellaTypeConfig( + "fakeModelObject", + links=[ + converter_config.LinkConfig( + capella_attr="attribute", + polarion_role="role1", + link_field="attribute1", + reverse_field="attribute_reverse", + ) + ], + ) work_item = data_models.CapellaWorkItem("id", "Dummy") + converter_data = data_session.ConverterData("test", config, [], work_item) links = [ polarion_api.WorkItemLink("prim1", "id", "role1"), polarion_api.WorkItemLink("prim2", "id", "role1"), ] - link_serializer.create_grouped_back_link_fields(work_item, links) + link_serializer.create_grouped_back_link_fields(converter_data, links) check_sum = work_item.calculate_checksum() @@ -1403,7 +1424,7 @@ def test_grouped_linked_work_items_order_consistency( polarion_api.WorkItemLink("prim2", "id", "role1"), polarion_api.WorkItemLink("prim1", "id", "role1"), ] - link_serializer.create_grouped_back_link_fields(work_item, links) + link_serializer.create_grouped_back_link_fields(converter_data, links) assert check_sum == work_item.calculate_checksum() @@ -1730,7 +1751,7 @@ def test_add_jinja_to_description(self, model: capellambse.MelodyModel): def test_multiple_serializers(model: capellambse.MelodyModel, prefix: str): cap = model.by_uuid(TEST_OCAP_UUID) type_config = converter_config.CapellaTypeConfig( - "test", + f"{prefix}_test" if prefix else "test", ["include_pre_and_post_condition", "add_context_diagram"], [], ) @@ -1743,12 +1764,12 @@ def test_multiple_serializers(model: capellambse.MelodyModel, prefix: str): ) }, True, - prefix, ) work_item = serializer.serialize(TEST_OCAP_UUID) assert work_item is not None + assert work_item.type is not None assert work_item.type.startswith(prefix) assert "preCondition" in work_item.additional_attributes assert "postCondition" in work_item.additional_attributes @@ -1815,13 +1836,13 @@ def test_generic_work_item_with_type_prefix( } type_config = config.get_type_config(layer, c_type, **attributes) assert type_config is not None + type_config.p_type = f"{prefix}_{type_config.p_type}" ework_item = data_models.CapellaWorkItem(id=f"{prefix}_TEST") serializer = element_converter.CapellaWorkItemSerializer( model, polarion_repo.PolarionDataRepository([ework_item]), {uuid: data_session.ConverterData(layer, type_config, obj)}, False, - prefix, ) work_item = serializer.serialize(uuid) @@ -1886,3 +1907,20 @@ def test_read_config_links(caplog: pytest.LogCaptureFixture): if link.capella_attr == "parent" ) assert caplog.record_tuples[0] + caplog.record_tuples[1] == expected + assert ( + system_fnc_config := config.get_type_config("sa", "SystemFunction") + ) + assert system_fnc_config.links[0] == converter_config.LinkConfig( + capella_attr="inputs.exchanges", + polarion_role="input_exchanges", + include={"Exchange Items": "exchange_items"}, + link_field="inputExchanges", + reverse_field="inputExchangesReverse", + ) + assert system_fnc_config.links[1] == converter_config.LinkConfig( + capella_attr="outputs.exchanges", + polarion_role="output_exchanges", + include={"Exchange Items": "exchange_items"}, + link_field="output_exchanges", + reverse_field="output_exchanges_reverse", + )