Skip to content

Commit 9be0034

Browse files
yt-msMidnighter
authored andcommitted
fix: keep Model and Element consistent when adding relationships
1 parent c6d6bd6 commit 9be0034

File tree

4 files changed

+32
-7
lines changed

4 files changed

+32
-7
lines changed

src/structurizr/model/element.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,21 @@ def get_afferent_relationships(self) -> Iterator[Relationship]:
9999
r for r in self.get_model().get_relationships() if self is r.destination
100100
)
101101

102-
def add_relationship(self, relationship: Optional[Relationship] = None, **kwargs) -> Relationship:
102+
def add_relationship(
103+
self, relationship: Optional[Relationship] = None, **kwargs
104+
) -> Relationship:
103105
if relationship is None:
104106
relationship = Relationship(**kwargs)
107+
elif relationship in self.relationships:
108+
return relationship # Nothing more to do
105109
if relationship.source is None:
106110
relationship.source = self
107111
elif relationship.source is not self:
108-
raise ValueError(f"Cannot add relationship {relationship} to element {self} that is not its source.")
112+
raise ValueError(
113+
f"Cannot add relationship {relationship} to element {self} that is not its source."
114+
)
109115
self.relationships.add(relationship)
116+
self.get_model().add_relationship(relationship)
110117
return relationship
111118

112119
@classmethod

src/structurizr/model/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ def _add_element(self, element: Element) -> None:
411411

412412
def _add_relationship(self, relationship: Relationship) -> bool:
413413
""""""
414+
if relationship in self.get_relationships():
415+
return True # Nothing to do
414416
if not relationship.id:
415417
relationship.id = self._id_generator.generate_id()
416418
elif relationship.id in self._elements_by_id:

tests/integration/test_model_element_relationships.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
DEFINITIONS = Path(__file__).parent / "data" / "workspace_definition"
2828

2929

30-
@pytest.mark.xfail(strict=True)
3130
def test_adding_relationship_to_element_adds_to_model():
3231
"""
3332
Make sure that when a relationship is added via Element.add_relationship it also
@@ -39,10 +38,10 @@ def test_adding_relationship_to_element_adds_to_model():
3938

4039
r = sys1.add_relationship(source=sys1, destination=sys2, description="uses")
4140
assert list(sys1.relationships) == [r]
42-
assert list(sys1.get_relationships()) == [r] # Currently will fail
43-
assert list(model.get_relationships()) == [r] # Currently will fail
41+
assert list(sys1.get_relationships()) == [r]
42+
assert list(model.get_relationships()) == [r]
4443
assert list(sys2.relationships) == [] # relationships only contains outbound
45-
assert list(sys2.get_relationships()) == [r] # Currently will fail
44+
assert list(sys2.get_relationships()) == [r]
4645

4746

4847
def test_adding_relationship_to_model_adds_to_element():

tests/unit/model/test_element.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import pytest
2020

2121
from structurizr.model.element import Element
22+
from structurizr.model.model import Model
2223

2324

2425
class ConcreteElement(Element):
@@ -60,7 +61,8 @@ def test_element_can_only_add_relationship_to_source():
6061
element1 = ConcreteElement(name="elt1")
6162
element2 = ConcreteElement(name="elt1")
6263
with pytest.raises(
63-
ValueError, match="Cannot add relationship .* to element .* that is not its source"
64+
ValueError,
65+
match="Cannot add relationship .* to element .* that is not its source",
6466
):
6567
element1.add_relationship(source=element2)
6668

@@ -72,5 +74,20 @@ def test_element_add_relationship_can_omit_source():
7274
"""
7375
element1 = ConcreteElement(name="elt1")
7476
element2 = ConcreteElement(name="elt1")
77+
model = Model()
78+
model._add_element(element1)
7579
r = element1.add_relationship(destination=element2)
7680
assert r.source is element1
81+
82+
83+
def test_element_add_relationship_twice_is_ok():
84+
"""
85+
Defensive test that adding the same relationship twice is fine.
86+
"""
87+
element1 = ConcreteElement(name="elt1")
88+
element2 = ConcreteElement(name="elt1")
89+
model = Model()
90+
model._add_element(element1)
91+
r = element1.add_relationship(destination=element2)
92+
element1.add_relationship(r)
93+
assert list(element1.relationships) == [r]

0 commit comments

Comments
 (0)