@@ -473,3 +473,172 @@ func TestExecuteDirect_Grep(t *testing.T) {
473473 t .Fatalf ("expected match, got: %s" , r .Output )
474474 }
475475}
476+
477+ // ── list_files tests ──
478+
479+ func TestListFiles_Basic (t * testing.T ) {
480+ dir := t .TempDir ()
481+ os .WriteFile (filepath .Join (dir , "a.txt" ), []byte ("" ), 0o644 )
482+ os .WriteFile (filepath .Join (dir , "b.go" ), []byte ("" ), 0o644 )
483+ sub := filepath .Join (dir , "sub" )
484+ os .MkdirAll (sub , 0o755 )
485+ os .WriteFile (filepath .Join (sub , "c.md" ), []byte ("" ), 0o644 )
486+
487+ // Save original working directory
488+ originalWd , _ := os .Getwd ()
489+ defer os .Chdir (originalWd )
490+
491+ // Change to a different directory to test relative paths
492+ os .Chdir ("/tmp" )
493+
494+ r := listFiles (map [string ]string {
495+ "directory" : dir ,
496+ }, 0 )
497+
498+ if ! r .Success {
499+ t .Fatalf ("expected success, got error: %s" , r .Error )
500+ }
501+
502+ // Check that paths are relative to 'dir', not to cwd
503+ lines := strings .Split (strings .TrimSpace (r .Output ), "\n " )
504+ expected := []string {"a.txt" , "b.go" , "sub/" , "sub/c.md" }
505+ if len (lines ) != len (expected ) {
506+ t .Fatalf ("expected %d lines, got %d: %s" , len (expected ), len (lines ), r .Output )
507+ }
508+
509+ for i , line := range lines {
510+ if line != expected [i ] {
511+ t .Errorf ("line %d: got %q, want %q" , i , line , expected [i ])
512+ }
513+ }
514+ }
515+
516+ func TestListFiles_FromWithinDirectory (t * testing.T ) {
517+ dir := t .TempDir ()
518+ os .WriteFile (filepath .Join (dir , "test.txt" ), []byte ("" ), 0o644 )
519+
520+ // Save original working directory
521+ originalWd , _ := os .Getwd ()
522+ defer os .Chdir (originalWd )
523+
524+ // Change to the test directory
525+ os .Chdir (dir )
526+
527+ r := listFiles (map [string ]string {
528+ "directory" : "." ,
529+ }, 0 )
530+
531+ if ! r .Success {
532+ t .Fatalf ("expected success, got error: %s" , r .Error )
533+ }
534+
535+ // Should list files relative to current directory (.)
536+ lines := strings .Split (strings .TrimSpace (r .Output ), "\n " )
537+ if len (lines ) != 1 || lines [0 ] != "test.txt" {
538+ t .Fatalf ("expected ['test.txt'], got %v" , lines )
539+ }
540+ }
541+
542+ func TestListFiles_ExtensionFilter (t * testing.T ) {
543+ dir := t .TempDir ()
544+ os .WriteFile (filepath .Join (dir , "a.txt" ), []byte ("" ), 0o644 )
545+ os .WriteFile (filepath .Join (dir , "b.go" ), []byte ("" ), 0o644 )
546+ os .WriteFile (filepath .Join (dir , "c.go" ), []byte ("" ), 0o644 )
547+
548+ r := listFiles (map [string ]string {
549+ "directory" : dir ,
550+ "extension" : ".go" ,
551+ }, 0 )
552+
553+ if ! r .Success {
554+ t .Fatalf ("expected success, got error: %s" , r .Error )
555+ }
556+
557+ lines := strings .Split (strings .TrimSpace (r .Output ), "\n " )
558+ if len (lines ) != 2 {
559+ t .Fatalf ("expected 2 .go files, got %d: %s" , len (lines ), r .Output )
560+ }
561+ for _ , line := range lines {
562+ if ! strings .HasSuffix (line , ".go" ) {
563+ t .Errorf ("expected .go file, got %q" , line )
564+ }
565+ }
566+ }
567+
568+ func TestListFiles_EmptyDirectory (t * testing.T ) {
569+ dir := t .TempDir ()
570+
571+ r := listFiles (map [string ]string {
572+ "directory" : dir ,
573+ }, 0 )
574+
575+ if ! r .Success {
576+ t .Fatalf ("expected success, got error: %s" , r .Error )
577+ }
578+ if r .Output != "(empty directory)" {
579+ t .Fatalf ("expected '(empty directory)', got %q" , r .Output )
580+ }
581+ }
582+
583+ func TestListFiles_SkipsHiddenAndSpecialDirs (t * testing.T ) {
584+ dir := t .TempDir ()
585+ os .WriteFile (filepath .Join (dir , "normal.txt" ), []byte ("" ), 0o644 )
586+ os .WriteFile (filepath .Join (dir , ".hidden" ), []byte ("" ), 0o644 )
587+ gitDir := filepath .Join (dir , ".git" )
588+ os .MkdirAll (gitDir , 0o755 )
589+ os .WriteFile (filepath .Join (gitDir , "config" ), []byte ("" ), 0o644 )
590+ nodeModules := filepath .Join (dir , "node_modules" )
591+ os .MkdirAll (nodeModules , 0o755 )
592+ os .WriteFile (filepath .Join (nodeModules , "package.json" ), []byte ("" ), 0o644 )
593+
594+ r := listFiles (map [string ]string {
595+ "directory" : dir ,
596+ }, 0 )
597+
598+ if ! r .Success {
599+ t .Fatalf ("expected success, got error: %s" , r .Error )
600+ }
601+
602+ // Should only show normal.txt
603+ lines := strings .Split (strings .TrimSpace (r .Output ), "\n " )
604+ if len (lines ) != 1 || lines [0 ] != "normal.txt" {
605+ t .Fatalf ("expected only 'normal.txt', got %v" , lines )
606+ }
607+ }
608+
609+ func TestListFiles_SubdirectoryListing (t * testing.T ) {
610+ dir := t .TempDir ()
611+ sub := filepath .Join (dir , "subdir" )
612+ os .MkdirAll (sub , 0o755 )
613+ os .WriteFile (filepath .Join (sub , "file.txt" ), []byte ("" ), 0o644 )
614+ os .WriteFile (filepath .Join (sub , "another.md" ), []byte ("" ), 0o644 )
615+
616+ r := listFiles (map [string ]string {
617+ "directory" : sub ,
618+ }, 0 )
619+
620+ if ! r .Success {
621+ t .Fatalf ("expected success, got error: %s" , r .Error )
622+ }
623+
624+ // Should list files relative to subdir
625+ lines := strings .Split (strings .TrimSpace (r .Output ), "\n " )
626+ expected := []string {"file.txt" , "another.md" }
627+ if len (lines ) != len (expected ) {
628+ t .Fatalf ("expected %d lines, got %d: %s" , len (expected ), len (lines ), r .Output )
629+ }
630+ // Check that we have both files (order may vary)
631+ hasFileTxt := false
632+ hasAnotherMd := false
633+ for _ , line := range lines {
634+ if line == "file.txt" {
635+ hasFileTxt = true
636+ }
637+ if line == "another.md" {
638+ hasAnotherMd = true
639+ }
640+ }
641+ if ! hasFileTxt || ! hasAnotherMd {
642+ t .Errorf ("missing expected files: got %v" , lines )
643+ }
644+ }
0 commit comments