Coverage for tests / unit / cli / test_rich_cli.py: 100%

161 statements  

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

1from pathlib import Path 

2from unittest.mock import patch 

3 

4from typer.testing import CliRunner 

5 

6from graphable.cli.rich_cli import app 

7from graphable.enums import Engine 

8 

9runner = CliRunner() 

10 

11 

12@patch("graphable.cli.rich_cli.info_command") 

13def test_rich_cli_info(mock_info): 

14 """Verify info command in Rich CLI.""" 

15 mock_info.return_value = { 

16 "nodes": 2, 

17 "edges": 1, 

18 "sources": ["A"], 

19 "sinks": ["B"], 

20 "project_duration": 10.0, 

21 "critical_path_length": 2, 

22 } 

23 result = runner.invoke(app, ["info", "test.json"]) 

24 assert result.exit_code == 0 

25 assert "Nodes" in result.stdout 

26 assert "Edges" in result.stdout 

27 assert "Sources" in result.stdout 

28 assert "Sinks" in result.stdout 

29 assert "Project Duration" in result.stdout 

30 assert "Critical Path Length" in result.stdout 

31 mock_info.assert_called_once_with( 

32 Path("test.json"), tag=None, upstream_of=None, downstream_of=None 

33 ) 

34 

35 

36@patch("graphable.cli.rich_cli.info_command") 

37def test_rich_cli_info_error(mock_info): 

38 """Verify info command handles error in Rich CLI.""" 

39 mock_info.side_effect = Exception("Test error") 

40 result = runner.invoke(app, ["info", "test.json"]) 

41 assert result.exit_code == 1 

42 assert "Error:" in result.stdout 

43 assert "Test error" in result.stdout 

44 

45 

46@patch("graphable.cli.rich_cli.check_command") 

47def test_rich_cli_check_valid(mock_check): 

48 """Verify check command (valid) in Rich CLI.""" 

49 mock_check.return_value = {"valid": True, "error": None} 

50 result = runner.invoke(app, ["check", "test.json"]) 

51 assert result.exit_code == 0 

52 assert "Graph is valid!" in result.stdout 

53 

54 

55@patch("graphable.cli.rich_cli.check_command") 

56def test_rich_cli_check_invalid(mock_check): 

57 """Verify check command (invalid) in Rich CLI.""" 

58 mock_check.return_value = {"valid": False, "error": "Test error"} 

59 result = runner.invoke(app, ["check", "test.json"]) 

60 assert result.exit_code == 1 

61 assert "Graph is invalid:" in result.stdout 

62 assert "Test error" in result.stdout 

63 

64 

65@patch("graphable.cli.rich_cli.reduce_command") 

66def test_rich_cli_reduce(mock_reduce): 

67 """Verify reduce command in Rich CLI.""" 

68 result = runner.invoke(app, ["reduce", "input.json", "output.json"]) 

69 assert result.exit_code == 0 

70 assert "Successfully reduced graph" in result.stdout 

71 mock_reduce.assert_called_once_with( 

72 Path("input.json"), 

73 Path("output.json"), 

74 embed_checksum=False, 

75 tag=None, 

76 upstream_of=None, 

77 downstream_of=None, 

78 ) 

79 

80 

81@patch("graphable.cli.rich_cli.convert_command") 

82def test_rich_cli_convert(mock_convert): 

83 """Verify convert command in Rich CLI.""" 

84 result = runner.invoke(app, ["convert", "input.json", "output.yaml"]) 

85 assert result.exit_code == 0 

86 assert "Successfully converted" in result.stdout 

87 mock_convert.assert_called_once_with( 

88 Path("input.json"), 

89 Path("output.yaml"), 

90 embed_checksum=False, 

91 tag=None, 

92 upstream_of=None, 

93 downstream_of=None, 

94 ) 

95 

96 

97@patch("graphable.cli.rich_cli.render_command") 

98def test_rich_cli_render(mock_render): 

99 """Verify render command in Rich CLI.""" 

100 result = runner.invoke(app, ["render", "i.json", "o.png", "--engine", "mermaid"]) 

101 assert result.exit_code == 0 

