@@ -304,3 +304,210 @@ fn test_copilot_returns_empty_transcript_in_remote_containers() {
304304 }
305305 }
306306}
307+
308+ // ============================================================================
309+ // Tests for before_edit (human checkpoint) and after_edit (AI checkpoint) logic
310+ // ============================================================================
311+
312+ #[ test]
313+ fn test_copilot_preset_before_edit_human_checkpoint ( ) {
314+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
315+ AgentCheckpointFlags , AgentCheckpointPreset ,
316+ } ;
317+
318+ let hook_input = json ! ( {
319+ "hook_event_name" : "before_edit" ,
320+ "workspaceFolder" : "/Users/test/project" ,
321+ "will_edit_filepaths" : [ "/Users/test/project/file.ts" ] ,
322+ "dirtyFiles" : {
323+ "/Users/test/project/file.ts" : "console.log('hello');"
324+ }
325+ } ) ;
326+
327+ let flags = AgentCheckpointFlags {
328+ hook_input : Some ( hook_input. to_string ( ) ) ,
329+ } ;
330+
331+ let preset = GithubCopilotPreset ;
332+ let result = preset. run ( flags) ;
333+
334+ assert ! ( result. is_ok( ) ) ;
335+ let run_result = result. unwrap ( ) ;
336+
337+ // Verify it's a human checkpoint
338+ assert_eq ! (
339+ run_result. checkpoint_kind,
340+ git_ai:: authorship:: working_log:: CheckpointKind :: Human
341+ ) ;
342+
343+ // Verify will_edit_filepaths is set
344+ assert ! ( run_result. will_edit_filepaths. is_some( ) ) ;
345+ assert_eq ! (
346+ run_result. will_edit_filepaths. unwrap( ) ,
347+ vec![ "/Users/test/project/file.ts" ]
348+ ) ;
349+
350+ // Verify edited_filepaths is None for human checkpoints
351+ assert ! ( run_result. edited_filepaths. is_none( ) ) ;
352+
353+ // Verify transcript is None for human checkpoints
354+ assert ! ( run_result. transcript. is_none( ) ) ;
355+
356+ // Verify dirty files are included
357+ assert ! ( run_result. dirty_files. is_some( ) ) ;
358+ let dirty_files = run_result. dirty_files . unwrap ( ) ;
359+ assert_eq ! ( dirty_files. len( ) , 1 ) ;
360+ assert_eq ! (
361+ dirty_files. get( "/Users/test/project/file.ts" ) . unwrap( ) ,
362+ "console.log('hello');"
363+ ) ;
364+
365+ // Verify agent_id uses "human" tool
366+ assert_eq ! ( run_result. agent_id. tool, "human" ) ;
367+ assert_eq ! ( run_result. agent_id. id, "human" ) ;
368+ assert_eq ! ( run_result. agent_id. model, "human" ) ;
369+ }
370+
371+ #[ test]
372+ fn test_copilot_preset_before_edit_requires_will_edit_filepaths ( ) {
373+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
374+ AgentCheckpointFlags , AgentCheckpointPreset ,
375+ } ;
376+
377+ let hook_input = json ! ( {
378+ "hook_event_name" : "before_edit" ,
379+ "workspaceFolder" : "/Users/test/project" ,
380+ "dirtyFiles" : { }
381+ } ) ;
382+
383+ let flags = AgentCheckpointFlags {
384+ hook_input : Some ( hook_input. to_string ( ) ) ,
385+ } ;
386+
387+ let preset = GithubCopilotPreset ;
388+ let result = preset. run ( flags) ;
389+
390+ // Should fail because will_edit_filepaths is missing
391+ assert ! ( result. is_err( ) ) ;
392+ assert ! ( result
393+ . unwrap_err( )
394+ . to_string( )
395+ . contains( "will_edit_filepaths is required" ) ) ;
396+ }
397+
398+ #[ test]
399+ fn test_copilot_preset_before_edit_requires_non_empty_filepaths ( ) {
400+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
401+ AgentCheckpointFlags , AgentCheckpointPreset ,
402+ } ;
403+
404+ let hook_input = json ! ( {
405+ "hook_event_name" : "before_edit" ,
406+ "workspaceFolder" : "/Users/test/project" ,
407+ "will_edit_filepaths" : [ ] ,
408+ "dirtyFiles" : { }
409+ } ) ;
410+
411+ let flags = AgentCheckpointFlags {
412+ hook_input : Some ( hook_input. to_string ( ) ) ,
413+ } ;
414+
415+ let preset = GithubCopilotPreset ;
416+ let result = preset. run ( flags) ;
417+
418+ // Should fail because will_edit_filepaths is empty
419+ assert ! ( result. is_err( ) ) ;
420+ assert ! ( result
421+ . unwrap_err( )
422+ . to_string( )
423+ . contains( "will_edit_filepaths cannot be empty" ) ) ;
424+ }
425+
426+ #[ test]
427+ fn test_copilot_preset_after_edit_requires_session_id ( ) {
428+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
429+ AgentCheckpointFlags , AgentCheckpointPreset ,
430+ } ;
431+
432+ let hook_input = json ! ( {
433+ "hook_event_name" : "after_edit" ,
434+ "workspaceFolder" : "/Users/test/project" ,
435+ "dirtyFiles" : { }
436+ } ) ;
437+
438+ let flags = AgentCheckpointFlags {
439+ hook_input : Some ( hook_input. to_string ( ) ) ,
440+ } ;
441+
442+ let preset = GithubCopilotPreset ;
443+ let result = preset. run ( flags) ;
444+
445+ // Should fail because chatSessionPath is missing for after_edit
446+ assert ! ( result. is_err( ) ) ;
447+ assert ! ( result
448+ . unwrap_err( )
449+ . to_string( )
450+ . contains( "chatSessionPath not found" ) ) ;
451+ }
452+
453+ #[ test]
454+ fn test_copilot_preset_invalid_hook_event_name ( ) {
455+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
456+ AgentCheckpointFlags , AgentCheckpointPreset ,
457+ } ;
458+
459+ let hook_input = json ! ( {
460+ "hook_event_name" : "invalid_event" ,
461+ "workspaceFolder" : "/Users/test/project"
462+ } ) ;
463+
464+ let flags = AgentCheckpointFlags {
465+ hook_input : Some ( hook_input. to_string ( ) ) ,
466+ } ;
467+
468+ let preset = GithubCopilotPreset ;
469+ let result = preset. run ( flags) ;
470+
471+ // Should fail with invalid hook event name
472+ assert ! ( result. is_err( ) ) ;
473+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "Invalid hook_event_name" ) ) ;
474+ }
475+
476+ #[ test]
477+ fn test_copilot_preset_before_edit_multiple_files ( ) {
478+ use git_ai:: commands:: checkpoint_agent:: agent_presets:: {
479+ AgentCheckpointFlags , AgentCheckpointPreset ,
480+ } ;
481+
482+ let hook_input = json ! ( {
483+ "hook_event_name" : "before_edit" ,
484+ "workspaceFolder" : "/Users/test/project" ,
485+ "will_edit_filepaths" : [
486+ "/Users/test/project/file1.ts" ,
487+ "/Users/test/project/file2.ts" ,
488+ "/Users/test/project/file3.ts"
489+ ] ,
490+ "dirtyFiles" : {
491+ "/Users/test/project/file1.ts" : "content1" ,
492+ "/Users/test/project/file2.ts" : "content2"
493+ }
494+ } ) ;
495+
496+ let flags = AgentCheckpointFlags {
497+ hook_input : Some ( hook_input. to_string ( ) ) ,
498+ } ;
499+
500+ let preset = GithubCopilotPreset ;
501+ let result = preset. run ( flags) ;
502+
503+ assert ! ( result. is_ok( ) ) ;
504+ let run_result = result. unwrap ( ) ;
505+
506+ // Verify all files are in will_edit_filepaths
507+ assert ! ( run_result. will_edit_filepaths. is_some( ) ) ;
508+ let files = run_result. will_edit_filepaths . unwrap ( ) ;
509+ assert_eq ! ( files. len( ) , 3 ) ;
510+ assert ! ( files. contains( & "/Users/test/project/file1.ts" . to_string( ) ) ) ;
511+ assert ! ( files. contains( & "/Users/test/project/file2.ts" . to_string( ) ) ) ;
512+ assert ! ( files. contains( & "/Users/test/project/file3.ts" . to_string( ) ) ) ;
513+ }
0 commit comments