11"""Unit tests for read_file path parsing with spaces."""
2- import pytest
32import shlex
43
4+ import pytest
5+
56
67def parse_read_cmd (read_cmd : str ) -> tuple [str , str , int | None , int | None ]:
78 """
89 Parse read command and extract path, command, and optional line numbers.
910 This mirrors the logic in tools.py read_file function.
10-
11+
1112 Returns:
1213 (command, path, start_line, end_line)
1314 """
1415 parts = shlex .split (read_cmd .strip ())
1516 if not parts :
1617 raise ValueError ("Empty command" )
17-
18+
1819 start_line = None
1920 end_line = None
20-
21+
2122 if parts [0 ] in ["cat" , "less" ]:
2223 command = parts [0 ]
2324 if len (parts ) < 2 :
2425 raise ValueError (f"Missing file path for { command } " )
25-
26+
2627 # Handle unquoted paths with spaces
2728 path_parts = []
2829 remaining_parts = []
29-
30+
3031 for i , part in enumerate (parts [1 :], 1 ):
3132 try :
3233 int (part )
3334 remaining_parts = parts [i :]
3435 break
3536 except ValueError :
3637 path_parts .append (part )
37-
38+
3839 path = " " .join (path_parts )
39-
40+
4041 if remaining_parts :
4142 if len (remaining_parts ) >= 1 :
4243 start_line = int (remaining_parts [0 ])
4344 if len (remaining_parts ) >= 2 :
4445 end_line = int (remaining_parts [1 ])
4546 else :
4647 command = "cat"
47-
48+
4849 path_parts = []
4950 remaining_parts = []
50-
51+
5152 for i , part in enumerate (parts ):
5253 try :
5354 int (part )
5455 remaining_parts = parts [i :]
5556 break
5657 except ValueError :
5758 path_parts .append (part )
58-
59+
5960 path = " " .join (path_parts ) if path_parts else parts [0 ]
60-
61+
6162 if remaining_parts :
6263 if len (remaining_parts ) >= 1 :
6364 start_line = int (remaining_parts [0 ])
6465 if len (remaining_parts ) >= 2 :
6566 end_line = int (remaining_parts [1 ])
66-
67+
6768 return command , path , start_line , end_line
6869
6970
7071class TestReadFilePathParsing :
7172 """Test suite for read_file path parsing with various scenarios."""
72-
73+
7374 def test_simple_cat (self ):
7475 """Test simple cat command without spaces."""
7576 cmd , path , start , end = parse_read_cmd ("cat /workspace/file.txt" )
7677 assert cmd == "cat"
7778 assert path == "/workspace/file.txt"
7879 assert start is None
7980 assert end is None
80-
81+
8182 def test_simple_less (self ):
8283 """Test simple less command without spaces."""
8384 cmd , path , start , end = parse_read_cmd ("less /workspace/file.txt" )
8485 assert cmd == "less"
8586 assert path == "/workspace/file.txt"
8687 assert start is None
8788 assert end is None
88-
89+
8990 def test_no_command_defaults_to_cat (self ):
9091 """Test that omitting command defaults to cat."""
9192 cmd , path , start , end = parse_read_cmd ("/workspace/file.txt" )
9293 assert cmd == "cat"
9394 assert path == "/workspace/file.txt"
9495 assert start is None
9596 assert end is None
96-
97+
9798 def test_cat_with_start_line (self ):
9899 """Test cat command with start line number."""
99100 cmd , path , start , end = parse_read_cmd ("cat /workspace/file.txt 10" )
100101 assert cmd == "cat"
101102 assert path == "/workspace/file.txt"
102103 assert start == 10
103104 assert end is None
104-
105+
105106 def test_cat_with_start_and_end_lines (self ):
106107 """Test cat command with start and end line numbers."""
107108 cmd , path , start , end = parse_read_cmd ("cat /workspace/file.txt 10 20" )
108109 assert cmd == "cat"
109110 assert path == "/workspace/file.txt"
110111 assert start == 10
111112 assert end == 20
112-
113+
113114 def test_no_command_with_line_numbers (self ):
114115 """Test path without command but with line numbers."""
115116 cmd , path , start , end = parse_read_cmd ("/workspace/file.txt 10 20" )
116117 assert cmd == "cat"
117118 assert path == "/workspace/file.txt"
118119 assert start == 10
119120 assert end == 20
120-
121+
121122 def test_unquoted_path_with_spaces (self ):
122123 """Test unquoted path containing spaces."""
123124 cmd , path , start , end = parse_read_cmd ("less /path/Procare Oct25 cash transactions.md" )
124125 assert cmd == "less"
125126 assert path == "/path/Procare Oct25 cash transactions.md"
126127 assert start is None
127128 assert end is None
128-
129+
129130 def test_unquoted_path_with_one_space (self ):
130131 """Test unquoted path with single space."""
131132 cmd , path , start , end = parse_read_cmd ("cat /workspace/my file.txt" )
132133 assert cmd == "cat"
133134 assert path == "/workspace/my file.txt"
134135 assert start is None
135136 assert end is None
136-
137+
137138 def test_no_command_unquoted_spaces (self ):
138139 """Test path without command containing spaces."""
139140 cmd , path , start , end = parse_read_cmd ("/workspace/my file with spaces.txt" )
140141 assert cmd == "cat"
141142 assert path == "/workspace/my file with spaces.txt"
142143 assert start is None
143144 assert end is None
144-
145+
145146 def test_spaces_with_line_numbers (self ):
146147 """Test unquoted path with spaces AND line numbers."""
147148 cmd , path , start , end = parse_read_cmd ("cat /workspace/my file.txt 1 100" )
148149 assert cmd == "cat"
149150 assert path == "/workspace/my file.txt"
150151 assert start == 1
151152 assert end == 100
152-
153+
153154 def test_spaces_with_start_line_only (self ):
154155 """Test unquoted path with spaces and start line."""
155156 cmd , path , start , end = parse_read_cmd ("less /path/Procare Oct25 transactions.md 50" )
156157 assert cmd == "less"
157158 assert path == "/path/Procare Oct25 transactions.md"
158159 assert start == 50
159160 assert end is None
160-
161+
161162 def test_no_command_spaces_and_lines (self ):
162163 """Test no command with spaces and line numbers."""
163164 cmd , path , start , end = parse_read_cmd ("/workspace/my file.txt 5 15" )
164165 assert cmd == "cat"
165166 assert path == "/workspace/my file.txt"
166167 assert start == 5
167168 assert end == 15
168-
169+
169170 def test_quoted_path_with_spaces (self ):
170171 """Test quoted path with spaces (backwards compatibility)."""
171172 cmd , path , start , end = parse_read_cmd ('cat "/workspace/my file.txt"' )
172173 assert cmd == "cat"
173174 assert path == "/workspace/my file.txt"
174175 assert start is None
175176 assert end is None
176-
177+
177178 def test_quoted_complex_path (self ):
178179 """Test quoted path with complex filename."""
179180 cmd , path , start , end = parse_read_cmd ('less "/path/Procare Oct25 cash transactions.md"' )
180181 assert cmd == "less"
181182 assert path == "/path/Procare Oct25 cash transactions.md"
182183 assert start is None
183184 assert end is None
184-
185+
185186 def test_quoted_path_with_line_numbers (self ):
186187 """Test quoted path with line numbers."""
187188 cmd , path , start , end = parse_read_cmd ('"/workspace/my file.txt" 10 20' )
188189 assert cmd == "cat"
189190 assert path == "/workspace/my file.txt"
190191 assert start == 10
191192 assert end == 20
192-
193+
193194 def test_virtual_parsed_file (self ):
194195 """Test virtual _parsed.{ext}.md file path."""
195196 cmd , path , start , end = parse_read_cmd ("cat /path/file_parsed.pdf.md" )
196197 assert cmd == "cat"
197198 assert path == "/path/file_parsed.pdf.md"
198199 assert start is None
199200 assert end is None
200-
201+
201202 def test_long_tenant_path_with_spaces (self ):
202203 """Test long tenant/user path with spaces in filename."""
203204 input_cmd = "less /tenant:smooth-flame-13/user:xxx/workspace/ws_personal_b37483e0b292/Procare Oct25 cash transactions 9.28-11.1.25_parsed.xlsx.md"
@@ -206,17 +207,17 @@ def test_long_tenant_path_with_spaces(self):
206207 assert path == "/tenant:smooth-flame-13/user:xxx/workspace/ws_personal_b37483e0b292/Procare Oct25 cash transactions 9.28-11.1.25_parsed.xlsx.md"
207208 assert start is None
208209 assert end is None
209-
210+
210211 def test_empty_command_raises_error (self ):
211212 """Test that empty command raises ValueError."""
212213 with pytest .raises (ValueError , match = "Empty command" ):
213214 parse_read_cmd ("" )
214-
215+
215216 def test_cat_without_path_raises_error (self ):
216217 """Test that cat without path raises ValueError."""
217218 with pytest .raises (ValueError , match = "Missing file path" ):
218219 parse_read_cmd ("cat" )
219-
220+
220221 def test_less_without_path_raises_error (self ):
221222 """Test that less without path raises ValueError."""
222223 with pytest .raises (ValueError , match = "Missing file path" ):
0 commit comments