102 assert "Successfully rendered" in result.stdout 

103 mock_render.assert_called_once() 

104 # Check that engine is passed correctly 

105 args, kwargs = mock_render.call_args 

106 assert kwargs["engine"] == Engine.MERMAID 

107 

108 

109@patch("graphable.cli.rich_cli.render_command") 

110def test_rich_cli_render_error(mock_render): 

111 """Verify render command error handling in Rich CLI.""" 

112 mock_render.side_effect = Exception("Render error") 

113 result = runner.invoke(app, ["render", "i.json", "o.png"]) 

114 assert result.exit_code == 1 

115 assert "Error:" in result.stdout 

116 assert "Render error" in result.stdout 

117 

118 

119@patch("graphable.cli.rich_cli.checksum_command") 

120def test_rich_cli_checksum(mock_checksum): 

121 """Verify checksum command in Rich CLI.""" 

122 mock_checksum.return_value = "abcdef123456" 

123 result = runner.invoke(app, ["checksum", "test.json"]) 

124 assert result.exit_code == 0 

125 assert "abcdef123456" in result.stdout 

126 

127 

128@patch("graphable.cli.rich_cli.checksum_command") 

129def test_rich_cli_checksum_error(mock_checksum): 

130 """Verify checksum command error handling in Rich CLI.""" 

131 mock_checksum.side_effect = Exception("Checksum error") 

132 result = runner.invoke(app, ["checksum", "test.json"]) 

133 assert result.exit_code == 1 

134 assert "Error:" in result.stdout 

135 assert "Checksum error" in result.stdout 

136 

137 

138@patch("graphable.cli.rich_cli.verify_command") 

139def test_rich_cli_verify_success(mock_verify): 

140 """Verify verify command (success) in Rich CLI.""" 

141 mock_verify.return_value = {"valid": True, "actual": "abc", "expected": "abc"} 

142 result = runner.invoke(app, ["verify", "test.json"]) 

143 assert result.exit_code == 0 

144 assert "Checksum verified successfully." in result.stdout 

145 mock_verify.assert_called_once_with( 

146 Path("test.json"), None, tag=None, upstream_of=None, downstream_of=None 

147 ) 

148 

149 

150@patch("graphable.cli.rich_cli.verify_command") 

151def test_rich_cli_verify_mismatch(mock_verify): 

152 """Verify verify command (mismatch) in Rich CLI.""" 

153 mock_verify.return_value = {"valid": False, "actual": "abc", "expected": "def"} 

154 result = runner.invoke(app, ["verify", "test.json"]) 

155 assert result.exit_code == 1 

156 assert "Checksum mismatch!" in result.stdout 

157 

158 

159@patch("graphable.cli.rich_cli.verify_command") 

160def test_rich_cli_verify_no_checksum(mock_verify): 

161 """Verify verify command (no checksum) in Rich CLI.""" 

162 mock_verify.return_value = {"valid": None, "actual": "abc", "expected": None} 

163 result = runner.invoke(app, ["verify", "test.json"]) 

164 assert result.exit_code == 0 

165 assert "No checksum found to verify." in result.stdout 

166 

167 

168@patch("graphable.cli.rich_cli.verify_command") 

169def test_rich_cli_verify_error(mock_verify): 

170 """Verify verify command error handling in Rich CLI.""" 

171 mock_verify.side_effect = Exception("Verify error") 

172 result = runner.invoke(app, ["verify", "test.json"]) 

173 assert result.exit_code == 1 

174 assert "Error:" in result.stdout 

175 assert "Verify error" in result.stdout 

176 

177 

178@patch("graphable.cli.rich_cli.write_checksum_command") 

179def test_rich_cli_write_checksum(mock_write): 

180 """Verify write-checksum command in Rich CLI.""" 

181 result = runner.invoke(app, ["write-checksum", "test.json", "test.blake2b"]) 

182 assert result.exit_code == 0 

183 assert "Checksum written to" in result.stdout 

184 mock_write.assert_called_once_with( 

185 Path("test.json"), 

186 Path("test.blake2b"), 

187 tag=None, 

188 upstream_of=None, 

189 downstream_of=None, 

190 ) 

