66
77/* eslint-disable @typescript-eslint/no-explicit-any */
88
9- const mockEnsureCorrectEdit = vi . hoisted ( ( ) => vi . fn ( ) ) ;
10- const mockGenerateJson = vi . hoisted ( ( ) => vi . fn ( ) ) ;
119const mockOpenDiff = vi . hoisted ( ( ) => vi . fn ( ) ) ;
1210
1311import { IDEConnectionStatus } from '../ide/ide-client.js' ;
1412
15- vi . mock ( '../utils/editCorrector.js' , ( ) => ( {
16- ensureCorrectEdit : mockEnsureCorrectEdit ,
17- } ) ) ;
18-
19- vi . mock ( '../core/client.js' , ( ) => ( {
20- GeminiClient : vi . fn ( ) . mockImplementation ( ( ) => ( {
21- generateJson : mockGenerateJson ,
22- } ) ) ,
23- } ) ) ;
24-
2513vi . mock ( '../utils/editor.js' , ( ) => ( {
2614 openDiff : mockOpenDiff ,
2715} ) ) ;
@@ -42,7 +30,6 @@ import fs from 'node:fs';
4230import os from 'node:os' ;
4331import type { Config } from '../config/config.js' ;
4432import { ApprovalMode } from '../config/config.js' ;
45- import type { Content , Part , SchemaUnion } from '@google/genai' ;
4633import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js' ;
4734import { StandardFileSystemService } from '../services/fileSystemService.js' ;
4835
@@ -51,30 +38,20 @@ describe('EditTool', () => {
5138 let tempDir : string ;
5239 let rootDir : string ;
5340 let mockConfig : Config ;
54- let geminiClient : any ;
55-
5641 beforeEach ( ( ) => {
5742 vi . restoreAllMocks ( ) ;
5843 tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'edit-tool-test-' ) ) ;
5944 rootDir = path . join ( tempDir , 'root' ) ;
6045 fs . mkdirSync ( rootDir ) ;
6146
62- geminiClient = {
63- generateJson : mockGenerateJson , // mockGenerateJson is already defined and hoisted
64- } ;
65-
6647 mockConfig = {
67- getGeminiClient : vi . fn ( ) . mockReturnValue ( geminiClient ) ,
6848 getTargetDir : ( ) => rootDir ,
6949 getApprovalMode : vi . fn ( ) ,
7050 setApprovalMode : vi . fn ( ) ,
7151 getWorkspaceContext : ( ) => createMockWorkspaceContext ( rootDir ) ,
7252 getFileSystemService : ( ) => new StandardFileSystemService ( ) ,
7353 getIdeClient : ( ) => undefined ,
7454 getIdeMode : ( ) => false ,
75- // getGeminiConfig: () => ({ apiKey: 'test-api-key' }), // This was not a real Config method
76- // Add other properties/methods of Config if EditTool uses them
77- // Minimal other methods to satisfy Config type if needed by EditTool constructor or other direct uses:
7855 getApiKey : ( ) => 'test-api-key' ,
7956 getModel : ( ) => 'test-model' ,
8057 getSandbox : ( ) => false ,
@@ -98,65 +75,6 @@ describe('EditTool', () => {
9875 // Default to not skipping confirmation
9976 ( mockConfig . getApprovalMode as Mock ) . mockReturnValue ( ApprovalMode . DEFAULT ) ;
10077
101- // Reset mocks and set default implementation for ensureCorrectEdit
102- mockEnsureCorrectEdit . mockReset ( ) ;
103- mockEnsureCorrectEdit . mockImplementation (
104- async ( _ , currentContent , params ) => {
105- let occurrences = 0 ;
106- if ( params . old_string && currentContent ) {
107- // Simple string counting for the mock
108- let index = currentContent . indexOf ( params . old_string ) ;
109- while ( index !== - 1 ) {
110- occurrences ++ ;
111- index = currentContent . indexOf ( params . old_string , index + 1 ) ;
112- }
113- } else if ( params . old_string === '' ) {
114- occurrences = 0 ; // Creating a new file
115- }
116- return Promise . resolve ( { params, occurrences } ) ;
117- } ,
118- ) ;
119-
120- // Default mock for generateJson to return the snippet unchanged
121- mockGenerateJson . mockReset ( ) ;
122- mockGenerateJson . mockImplementation (
123- async ( contents : Content [ ] , schema : SchemaUnion ) => {
124- // The problematic_snippet is the last part of the user's content
125- const userContent = contents . find ( ( c : Content ) => c . role === 'user' ) ;
126- let promptText = '' ;
127- if ( userContent && userContent . parts ) {
128- promptText = userContent . parts
129- . filter ( ( p : Part ) => typeof ( p as any ) . text === 'string' )
130- . map ( ( p : Part ) => ( p as any ) . text )
131- . join ( '\n' ) ;
132- }
133- const snippetMatch = promptText . match (
134- / P r o b l e m a t i c t a r g e t s n i p p e t : \n ` ` ` \n ( [ \s \S ] * ?) \n ` ` ` / ,
135- ) ;
136- const problematicSnippet =
137- snippetMatch && snippetMatch [ 1 ] ? snippetMatch [ 1 ] : '' ;
138-
139- if ( ( ( schema as any ) . properties as any ) ?. corrected_target_snippet ) {
140- return Promise . resolve ( {
141- corrected_target_snippet : problematicSnippet ,
142- } ) ;
143- }
144- if ( ( ( schema as any ) . properties as any ) ?. corrected_new_string ) {
145- // For new_string correction, we might need more sophisticated logic,
146- // but for now, returning original is a safe default if not specified by a test.
147- const originalNewStringMatch = promptText . match (
148- / o r i g i n a l _ n e w _ s t r i n g \( w h a t w a s i n t e n d e d t o r e p l a c e o r i g i n a l _ o l d _ s t r i n g \) : \n ` ` ` \n ( [ \s \S ] * ?) \n ` ` ` / ,
149- ) ;
150- const originalNewString =
151- originalNewStringMatch && originalNewStringMatch [ 1 ]
152- ? originalNewStringMatch [ 1 ]
153- : '' ;
154- return Promise . resolve ( { corrected_new_string : originalNewString } ) ;
155- }
156- return Promise . resolve ( { } ) ; // Default empty object if schema doesn't match
157- } ,
158- ) ;
159-
16078 tool = new EditTool ( mockConfig ) ;
16179 } ) ;
16280
@@ -249,8 +167,6 @@ describe('EditTool', () => {
249167 old_string : 'old' ,
250168 new_string : 'new' ,
251169 } ;
252- // ensureCorrectEdit will be called by shouldConfirmExecute
253- mockEnsureCorrectEdit . mockResolvedValueOnce ( { params, occurrences : 1 } ) ;
254170 const invocation = tool . build ( params ) ;
255171 const confirmation = await invocation . shouldConfirmExecute (
256172 new AbortController ( ) . signal ,
@@ -264,29 +180,27 @@ describe('EditTool', () => {
264180 ) ;
265181 } ) ;
266182
267- it ( 'should return false if old_string is not found (ensureCorrectEdit returns 0) ' , async ( ) => {
183+ it ( 'should return false if old_string is not found' , async ( ) => {
268184 fs . writeFileSync ( filePath , 'some content here' ) ;
269185 const params : EditToolParams = {
270186 file_path : filePath ,
271187 old_string : 'not_found' ,
272188 new_string : 'new' ,
273189 } ;
274- mockEnsureCorrectEdit . mockResolvedValueOnce ( { params, occurrences : 0 } ) ;
275190 const invocation = tool . build ( params ) ;
276191 const confirmation = await invocation . shouldConfirmExecute (
277192 new AbortController ( ) . signal ,
278193 ) ;
279194 expect ( confirmation ) . toBe ( false ) ;
280195 } ) ;
281196
282- it ( 'should return false if multiple occurrences of old_string are found (ensureCorrectEdit returns > 1) ' , async ( ) => {
197+ it ( 'should return false if multiple occurrences of old_string are found' , async ( ) => {
283198 fs . writeFileSync ( filePath , 'old old content here' ) ;
284199 const params : EditToolParams = {
285200 file_path : filePath ,
286201 old_string : 'old' ,
287202 new_string : 'new' ,
288203 } ;
289- mockEnsureCorrectEdit . mockResolvedValueOnce ( { params, occurrences : 2 } ) ;
290204 const invocation = tool . build ( params ) ;
291205 const confirmation = await invocation . shouldConfirmExecute (
292206 new AbortController ( ) . signal ,
@@ -302,10 +216,6 @@ describe('EditTool', () => {
302216 old_string : '' ,
303217 new_string : 'new file content' ,
304218 } ;
305- // ensureCorrectEdit might not be called if old_string is empty,
306- // as shouldConfirmExecute handles this for diff generation.
307- // If it is called, it should return 0 occurrences for a new file.
308- mockEnsureCorrectEdit . mockResolvedValueOnce ( { params, occurrences : 0 } ) ;
309219 const invocation = tool . build ( params ) ;
310220 const confirmation = await invocation . shouldConfirmExecute (
311221 new AbortController ( ) . signal ,
@@ -319,65 +229,9 @@ describe('EditTool', () => {
319229 ) ;
320230 } ) ;
321231
322- it ( 'should use corrected params from ensureCorrectEdit for diff generation' , async ( ) => {
323- const originalContent = 'This is the original string to be replaced.' ;
324- const originalOldString = 'original string' ;
325- const originalNewString = 'new string' ;
326-
327- const correctedOldString = 'original string to be replaced' ; // More specific
328- const correctedNewString = 'completely new string' ; // Different replacement
329- const expectedFinalContent = 'This is the completely new string.' ;
330-
331- fs . writeFileSync ( filePath , originalContent ) ;
332- const params : EditToolParams = {
333- file_path : filePath ,
334- old_string : originalOldString ,
335- new_string : originalNewString ,
336- } ;
337-
338- // The main beforeEach already calls mockEnsureCorrectEdit.mockReset()
339- // Set a specific mock for this test case
340- let mockCalled = false ;
341- mockEnsureCorrectEdit . mockImplementationOnce (
342- async ( _ , content , p , client ) => {
343- mockCalled = true ;
344- expect ( content ) . toBe ( originalContent ) ;
345- expect ( p ) . toBe ( params ) ;
346- expect ( client ) . toBe ( geminiClient ) ;
347- return {
348- params : {
349- file_path : filePath ,
350- old_string : correctedOldString ,
351- new_string : correctedNewString ,
352- } ,
353- occurrences : 1 ,
354- } ;
355- } ,
356- ) ;
357- const invocation = tool . build ( params ) ;
358- const confirmation = ( await invocation . shouldConfirmExecute (
359- new AbortController ( ) . signal ,
360- ) ) as FileDiff ;
361-
362- expect ( mockCalled ) . toBe ( true ) ; // Check if the mock implementation was run
363- // expect(mockEnsureCorrectEdit).toHaveBeenCalledWith(originalContent, params, expect.anything()); // Keep this commented for now
364- expect ( confirmation ) . toEqual (
365- expect . objectContaining ( {
366- title : `Confirm Edit: ${ testFile } ` ,
367- fileName : testFile ,
368- } ) ,
369- ) ;
370- // Check that the diff is based on the corrected strings leading to the new state
371- expect ( confirmation . fileDiff ) . toContain ( `-${ originalContent } ` ) ;
372- expect ( confirmation . fileDiff ) . toContain ( `+${ expectedFinalContent } ` ) ;
373-
374- // Verify that applying the correctedOldString and correctedNewString to originalContent
375- // indeed produces the expectedFinalContent, which is what the diff should reflect.
376- const patchedContent = originalContent . replace (
377- correctedOldString , // This was the string identified by ensureCorrectEdit for replacement
378- correctedNewString , // This was the string identified by ensureCorrectEdit as the replacement
379- ) ;
380- expect ( patchedContent ) . toBe ( expectedFinalContent ) ;
232+ // This test is no longer relevant since editCorrector functionality was removed
233+ it . skip ( 'should use corrected params from ensureCorrectEdit for diff generation' , async ( ) => {
234+ // Test skipped - editCorrector functionality removed
381235 } ) ;
382236 } ) ;
383237
@@ -387,20 +241,6 @@ describe('EditTool', () => {
387241
388242 beforeEach ( ( ) => {
389243 filePath = path . join ( rootDir , testFile ) ;
390- // Default for execute tests, can be overridden
391- mockEnsureCorrectEdit . mockImplementation ( async ( _ , content , params ) => {
392- let occurrences = 0 ;
393- if ( params . old_string && content ) {
394- let index = content . indexOf ( params . old_string ) ;
395- while ( index !== - 1 ) {
396- occurrences ++ ;
397- index = content . indexOf ( params . old_string , index + 1 ) ;
398- }
399- } else if ( params . old_string === '' ) {
400- occurrences = 0 ;
401- }
402- return { params, occurrences } ;
403- } ) ;
404244 } ) ;
405245
406246 it ( 'should throw error if file path is not absolute' , async ( ) => {
@@ -433,10 +273,6 @@ describe('EditTool', () => {
433273 new_string : 'new' ,
434274 } ;
435275
436- // Specific mock for this test's execution path in calculateEdit
437- // ensureCorrectEdit is NOT called by calculateEdit, only by shouldConfirmExecute
438- // So, the default mockEnsureCorrectEdit should correctly return 1 occurrence for 'old' in initialContent
439-
440276 const invocation = tool . build ( params ) ;
441277 const result = await invocation . execute ( new AbortController ( ) . signal ) ;
442278
@@ -477,7 +313,6 @@ describe('EditTool', () => {
477313 old_string : 'nonexistent' ,
478314 new_string : 'replacement' ,
479315 } ;
480- // The default mockEnsureCorrectEdit will return 0 occurrences for 'nonexistent'
481316 const invocation = tool . build ( params ) ;
482317 const result = await invocation . execute ( new AbortController ( ) . signal ) ;
483318 expect ( result . llmContent ) . toMatch (
@@ -495,7 +330,6 @@ describe('EditTool', () => {
495330 old_string : 'old' ,
496331 new_string : 'new' ,
497332 } ;
498- // The default mockEnsureCorrectEdit will return 2 occurrences for 'old'
499333 const invocation = tool . build ( params ) ;
500334 const result = await invocation . execute ( new AbortController ( ) . signal ) ;
501335 expect ( result . llmContent ) . toMatch (
@@ -638,8 +472,7 @@ describe('EditTool', () => {
638472 } ) ;
639473
640474 it ( 'should return EDIT_NO_CHANGE error if replacement results in identical content' , async ( ) => {
641- // This can happen if ensureCorrectEdit finds a fuzzy match, but the literal
642- // string replacement with `replaceAll` results in no change.
475+ // This can happen if the literal string replacement with `replaceAll` results in no change.
643476 const initialContent = 'line 1\nline 2\nline 3' ; // Note the double space
644477 fs . writeFileSync ( filePath , initialContent , 'utf8' ) ;
645478 const params : EditToolParams = {
@@ -649,16 +482,12 @@ describe('EditTool', () => {
649482 new_string : 'line 1\nnew line 2\nline 3' ,
650483 } ;
651484
652- // Mock ensureCorrectEdit to simulate it finding a match (e.g., via fuzzy matching)
653- // but it doesn't correct the old_string to the literal content.
654- mockEnsureCorrectEdit . mockResolvedValueOnce ( { params, occurrences : 1 } ) ;
655-
656485 const invocation = tool . build ( params ) ;
657486 const result = await invocation . execute ( new AbortController ( ) . signal ) ;
658487
659- expect ( result . error ?. type ) . toBe ( ToolErrorType . EDIT_NO_CHANGE ) ;
488+ expect ( result . error ?. type ) . toBe ( ToolErrorType . EDIT_NO_OCCURRENCE_FOUND ) ;
660489 expect ( result . returnDisplay ) . toMatch (
661- / N o c h a n g e s t o a p p l y . T h e n e w c o n t e n t i s i d e n t i c a l t o t h e c u r r e n t c o n t e n t ./ ,
490+ / F a i l e d t o e d i t , c o u l d n o t f i n d t h e s t r i n g t o r e p l a c e ./ ,
662491 ) ;
663492 // Ensure the file was not actually changed
664493 expect ( fs . readFileSync ( filePath , 'utf8' ) ) . toBe ( initialContent ) ;
@@ -870,10 +699,6 @@ describe('EditTool', () => {
870699 old_string : 'old' ,
871700 new_string : 'new' ,
872701 } ;
873- mockEnsureCorrectEdit . mockResolvedValueOnce ( {
874- params : { ...params , old_string : 'old' , new_string : 'new' } ,
875- occurrences : 1 ,
876- } ) ;
877702 ideClient . openDiff . mockResolvedValueOnce ( {
878703 status : 'accepted' ,
879704 content : modifiedContent ,
0 commit comments