Skip to content

Commit 841dfcd

Browse files
authored
Merge pull request #65 from JansthcirlU:61-fix-duplicate-node-adds-in-graph-and-subgraph-add-link
61-fix-duplicate-node-adds-in-graph-and-subgraph-add-link
2 parents f9839db + 938c063 commit 841dfcd

File tree

4 files changed

+300
-38
lines changed

4 files changed

+300
-38
lines changed

src/Mermaid.Flowcharts/Flowchart.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class Flowchart : IMermaidPrintable
1616
public IEnumerable<Node> Nodes => _nodes.OfType<Node>();
1717
public IEnumerable<Subgraph> Subgraphs => _nodes.OfType<Subgraph>();
1818
public IEnumerable<Link> Links => _links.AsReadOnly();
19+
public IEnumerable<INode> AllNodeChildren => _nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllNodeChildren));
1920
public IEnumerable<Node> AllNodes => Nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllNodes));
2021
public IEnumerable<Link> AllLinks => Links.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllLinks));
2122

@@ -41,10 +42,12 @@ public Flowchart AddNode(INode node)
4142

4243
public Flowchart AddLink(Link link)
4344
{
44-
_links.Add(link);
45-
AddNode(link.Source);
46-
AddNode(link.Destination);
47-
return this;
45+
if (AllNodeChildren.Any(link.Source.Equals) && AllNodeChildren.Any(link.Destination.Equals))
46+
{
47+
_links.Add(link);
48+
return this;
49+
}
50+
throw new InvalidOperationException("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart.");
4851
}
4952

5053
public override string ToString()

src/Mermaid.Flowcharts/Subgraphs/Subgraph.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public record Subgraph : INode<Subgraph>
1616
public IEnumerable<Node> Nodes => _nodes.OfType<Node>();
1717
public IEnumerable<Subgraph> Subgraphs => _nodes.OfType<Subgraph>();
1818
public IEnumerable<Link> Links => _links.AsReadOnly();
19+
public IEnumerable<INode> AllNodeChildren => _nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph._nodes));
1920
public IEnumerable<Node> AllNodes => Nodes.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllNodes));
2021
public IEnumerable<Link> AllLinks => Links.Concat(Subgraphs.SelectMany(subgraph => subgraph.AllLinks));
2122

