@@ -372,6 +372,167 @@ func TestLookPathExcludingShims_SkipsShimsDir(t *testing.T) {
372372 })
373373}
374374
375+ func TestIsDtvemShimsPath (t * testing.T ) {
376+ // Platform-specific path separator handling: filepath.Join produces
377+ // backslashes on Windows and forward slashes on Unix, which matches what
378+ // real PATH entries look like on each platform.
379+ tests := []struct {
380+ name string
381+ path string
382+ want bool
383+ }{
384+ {
385+ name : "leading-dot dtvem under home" ,
386+ path : filepath .Join ("C:" , "Users" , "testuser" , ".dtvem" , "shims" ),
387+ want : true ,
388+ },
389+ {
390+ name : "no-dot dtvem under XDG data home" ,
391+ path : filepath .Join ("C:" , "Users" , "testuser" , ".local" , "share" , "dtvem" , "shims" ),
392+ want : true ,
393+ },
394+ {
395+ name : "unix style leading-dot" ,
396+ path : "/home/testuser/.dtvem/shims" ,
397+ want : true ,
398+ },
399+ {
400+ name : "unix style XDG" ,
401+ path : "/home/testuser/.local/share/dtvem/shims" ,
402+ want : true ,
403+ },
404+ {
405+ name : "trailing slash is normalized" ,
406+ path : "/home/testuser/.dtvem/shims/" ,
407+ want : true ,
408+ },
409+ {
410+ name : "shims under non-dtvem parent does not match" ,
411+ path : "/home/testuser/something/shims" ,
412+ want : false ,
413+ },
414+ {
415+ name : "dtvem dir without shims leaf does not match" ,
416+ path : "/home/testuser/.dtvem/bin" ,
417+ want : false ,
418+ },
419+ {
420+ name : "empty string" ,
421+ path : "" ,
422+ want : false ,
423+ },
424+ }
425+
426+ for _ , tt := range tests {
427+ t .Run (tt .name , func (t * testing.T ) {
428+ got := IsDtvemShimsPath (tt .path )
429+ if got != tt .want {
430+ t .Errorf ("IsDtvemShimsPath(%q) = %v, want %v" , tt .path , got , tt .want )
431+ }
432+ })
433+ }
434+ }
435+
436+ func TestIsDtvemShimsPath_WindowsCaseInsensitive (t * testing.T ) {
437+ if runtime .GOOS != constants .OSWindows {
438+ t .Skip ("Windows-only: case-insensitive path matching" )
439+ }
440+
441+ cases := []string {
442+ `C:\Users\testuser\.DTVEM\Shims` ,
443+ `C:\Users\testuser\.local\share\DTVEM\SHIMS` ,
444+ `C:\Users\testuser\.Dtvem\shims` ,
445+ }
446+ for _ , p := range cases {
447+ if ! IsDtvemShimsPath (p ) {
448+ t .Errorf ("IsDtvemShimsPath(%q) = false, want true (Windows case-insensitive)" , p )
449+ }
450+ }
451+ }
452+
453+ func TestFindStaleShimsEntries (t * testing.T ) {
454+ // Build paths that look right on the current platform so the
455+ // case-insensitive comparison logic exercises real separators.
456+ currentXDG := filepath .Join ("C:" , "Users" , "testuser" , ".local" , "share" , "dtvem" , "shims" )
457+ staleHome := filepath .Join ("C:" , "Users" , "testuser" , ".dtvem" , "shims" )
458+ unrelated := filepath .Join ("C:" , "Windows" , "System32" )
459+
460+ tests := []struct {
461+ name string
462+ entries []string
463+ current string
464+ want []string
465+ }{
466+ {
467+ name : "stale leading-dot entry alongside current XDG" ,
468+ entries : []string {currentXDG , unrelated , staleHome },
469+ current : currentXDG ,
470+ want : []string {staleHome },
471+ },
472+ {
473+ name : "no stale entries when only current is present" ,
474+ entries : []string {currentXDG , unrelated },
475+ current : currentXDG ,
476+ want : nil ,
477+ },
478+ {
479+ name : "current dir is the leading-dot variant" ,
480+ entries : []string {staleHome , currentXDG , unrelated },
481+ current : staleHome ,
482+ want : []string {currentXDG },
483+ },
484+ {
485+ name : "empty entries are skipped" ,
486+ entries : []string {"" , staleHome , " " },
487+ current : currentXDG ,
488+ want : []string {staleHome },
489+ },
490+ {
491+ name : "preserves original entry strings (not cleaned)" ,
492+ entries : []string {staleHome + string (filepath .Separator ), unrelated },
493+ current : currentXDG ,
494+ want : []string {staleHome + string (filepath .Separator )},
495+ },
496+ {
497+ name : "empty current shimsDir returns nil" ,
498+ entries : []string {staleHome },
499+ current : "" ,
500+ want : nil ,
501+ },
502+ }
503+
504+ for _ , tt := range tests {
505+ t .Run (tt .name , func (t * testing.T ) {
506+ got := FindStaleShimsEntries (tt .entries , tt .current )
507+ if len (got ) != len (tt .want ) {
508+ t .Fatalf ("FindStaleShimsEntries() = %v, want %v" , got , tt .want )
509+ }
510+ for i := range got {
511+ if got [i ] != tt .want [i ] {
512+ t .Errorf ("FindStaleShimsEntries()[%d] = %q, want %q" , i , got [i ], tt .want [i ])
513+ }
514+ }
515+ })
516+ }
517+ }
518+
519+ func TestFindStaleShimsEntries_WindowsCaseInsensitive (t * testing.T ) {
520+ if runtime .GOOS != constants .OSWindows {
521+ t .Skip ("Windows-only: case-insensitive comparison" )
522+ }
523+
524+ current := `C:\Users\testuser\.local\share\dtvem\shims`
525+ // Same logical path as `current` but with mixed casing — should NOT be
526+ // flagged stale.
527+ sameAsCurrentDifferentCase := `C:\Users\TESTUSER\.LOCAL\share\dtvem\SHIMS`
528+ stale := `C:\Users\testuser\.dtvem\shims`
529+
530+ got := FindStaleShimsEntries ([]string {sameAsCurrentDifferentCase , stale }, current )
531+ if len (got ) != 1 || got [0 ] != stale {
532+ t .Errorf ("FindStaleShimsEntries() = %v, want exactly [%q]" , got , stale )
533+ }
534+ }
535+
375536func TestFindExecutableInDir (t * testing.T ) {
376537 tempDir := t .TempDir ()
377538
0 commit comments