191 

192 

193@patch("graphable.cli.rich_cli.write_checksum_command") 

194def test_rich_cli_write_checksum_error(mock_write): 

195 """Verify write-checksum command error handling in Rich CLI.""" 

196 mock_write.side_effect = Exception("Write error") 

197 result = runner.invoke(app, ["write-checksum", "test.json", "test.blake2b"]) 

198 assert result.exit_code == 1 

199 assert "Error:" in result.stdout 

200 assert "Write error" in result.stdout 

201 

202 

203@patch("graphable.cli.rich_cli.diff_command") 

204def test_rich_cli_diff(mock_diff): 

205 """Verify diff command in Rich CLI.""" 

206 mock_diff.return_value = { 

207 "added_nodes": ["C"], 

208 "removed_nodes": ["A"], 

209 "modified_nodes": ["B"], 

210 "added_edges": [("B", "C")], 

211 "removed_edges": [("A", "B")], 

212 "modified_edges": [("X", "Y")], 

213 } 

214 result = runner.invoke(app, ["diff", "v1.json", "v2.json"]) 

215 assert result.exit_code == 0 

216 assert "Graph Diff" in result.stdout 

217 assert "Added Nodes" in result.stdout 

218 assert "Removed Nodes" in result.stdout 

219 assert "Modified Nodes" in result.stdout 

220 assert "Added Edges" in result.stdout 

221 assert "Removed Edges" in result.stdout 

222 assert "Modified Edges" in result.stdout 

223 

224 

225@patch("graphable.cli.rich_cli.diff_visual_command") 

226def test_rich_cli_diff_visual(mock_diff_visual): 

227 """Verify diff command with output (visual) in Rich CLI.""" 

228 result = runner.invoke(app, ["diff", "v1.json", "v2.json", "--output", "diff.svg"]) 

229 assert result.exit_code == 0 

230 assert "Visual diff saved to" in result.stdout 

231 mock_diff_visual.assert_called_once_with( 

232 Path("v1.json"), Path("v2.json"), Path("diff.svg"), tag=None 

233 ) 

234 

235 

236@patch("graphable.cli.rich_cli.diff_command") 

237def test_rich_cli_diff_identical(mock_diff): 

238 """Verify diff command (identical) in Rich CLI.""" 

239 mock_diff.return_value = { 

240 "added_nodes": set(), 

241 "removed_nodes": set(), 

242 "modified_nodes": set(), 

243 "added_edges": set(), 

244 "removed_edges": set(), 

245 "modified_edges": set(), 

246 } 

247 result = runner.invoke(app, ["diff", "v1.json", "v2.json"]) 

248 assert result.exit_code == 0 

249 assert "Graphs are identical." in result.stdout 

250 

251 

252@patch("graphable.cli.rich_cli.diff_command") 

253def test_rich_cli_diff_error(mock_diff): 

254 """Verify diff command error handling in Rich CLI.""" 

255 mock_diff.side_effect = Exception("Diff error") 

256 result = runner.invoke(app, ["diff", "v1.json", "v2.json"]) 

257 assert result.exit_code == 1 

258 assert "Error:" in result.stdout 

259 assert "Diff error" in result.stdout 

260 

261 

262@patch("graphable.cli.rich_cli.serve_command") 

263def test_rich_cli_serve(mock_serve): 

264 """Verify serve command in Rich CLI.""" 

265 result = runner.invoke(app, ["serve", "test.json", "--port", "8080"]) 

266 assert result.exit_code == 0 

267 assert "Serving" in result.stdout 

268 mock_serve.assert_called_once_with(Path("test.json"), port=8080) 

269 

270 

271@patch("graphable.cli.rich_cli.serve_command") 

272def test_rich_cli_serve_error(mock_serve): 

273 """Verify serve command error handling in Rich CLI.""" 

274 mock_serve.side_effect = Exception("Serve error") 

275 result = runner.invoke(app, ["serve", "test.json"]) 

276 assert result.exit_code == 1 

277 assert "Error:" in result.stdout 

278 assert "Serve error" in result.stdout