1+ import unittest
2+ import tempfile
3+ import shutil
4+ from pathlib import Path
5+ from click .testing import CliRunner
6+ from concore_cli .cli import cli
7+
8+ class TestGraphValidation (unittest .TestCase ):
9+
10+ def setUp (self ):
11+ self .runner = CliRunner ()
12+ self .temp_dir = tempfile .mkdtemp ()
13+
14+ def tearDown (self ):
15+ if Path (self .temp_dir ).exists ():
16+ shutil .rmtree (self .temp_dir )
17+
18+ def create_graph_file (self , filename , content ):
19+ filepath = Path (self .temp_dir ) / filename
20+ with open (filepath , 'w' ) as f :
21+ f .write (content )
22+ return str (filepath )
23+
24+ def test_validate_corrupted_xml (self ):
25+ content = '<graphml><node id="n0">'
26+ filepath = self .create_graph_file ('corrupted.graphml' , content )
27+
28+ result = self .runner .invoke (cli , ['validate' , filepath ])
29+
30+ self .assertIn ('Validation failed' , result .output )
31+ self .assertIn ('Invalid XML' , result .output )
32+
33+ def test_validate_empty_file (self ):
34+ filepath = self .create_graph_file ('empty.graphml' , '' )
35+
36+ result = self .runner .invoke (cli , ['validate' , filepath ])
37+
38+ self .assertIn ('Validation failed' , result .output )
39+ self .assertIn ('File is empty' , result .output )
40+
41+ def test_validate_missing_node_id (self ):
42+ content = '''
43+ <graphml xmlns:y="http://www.yworks.com/xml/graphml">
44+ <graph id="G" edgedefault="directed">
45+ <node>
46+ <data key="d0"><y:NodeLabel>n0:script.py</y:NodeLabel></data>
47+ </node>
48+ </graph>
49+ </graphml>
50+ '''
51+ filepath = self .create_graph_file ('missing_id.graphml' , content )
52+ result = self .runner .invoke (cli , ['validate' , filepath ])
53+ self .assertIn ('Validation failed' , result .output )
54+ self .assertIn ("Node missing required 'id' attribute" , result .output )
55+
56+ def test_validate_missing_edgedefault (self ):
57+ content = '''
58+ <graphml xmlns:y="http://www.yworks.com/xml/graphml">
59+ <graph id="G">
60+ <node id="n0">
61+ <data key="d0"><y:NodeLabel>n0:script.py</y:NodeLabel></data>
62+ </node>
63+ </graph>
64+ </graphml>
65+ '''
66+ filepath = self .create_graph_file ('missing_default.graphml' , content )
67+ result = self .runner .invoke (cli , ['validate' , filepath ])
68+ self .assertIn ('Validation failed' , result .output )
69+ self .assertIn ("Graph missing required 'edgedefault'" , result .output )
70+
71+ def test_validate_missing_root_element (self ):
72+ content = '<?xml version="1.0"?><other_root></other_root>'
73+ filepath = self .create_graph_file ('not_graphml.xml' , content )
74+
75+ result = self .runner .invoke (cli , ['validate' , filepath ])
76+
77+ self .assertIn ('Validation failed' , result .output )
78+ self .assertIn ('missing <graphml> root element' , result .output )
79+
80+ def test_validate_broken_edges (self ):
81+ content = '''
82+ <graphml xmlns:y="http://www.yworks.com/xml/graphml">
83+ <graph id="G" edgedefault="directed">
84+ <node id="n0">
85+ <data key="d0"><y:NodeLabel>n0:script.py</y:NodeLabel></data>
86+ </node>
87+ <edge source="n0" target="n1"/>
88+ </graph>
89+ </graphml>
90+ '''
91+ filepath = self .create_graph_file ('bad_edge.graphml' , content )
92+
93+ result = self .runner .invoke (cli , ['validate' , filepath ])
94+
95+ self .assertIn ('Validation failed' , result .output )
96+ self .assertIn ('Edge references non-existent target node' , result .output )
97+
98+ def test_validate_node_missing_filename (self ):
99+ content = '''
100+ <graphml xmlns:y="http://www.yworks.com/xml/graphml">
101+ <graph id="G" edgedefault="directed">
102+ <node id="n0">
103+ <data key="d0"><y:NodeLabel>n0:</y:NodeLabel></data>
104+ </node>
105+ </graph>
106+ </graphml>
107+ '''
108+ filepath = self .create_graph_file ('bad_node.graphml' , content )
109+
110+ result = self .runner .invoke (cli , ['validate' , filepath ])
111+
112+ self .assertIn ('Validation failed' , result .output )
113+ self .assertIn ('has no filename' , result .output )
114+
115+ def test_validate_valid_graph (self ):
116+ content = '''
117+ <graphml xmlns:y="http://www.yworks.com/xml/graphml">
118+ <graph id="G" edgedefault="directed">
119+ <node id="n0">
120+ <data key="d0"><y:NodeLabel>n0:script.py</y:NodeLabel></data>
121+ </node>
122+ </graph>
123+ </graphml>
124+ '''
125+ filepath = self .create_graph_file ('valid.graphml' , content )
126+
127+ result = self .runner .invoke (cli , ['validate' , filepath ])
128+
129+ self .assertIn ('Validation passed' , result .output )
130+ self .assertIn ('Workflow is valid' , result .output )
131+
132+ if __name__ == '__main__' :
133+ unittest .main ()
0 commit comments