@@ -1435,12 +1435,15 @@ describe("read_file tool XML output structure", () => {
14351435 partial : false ,
14361436 }
14371437
1438+ // Create a spy for handleError
1439+ const handleErrorSpy = vi . fn ( )
1440+
14381441 // Execute
14391442 await readFileTool (
14401443 mockCline ,
14411444 toolUse ,
14421445 mockCline . ask ,
1443- vi . fn ( ) ,
1446+ handleErrorSpy ,
14441447 ( result : ToolResponse ) => {
14451448 toolResult = result
14461449 } ,
@@ -1449,7 +1452,122 @@ describe("read_file tool XML output structure", () => {
14491452
14501453 // Verify error is returned for empty path
14511454 expect ( toolResult ) . toContain ( "<error>" )
1452- expect ( mockCline . sayAndCreateMissingParamError ) . toHaveBeenCalled ( )
1455+ expect ( toolResult ) . toContain ( "No valid file paths found" )
1456+ expect ( toolResult ) . toContain ( "Ensure each <file> element contains a non-empty <path> element" )
1457+ expect ( handleErrorSpy ) . toHaveBeenCalled ( )
1458+ } )
1459+
1460+ it ( "should work without line_range parameter (line_range is optional)" , async ( ) => {
1461+ // Test that line_range is truly optional
1462+ const argsWithoutLineRange = `
1463+ <file>
1464+ <path>test/file.txt</path>
1465+ </file>
1466+ `
1467+ const toolUse : ReadFileToolUse = {
1468+ type : "tool_use" ,
1469+ name : "read_file" ,
1470+ params : { args : argsWithoutLineRange } ,
1471+ partial : false ,
1472+ }
1473+
1474+ // Setup mocks
1475+ mockedCountFileLines . mockResolvedValue ( 5 )
1476+ mockedExtractTextFromFile . mockResolvedValue ( "1 | Line 1\n2 | Line 2\n3 | Line 3\n4 | Line 4\n5 | Line 5" )
1477+ mockProvider . getState . mockResolvedValue ( {
1478+ maxReadFileLine : - 1 ,
1479+ maxImageFileSize : 20 ,
1480+ maxTotalImageSize : 20 ,
1481+ } )
1482+
1483+ // Execute
1484+ await readFileTool (
1485+ mockCline ,
1486+ toolUse ,
1487+ mockCline . ask ,
1488+ vi . fn ( ) ,
1489+ ( result : ToolResponse ) => {
1490+ toolResult = result
1491+ } ,
1492+ ( param : ToolParamName , content ?: string ) => content ?? "" ,
1493+ )
1494+
1495+ // Verify the file was read successfully without line_range
1496+ expect ( toolResult ) . toContain ( "<file><path>test/file.txt</path>" )
1497+ expect ( toolResult ) . toContain ( '<content lines="1-5">' )
1498+ expect ( toolResult ) . not . toContain ( "<error>" )
1499+ expect ( toolResult ) . not . toContain ( "line_range" )
1500+ } )
1501+
1502+ it ( "should provide helpful error for malformed XML missing path element" , async ( ) => {
1503+ // Test case simulating what Grok might send - file element with missing path
1504+ const malformedArgs = `
1505+ <file>
1506+ <path></path>
1507+ </file>
1508+ `
1509+ const toolUse : ReadFileToolUse = {
1510+ type : "tool_use" ,
1511+ name : "read_file" ,
1512+ params : { args : malformedArgs } ,
1513+ partial : false ,
1514+ }
1515+
1516+ // Create a spy for handleError
1517+ const handleErrorSpy = vi . fn ( )
1518+
1519+ // Execute
1520+ await readFileTool (
1521+ mockCline ,
1522+ toolUse ,
1523+ mockCline . ask ,
1524+ handleErrorSpy ,
1525+ ( result : ToolResponse ) => {
1526+ toolResult = result
1527+ } ,
1528+ ( param : ToolParamName , content ?: string ) => content ?? "" ,
1529+ )
1530+
1531+ // Verify helpful error is returned
1532+ expect ( toolResult ) . toContain ( "<error>" )
1533+ expect ( toolResult ) . toContain ( "No valid file paths found" )
1534+ expect ( toolResult ) . toContain ( "Ensure each <file> element contains a non-empty <path> element" )
1535+ expect ( handleErrorSpy ) . toHaveBeenCalled ( )
1536+ } )
1537+
1538+ it ( "should provide error when file element has no path child at all" , async ( ) => {
1539+ // Test case where file element exists but has no path child element
1540+ const malformedArgs = `
1541+ <file>
1542+ </file>
1543+ `
1544+ const toolUse : ReadFileToolUse = {
1545+ type : "tool_use" ,
1546+ name : "read_file" ,
1547+ params : { args : malformedArgs } ,
1548+ partial : false ,
1549+ }
1550+
1551+ // Execute
1552+ await readFileTool (
1553+ mockCline ,
1554+ toolUse ,
1555+ mockCline . ask ,
1556+ vi . fn ( ) ,
1557+ ( result : ToolResponse ) => {
1558+ toolResult = result
1559+ } ,
1560+ ( param : ToolParamName , content ?: string ) => content ?? "" ,
1561+ )
1562+
1563+ // When file element has no path child, it falls through to the general error
1564+ expect ( toolResult ) . toContain ( "<error>" )
1565+ expect ( toolResult ) . toContain ( "Missing required parameter" )
1566+ // This is handled by sayAndCreateMissingParamError
1567+ expect ( mockCline . sayAndCreateMissingParamError ) . toHaveBeenCalledWith (
1568+ "read_file" ,
1569+ "args with valid file paths. Expected format: <args><file><path>filepath</path></file></args>" ,
1570+ )
14531571 } )
14541572 } )
14551573} )
0 commit comments