Coverage for tests / unit / views / test_plantuml.py: 100%

110 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-16 21:32 +0000

1from pathlib import Path 

2from subprocess import CalledProcessError 

3from unittest.mock import MagicMock, mock_open, patch 

4 

5from pytest import fixture, raises 

6 

7from graphable.graph import Graph 

8from graphable.graphable import Graphable 

9from graphable.views.plantuml import ( 

10 PlantUmlStylingConfig, 

11 _check_plantuml_on_path, 

12 create_topology_plantuml, 

13 export_topology_plantuml, 

14 export_topology_plantuml_image, 

15) 

16 

17 

18class TestPlantUML: 

19 @fixture 

20 def graph_fixture(self): 

21 a = Graphable("A") 

22 b = Graphable("B") 

23 g = Graph() 

24 g.add_edge(a, b) 

25 return g, a, b 

26 

27 def test_create_topology_plantuml_default(self, graph_fixture): 

28 g, a, b = graph_fixture 

29 puml = create_topology_plantuml(g) 

30 

31 assert "@startuml" in puml 

32 assert 'node "A" as' in puml 

33 assert 'node "B" as' in puml 

34 assert "-->" in puml 

35 assert "@enduml" in puml 

36 

37 def test_create_topology_plantuml_custom_config(self, graph_fixture): 

38 g, a, b = graph_fixture 

39 config = PlantUmlStylingConfig( 

40 node_ref_fnc=lambda n: n.reference, 

41 node_type="component", 

42 direction="left to right direction", 

43 ) 

44 puml = create_topology_plantuml(g, config) 

45 

46 assert "left to right" in puml 

47 assert 'component "A" as A' in puml 

48 assert "A --> B" in puml 

49 

50 def test_export_topology_plantuml(self, graph_fixture): 

51 g, _, _ = graph_fixture 

52 output_path = Path("output.puml") 

53 

54 with patch("builtins.open", mock_open()) as mock_file: 

55 export_topology_plantuml(g, output_path) 

56 

57 mock_file.assert_called_with(output_path, "w+") 

58 mock_file().write.assert_called() 

59 

60 @patch("graphable.views.plantuml.which") 

61 def test_check_plantuml_on_path_success(self, mock_which): 

62 mock_which.return_value = "/usr/bin/plantuml" 

63 _check_plantuml_on_path() # Should not raise 

64 

65 @patch("graphable.views.plantuml.which") 

66 def test_check_plantuml_on_path_failure(self, mock_which): 

67 mock_which.return_value = None 

68 with raises(FileNotFoundError): 

69 _check_plantuml_on_path() 

70 

71 @patch("graphable.views.plantuml.run") 

72 @patch("graphable.views.plantuml._check_plantuml_on_path") 

73 @patch("builtins.open", new_callable=mock_open) 

74 def test_export_topology_plantuml_image_svg_success( 

75 self, mock_file, mock_check, mock_run, graph_fixture 

76 ): 

77 g, _, _ = graph_fixture 

78 output_path = Path("output.svg") 

79 

80 mock_run.return_value = MagicMock() 

81 

82 export_topology_plantuml_image(g, output_path) 

83 

84 mock_check.assert_called_once() 

85 mock_run.assert_called_once() 

86 args, kwargs = mock_run.call_args 

87 assert "plantuml" in args[0] 

88 assert "-tsvg" in args[0] 

89 

90 @patch("graphable.views.plantuml.run") 

91 @patch("graphable.views.plantuml._check_plantuml_on_path") 

92 @patch("builtins.open", new_callable=mock_open) 

93 def test_export_topology_plantuml_image_png_success( 

94 self, mock_file, mock_check, mock_run, graph_fixture 

95 ): 

96 g, _, _ = graph_fixture 

97 output_path = Path("output.png") 

98 

99 mock_run.return_value = MagicMock() 

100 

101 export_topology_plantuml_image(g, output_path) 

102 

103 mock_check.assert_called_once() 

104 mock_run.assert_called_once() 

105 args, kwargs = mock_run.call_args 

106 assert "plantuml" in args[0] 

107 assert "-tpng" in args[0] 

108 

109 @patch("graphable.views.plantuml.run") 

110 @patch("graphable.views.plantuml._check_plantuml_on_path") 

111 @patch("builtins.open", new_callable=mock_open) 

112 def test_export_topology_plantuml_image_failure( 

113 self, mock_file, mock_check, mock_run, graph_fixture 

114 ): 

115 g, _, _ = graph_fixture 

116 output_path = Path("output.svg") 

117 

118 mock_run.side_effect = CalledProcessError(1, "plantuml", stderr="error") 

119 

120 with raises(CalledProcessError): 

121 export_topology_plantuml_image(g, output_path) 

122 

123 @patch("graphable.views.plantuml.run") 

124 @patch("graphable.views.plantuml._check_plantuml_on_path") 

125 @patch("builtins.open", new_callable=mock_open) 

126 def test_export_topology_plantuml_image_generic_exception( 

127 self, mock_file, mock_check, mock_run, graph_fixture 

128 ): 

129 g, _, _ = graph_fixture 

130 output_path = Path("output.svg") 

131 

132 mock_run.side_effect = Exception("generic error") 

133 

134 with raises(Exception): 

135 export_topology_plantuml_image(g, output_path) 

136 

137 def test_create_topology_plantuml_clustering(self): 

138 a = Graphable("A") 

139 a.add_tag("group1") 

140 b = Graphable("B") 

141 b.add_tag("group1") 

142 c = Graphable("C") 

143 c.add_tag("group2") 

144 

145 g = Graph() 

146 g.add_edge(a, b) 

147 g.add_edge(b, c) 

148 

149 from graphable.views.plantuml import PlantUmlStylingConfig 

150 

151 config = PlantUmlStylingConfig( 

152 cluster_by_tag=True, node_ref_fnc=lambda n: n.reference 

153 ) 

154 

155 puml = create_topology_plantuml(g, config) 

156 

157 assert 'package "group1" {' in puml 

158 assert 'package "group2" {' in puml 

159 assert 'node "A" as A' in puml 

160 assert 'node "B" as B' in puml 

161 assert 'node "C" as C' in puml 

162 assert "A --> B" in puml 

163 assert "B --> C" in puml