@@ -50,6 +51,11 @@ public static Subgraph Create(string identifier, string title, SubgraphDirection
5051

5152
public Subgraph AddNode(INode node)
5253
{
54+
if (Equals(node))
55+
{
56+
throw new ArgumentException("Cannot add subgraph as a node to itself.", nameof(node));
57+
}
58+
5359
if (node is Node nd && Nodes.Any(nd.Equals))
5460
{
5561
return this;
@@ -61,10 +67,12 @@ public Subgraph AddNode(INode node)
6167

6268
public Subgraph AddLink(Link link)
6369
{
64-
_links.Add(link);
65-
AddNode(link.Source);
66-
AddNode(link.Destination);
67-
return this;
70+
if (AllNodeChildren.Any(link.Source.Equals) && AllNodeChildren.Any(link.Destination.Equals))
71+
{
72+
_links.Add(link);
73+
return this;
74+
}
75+
throw new InvalidOperationException("Cannot add link to subgraph: the source and the destination nodes should both be present within the subgraph.");
6876
}
6977

7078
public override string ToString()

tests/Mermaid.Flowcharts.Tests/FlowchartTests.cs

Lines changed: 180 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,102 @@ public void Flowchart_ShouldContainTwoNodes_WhenTryingToAddTwoNodesWithDuplicate
4545
}
4646

4747
[Fact]
48-
public void Flowchart_ShouldAddLinkNode_WhenNodesAreNotPartOfSubgraphs()
48+
public void Flowchart_WhenLinkNodesExist_ShouldAddLink()
4949
{
5050
// Arrange
5151
Flowchart flowchart = new();
52-
Node source = Node.CreateNew<MermaidUnicodeText>(Guid.NewGuid().ToString());
53-
Node destination = Node.CreateNew<MermaidUnicodeText>(Guid.NewGuid().ToString());
54-
Link link = Link.Create(source, destination);
52+
Node n1 = Node.CreateNew("Node 1");
53+
Node n2 = Node.CreateNew("Node 2");
54+
Link link = Link.Create(n1, n2);
5555

5656
// Act
57-
flowchart.AddLink(link);
57+
flowchart
58+
.AddNode(n1)
59+
.AddNode(n2)
60+
.AddLink(link);
5861

5962
// Assert
60-
Assert.NotEmpty(flowchart.Nodes);
61-
Assert.Equal(2, flowchart.Nodes.Count());
63+
Assert.NotEmpty(flowchart.AllNodes);
64+
Assert.Equal(2, flowchart.AllNodes.Count());
65+
Assert.Single(flowchart.Links);
6266
}
6367

6468
[Fact]
65-
public void Flowchart_ShouldNotAddLinkNode_WhenLinkNodesAlreadyExist()
69+
public void Flowchart_WhenLinkNodesAreNestedButExist_ShouldAddLink()
6670
{
6771
// Arrange
6872
Flowchart flowchart = new();
69-
Node source = Node.CreateNew<MermaidUnicodeText>(Guid.NewGuid().ToString());
70-
Node destination = Node.CreateNew<MermaidUnicodeText>(Guid.NewGuid().ToString());
71-
Link link = Link.Create(source, destination);
73+
Subgraph sg1 = Subgraph.CreateNew("Subgraph 1");
74+
Node sg1Node = Node.CreateNew("Node 1");
75+
Subgraph sg2 = Subgraph.CreateNew("Subgraph 2");
76+
Node sg2Node = Node.CreateNew("Node 2");
77+
Link link = Link.Create(sg1Node, sg2Node);
7278

7379
// Act
74-
flowchart.AddNode(source);
75-
flowchart.AddLink(link);
80+
flowchart
81+
.AddNode(sg1.AddNode(sg1Node))
82+
.AddNode(sg2.AddNode(sg2Node))
83+
.AddLink(link);
7684

7785
// Assert
78-
Assert.NotEmpty(flowchart.Nodes);
79-
Assert.Equal(2, flowchart.Nodes.Count());
86+
Assert.NotEmpty(flowchart.AllNodes);
87+
Assert.Equal(2, flowchart.AllNodes.Count());
88+
Assert.Single(flowchart.Links);
89+
}
90+
91+
[Fact]
92+
public void Flowchart_WhenLinkNodeIsSubgraphAndExists_ShouldAddLink()
93+
{
94+
// Arrange
95+
Flowchart flowchart = new();
96+
Node n = Node.CreateNew("n");
97+
Subgraph subgraph = Subgraph.CreateNew("Subgraph");
98+
Link link = Link.Create(n, subgraph);
99+
100+
// Act
101+
flowchart
102+
.AddNode(n)
103+
.AddNode(subgraph)
104+
.AddLink(link);
105+
106+
// Assert
107+
Assert.NotEmpty(flowchart.AllNodeChildren);
108+
Assert.Equal(2, flowchart.AllNodeChildren.Count());
109+
Assert.Single(flowchart.Links);
110+
}
111+
112+
[Fact]
113+
public void Flowchart_WhenLinkSourceNotPresent_AddLinkShouldThrow()
114+
{
115+
// Arrange
116+
Flowchart flowchart = new();
117+
Node source = Node.CreateNew(Guid.NewGuid().ToString());
118+
Node destination = Node.CreateNew(Guid.NewGuid().ToString());
119+
Link link = Link.Create(source, destination);
120+
121+
// Act & Assert
122+
flowchart.AddNode(destination); // Forgot to add source
123+
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(
124+
() => flowchart.AddLink(link)
125+
);
126+
Assert.Contains("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart.", exception.Message);
127+
}
128+
129+
[Fact]
130+
public void Flowchart_WhenLinkDestinationNotPresent_AddLinkShouldThrow()
131+
{
132+
// Arrange
133+
Flowchart flowchart = new();
134+
Node source = Node.CreateNew(Guid.NewGuid().ToString());
135+
Node destination = Node.CreateNew(Guid.NewGuid().ToString());
136+
Link link = Link.Create(source, destination);
137+
138+
// Act & Assert
139+
flowchart.AddNode(source); // Forgot to add destination
140+
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(
141+
() => flowchart.AddLink(link)
142+
);
143+
Assert.Contains("Cannot add link to flowchart: the source and the destination nodes should both be present within the flowchart.", exception.Message);
80144
}
81145

82146
[Fact]
@@ -640,4 +704,105 @@ flowchart TD
640704
// Assert
641705
Assert.Equal(expected, actual);
642706
}
707+
708+
[Fact]
709+
public void Flowchart_WhenNestedLinksParentToChild_ShouldNotAddDuplicateNodes()
710+
{
711+
// Arrange
712+
Flowchart flowchart = new();
713+
Node flowchartNode = Node.Create("n", "Inside flowchart");
714+
Subgraph subgraph = Subgraph.Create("sg", "Subgraph");
715+
Node subgraphNode = Node.Create("sgn", "Inside subgraph");
716+
Link nToSgn = Link.Create(flowchartNode, subgraphNode);
717+
Subgraph subsubgraph = Subgraph.Create("ssg", "Subsubgraph");
718+
Node subsubgraphNode = Node.Create("ssgn", "Inside subsubgraph");
719+
Link sgnToSsgn = Link.Create(subgraphNode, subsubgraphNode);
720+
flowchart
721+
.AddNode(flowchartNode)
722+
.AddNode(
723+
subgraph
724+
.AddNode(subgraphNode)
725+
.AddNode(
726+
subsubgraph
727+
.AddNode(subsubgraphNode)
728+
)
729+
)
730+
.AddLink(nToSgn)
731+
.AddLink(sgnToSsgn);
732+
733+
string expected =
734+
$"""
735+
flowchart TD
736+
n["Inside flowchart"]
737+
738+
subgraph sg ["Subgraph"]
739+
sgn["Inside subgraph"]
740+
741+
subgraph ssg ["Subsubgraph"]
742+
ssgn["Inside subsubgraph"]
743+
end
744+
end
745+
746+
n ---> sgn
747+
sgn ---> ssgn
748+
749+
""";
750+
751+
// Act
752+
string actual = flowchart.ToMermaidString(0, " ");
753+
754+
// Assert
755+
Assert.Equal(expected, actual);
756+
}
757+
758+
[Fact]
759+
public void Flowchart_WhenNestedLinksChildToParent_ShouldNotAddDuplicateNodes()
760+
{
761+
// Arrange
762+
Flowchart flowchart = new();
763+
Node flowchartNode = Node.Create("n", "Inside flowchart");
764+
Subgraph subgraph = Subgraph.Create("sg", "Subgraph");
765+
Node subgraphNode = Node.Create("sgn", "Inside subgraph");
766+
Link sgnToN = Link.Create(subgraphNode, flowchartNode);
767+
Subgraph subsubgraph = Subgraph.Create("ssg", "Subsubgraph");
768+
Node subsubgraphNode = Node.Create("ssgn", "Inside subsubgraph");
769+
Link ssgnToSgn = Link.Create(subsubgraphNode, subgraphNode);
770+
flowchart
771+
.AddNode(flowchartNode)
772+
.AddNode(
773+
subgraph
774+
.AddNode(subgraphNode)
775+
.AddNode(
776+
subsubgraph
777+
.AddNode(subsubgraphNode)
778+
)
779+
.AddLink(ssgnToSgn)
780+
)
781+
.AddLink(sgnToN);
782+
783+
string expected =
784+
$"""
785+
flowchart TD
786+
n["Inside flowchart"]
787+
788+
subgraph sg ["Subgraph"]
789+
sgn["Inside subgraph"]
790+
791+
subgraph ssg ["Subsubgraph"]
792+
ssgn["Inside subsubgraph"]
793+
end
794+
795+
ssgn ---> sgn
796+
end
797+
798+
sgn ---> n
799+
800+
""";
801+
802+
// Act
803+
string actual = flowchart.ToMermaidString(0, " ");
804+
805+
// Assert
806+
Assert.Equal(expected, actual);
807+
}
643808
}

0 commit comments

Comments
 (0)