2026-10-24 15:39:59 +00:00
package engine
import (
2026-10-24 15:43:10 +00:00
"errors"
2026-10-24 15:46:36 +00:00
"io/fs"
"os"
2026-10-24 15:43:10 +00:00
"path/filepath"
2026-10-24 15:39:59 +00:00
"runtime"
2026-10-24 15:46:36 +00:00
"strings"
2026-10-24 15:39:59 +00:00
"testing"
)
func TestSearchDescriptionEmptyAndSlugRequirements ( t * testing . T ) {
t . Parallel ( )
if diagnostics := validateSearch ( Search { } , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "zero search diagnostics = %#v" , diagnostics )
}
if diagnostics := validateSearch ( Searches ( ) , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "empty concatenation diagnostics = %#v" , diagnostics )
}
if diagnostics := validateSearch ( Paths ( ) , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "empty path list diagnostics = %#v" , diagnostics )
}
if diagnostics := validateSearch ( Paths ( "relative/config.yml" ) , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "literal path unexpectedly requires a slug: %#v" , diagnostics )
}
generic := SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { GenericFilenames ( ) } } )
if diagnostics := validateSearch ( generic , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "generic-only directory unexpectedly requires a slug: %#v" , diagnostics )
}
if diagnostics := validateSearch ( SearchDirectory ( "configs" ) , "" ) ; len ( diagnostics ) == 0 || diagnostics [ 0 ] . Code != CodeOptionsInvalid {
t . Fatalf ( "default named directory accepted an empty slug: %#v" , diagnostics )
}
if diagnostics := validateSearch ( UnixSystemDirectories ( ) , "myapp" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "valid preset slug diagnostics = %#v" , diagnostics )
}
}
func TestSearchDescriptionSnapshotsInputs ( t * testing . T ) {
t . Parallel ( )
paths := [ ] string { "first.yml" , "second.yml" }
pathSearch := Paths ( paths ... )
paths [ 0 ] = "changed.yml"
customNames := [ ] string { "one.yml" , "two.yml" }
group := CustomFilenames ( customNames ... )
customNames [ 0 ] = "changed.yml"
groups := [ ] FilenameGroup { group }
options := DirectorySearchOptions { Filenames : groups }
directorySearch := SearchDirectory ( "configs" , options )
groups [ 0 ] = GenericFilenames ( )
options . Filenames [ 0 ] = NamedFilenames ( )
searches := [ ] Search { pathSearch , directorySearch }
combined := Searches ( searches ... )
searches [ 0 ] = Search { }
if got := combined . entries [ 0 ] . path ; got != "first.yml" {
t . Fatalf ( "snapshotted path = %q" , got )
}
gotNames := combined . entries [ 2 ] . filenames [ 0 ] . names
if len ( gotNames ) != 2 || gotNames [ 0 ] != "one.yml" || gotNames [ 1 ] != "two.yml" {
t . Fatalf ( "snapshotted custom names = %#v" , gotNames )
}
if diagnostics := validateSearch ( combined , "" ) ; len ( diagnostics ) != 0 {
t . Fatalf ( "snapshot description diagnostics = %#v" , diagnostics )
}
}
func TestDirectoryFilenameDefaultsAndExplicitEmpty ( t * testing . T ) {
t . Parallel ( )
omitted := SearchDirectory ( "configs" )
if len ( omitted . entries ) != 1 || len ( omitted . entries [ 0 ] . filenames ) != 2 || omitted . entries [ 0 ] . filenames [ 0 ] . kind != filenameGroupNamed || omitted . entries [ 0 ] . filenames [ 1 ] . kind != filenameGroupGeneric {
t . Fatalf ( "omitted groups = %#v" , omitted . entries )
}
explicitNil := SearchDirectory ( "configs" , DirectorySearchOptions { } )
if len ( explicitNil . entries [ 0 ] . filenames ) != 2 {
t . Fatalf ( "nil filename selection did not use defaults: %#v" , explicitNil . entries [ 0 ] . filenames )
}
explicitEmpty := SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { } } )
if explicitEmpty . entries [ 0 ] . filenames == nil || len ( explicitEmpty . entries [ 0 ] . filenames ) != 0 {
t . Fatalf ( "explicit empty selection lost its distinction: %#v" , explicitEmpty . entries [ 0 ] . filenames )
}
if len ( validateSearch ( explicitEmpty , "" ) ) != 0 {
t . Fatal ( "explicit empty filename selection should need no slug" )
}
}
func TestSearchDescriptionArityAndErrorConcatenation ( t * testing . T ) {
t . Parallel ( )
tooMany := SearchDirectory ( "configs" , DirectorySearchOptions { } , DirectorySearchOptions { } )
if diagnostics := validateSearch ( tooMany , "app" ) ; len ( diagnostics ) == 0 {
t . Fatal ( "multiple option values should be invalid" )
}
invalid := Paths ( "" )
combined := Searches ( Paths ( "good.yml" ) , invalid )
if diagnostics := validateSearch ( combined , "" ) ; len ( diagnostics ) == 0 {
t . Fatal ( "concatenation lost a constructor error" )
}
if diagnostics := validateSearch ( Paths ( "good.yml" , "" ) , "" ) ; len ( diagnostics ) == 0 {
t . Fatal ( "later invalid path was hidden by an earlier component" )
}
}
func TestSearchDescriptionPreservesFilenameGroupOrder ( t * testing . T ) {
t . Parallel ( )
groups := [ ] FilenameGroup { GenericFilenames ( ) , NamedFilenames ( ) , CustomFilenames ( "override.yml" ) }
search := SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : groups } )
got := search . entries [ 0 ] . filenames
if len ( got ) != 3 || got [ 0 ] . kind != filenameGroupGeneric || got [ 1 ] . kind != filenameGroupNamed || got [ 2 ] . names [ 0 ] != "override.yml" {
t . Fatalf ( "filename group order = %#v" , got )
}
if got := SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { CustomFilenames ( ) } } ) . entries [ 0 ] . filenames [ 0 ] . names ; len ( got ) != 0 {
t . Fatalf ( "empty custom group = %#v" , got )
}
}
func TestFilenameComponentValidation ( t * testing . T ) {
t . Parallel ( )
for _ , value := range [ ] string { "" , "." , ".." , "nested/name" , "bad\x00name" } {
if validFilenameComponent ( value ) {
t . Errorf ( "validFilenameComponent(%q) = true" , value )
}
}
if runtime . GOOS == "windows" {
2026-10-24 19:20:54 +00:00
for _ , value := range [ ] string { "CON" , "CONIN$" , "CONOUT$" , "name." , "bad:name" , "bad\x01name" } {
2026-10-24 15:39:59 +00:00
if validFilenameComponent ( value ) {
t . Errorf ( "Windows component %q unexpectedly accepted" , value )
}
}
2026-10-24 19:20:54 +00:00
// Device names with extensions depend on the Windows version. Match
// Go's platform-aware decision instead of imposing a blanket ban.
for _ , value := range [ ] string { "nul.txt" , "COM¹.log" , "LPT³.txt" , "CON.extra.yml" } {
if got , want := validFilenameComponent ( value ) , filepath . IsLocal ( value ) ; got != want {
t . Errorf ( "Windows component %q validity = %v, want Go's %v" , value , got , want )
}
}
2026-10-24 15:39:59 +00:00
} else {
2026-10-24 19:20:54 +00:00
for _ , value := range [ ] string { "CONIN$" , "CONOUT$" , "nul.txt" } {
2026-10-24 15:39:59 +00:00
if ! validFilenameComponent ( value ) {
2026-10-24 19:20:54 +00:00
t . Errorf ( "Windows component restriction applied to this host: %q" , value )
2026-10-24 15:39:59 +00:00
}
}
}
for _ , value := range [ ] string { "name with spaces" , "naïve" } {
2026-10-24 19:20:54 +00:00
if ! validFilenameComponent ( value ) {
t . Errorf ( "host-valid component %q rejected" , value )
2026-10-24 15:39:59 +00:00
}
}
2026-10-24 19:20:54 +00:00
if diagnostics := validateSearch ( SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { CustomFilenames ( "bad/name" ) } } ) , "" ) ; len ( diagnostics ) == 0 {
t . Fatal ( "invalid custom filename was not retained for validation" )
}
2026-10-24 15:39:59 +00:00
}
2026-10-24 15:43:10 +00:00
func TestSearchCandidatesPreserveDirectoryAndGroupOrder ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
helpers := discoveryDirectoryHelpers { workingDir : func ( ) ( string , error ) { return root , nil } }
search := Searches (
Paths ( "first.yml" ) ,
SearchDirectory ( "settings" , DirectorySearchOptions { Filenames : [ ] FilenameGroup {
GenericFilenames ( ) , CustomFilenames ( "special.yml" ) ,
} } ) ,
Paths ( "last.yml" ) ,
)
candidates := collectSearchCandidates ( t , search , "app" , helpers )
want := [ ] string {
filepath . Join ( root , "first.yml" ) ,
filepath . Join ( root , "settings" , "config.yml" ) ,
filepath . Join ( root , "settings" , "config.yaml" ) ,
filepath . Join ( root , "settings" , "main.yml" ) ,
filepath . Join ( root , "settings" , "main.yaml" ) ,
filepath . Join ( root , "settings" , "special.yml" ) ,
filepath . Join ( root , "last.yml" ) ,
}
assertCandidatePaths ( t , candidates , want )
reversed := SearchDirectory ( "settings" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { NamedFilenames ( ) , GenericFilenames ( ) } } )
reversedCandidates := collectSearchCandidates ( t , reversed , "app" , helpers )
assertCandidatePaths ( t , reversedCandidates [ : 2 ] , [ ] string {
filepath . Join ( root , "settings" , "app.yml" ) ,
filepath . Join ( root , "settings" , "app.yaml" ) ,
} )
}
func TestSearchPresetCandidateLists ( t * testing . T ) {
t . Parallel ( )
helpers := discoveryDirectoryHelpers {
userConfigDir : func ( ) ( string , error ) { return "/controlled/config" , nil } ,
userHomeDir : func ( ) ( string , error ) { return "/controlled/home" , nil } ,
workingDir : func ( ) ( string , error ) { return "/controlled/work" , nil } ,
unixSupported : func ( ) bool { return true } ,
}
unix := collectSearchCandidates ( t , UnixSystemDirectories ( ) , "app" , helpers )
if len ( unix ) != 14 {
t . Fatalf ( "Unix preset candidate count = %d: %#v" , len ( unix ) , unix )
}
assertCandidatePaths ( t , unix , [ ] string {
"/usr/local/etc/app/app.yml" , "/usr/local/etc/app/app.yaml" , "/usr/local/etc/app/config.yml" , "/usr/local/etc/app/config.yaml" , "/usr/local/etc/app/main.yml" , "/usr/local/etc/app/main.yaml" ,
"/etc/app/app.yml" , "/etc/app/app.yaml" , "/etc/app/config.yml" , "/etc/app/config.yaml" , "/etc/app/main.yml" , "/etc/app/main.yaml" ,
"/etc/app.yml" , "/etc/app.yaml" ,
} )
user := collectSearchCandidates ( t , UserConfigDirectory ( ) , "app" , helpers )
assertCandidatePaths ( t , user , [ ] string {
"/controlled/config/app/app.yml" , "/controlled/config/app/app.yaml" , "/controlled/config/app/config.yml" , "/controlled/config/app/config.yaml" , "/controlled/config/app/main.yml" , "/controlled/config/app/main.yaml" ,
"/controlled/config/app.yml" , "/controlled/config/app.yaml" ,
} )
home := collectSearchCandidates ( t , HomeDirectory ( ) , "app" , helpers )
assertCandidatePaths ( t , home , [ ] string {
"/controlled/home/.app/app.yml" , "/controlled/home/.app/app.yaml" , "/controlled/home/.app/config.yml" , "/controlled/home/.app/config.yaml" , "/controlled/home/.app/main.yml" , "/controlled/home/.app/main.yaml" ,
"/controlled/home/app.yml" , "/controlled/home/app.yaml" ,
} )
working := collectSearchCandidates ( t , WorkingDirectory ( ) , "app" , helpers )
assertCandidatePaths ( t , working , [ ] string { "/controlled/work/app.yml" , "/controlled/work/app.yaml" } )
}
func TestSearchCandidateExpansionIsLazyAndSkipsEmptyDirectories ( t * testing . T ) {
t . Parallel ( )
configCalls := 0
helpers := discoveryDirectoryHelpers {
userConfigDir : func ( ) ( string , error ) {
configCalls ++
return "/controlled/config" , nil
} ,
workingDir : func ( ) ( string , error ) { return "/controlled/work" , nil } ,
unixSupported : func ( ) bool { return true } ,
}
search := Searches ( Paths ( "/selected.yml" ) , UserConfigDirectory ( ) )
iterator , diagnostics := newSearchCandidateIterator ( search , "app" , helpers )
if len ( diagnostics ) != 0 {
t . Fatalf ( "iterator diagnostics = %#v" , diagnostics )
}
if candidate , ok , err := iterator . next ( ) ; err != nil || ! ok || candidate != "/selected.yml" {
t . Fatalf ( "first candidate = %q, %v, %v" , candidate , ok , err )
}
if configCalls != 0 {
t . Fatalf ( "later user directory resolved before consumption: %d calls" , configCalls )
}
emptyOnly := SearchDirectory ( "should-not-resolve" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { } } )
emptyCandidates := collectSearchCandidates ( t , emptyOnly , "" , helpers )
if len ( emptyCandidates ) != 0 || configCalls != 0 {
t . Fatalf ( "empty directory candidates = %#v, helper calls = %d" , emptyCandidates , configCalls )
}
continued := collectSearchCandidates ( t , Searches ( emptyOnly , Paths ( "later.yml" ) ) , "" , helpers )
assertCandidatePaths ( t , continued , [ ] string { filepath . Join ( "/controlled/work" , "later.yml" ) } )
if configCalls != 0 {
t . Fatalf ( "empty directory base was resolved: %d calls" , configCalls )
}
}
func TestSearchCandidateBaseFailuresAndPlatformApplicability ( t * testing . T ) {
t . Parallel ( )
baseFailure := errors . New ( "controlled base failure" )
helpers := discoveryDirectoryHelpers {
userConfigDir : func ( ) ( string , error ) { return "" , baseFailure } ,
workingDir : func ( ) ( string , error ) { return "/controlled/work" , nil } ,
unixSupported : func ( ) bool { return false } ,
}
iterator , diagnostics := newSearchCandidateIterator ( UserConfigDirectory ( ) , "app" , helpers )
if len ( diagnostics ) != 0 {
t . Fatalf ( "user preset validation = %#v" , diagnostics )
}
if _ , ok , err := iterator . next ( ) ; ok || ! errors . Is ( err , baseFailure ) {
t . Fatalf ( "unavailable user base = ok %v, err %v" , ok , err )
}
iterator , diagnostics = newSearchCandidateIterator ( UnixSystemDirectories ( ) , "app" , helpers )
if len ( diagnostics ) != 0 {
t . Fatalf ( "Unix preset validation = %#v" , diagnostics )
}
if _ , ok , err := iterator . next ( ) ; ok || ! errors . Is ( err , errUnixSystemPresetUnavailable ) {
t . Fatalf ( "unsupported Unix preset = ok %v, err %v" , ok , err )
}
iterator , diagnostics = newSearchCandidateIterator ( Searches ( Paths ( "/earlier.yml" ) , UnixSystemDirectories ( ) ) , "app" , helpers )
if len ( diagnostics ) != 0 {
t . Fatalf ( "composed Unix preset validation = %#v" , diagnostics )
}
if candidate , ok , err := iterator . next ( ) ; err != nil || ! ok || candidate != "/earlier.yml" {
t . Fatalf ( "earlier candidate = %q, %v, %v" , candidate , ok , err )
}
}
func TestRelativeAndShellLikeSearchPathsRemainLiteral ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
helpers := discoveryDirectoryHelpers { workingDir : func ( ) ( string , error ) { return root , nil } }
search := Searches ( Paths ( "~/literal.yml" , "$CONFIG/*.yaml" ) , SearchDirectory ( "./settings" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { CustomFilenames ( "name with spaces.yml" ) } } ) )
candidates := collectSearchCandidates ( t , search , "" , helpers )
assertCandidatePaths ( t , candidates , [ ] string {
filepath . Join ( root , "~/literal.yml" ) ,
filepath . Join ( root , "$CONFIG/*.yaml" ) ,
filepath . Join ( root , "settings" , "name with spaces.yml" ) ,
} )
}
func collectSearchCandidates ( t * testing . T , search Search , application string , helpers discoveryDirectoryHelpers ) [ ] string {
t . Helper ( )
iterator , diagnostics := newSearchCandidateIterator ( search , application , helpers )
if len ( diagnostics ) != 0 {
t . Fatalf ( "search validation = %#v" , diagnostics )
}
var candidates [ ] string
for {
candidate , ok , err := iterator . next ( )
if err != nil {
t . Fatalf ( "expand candidate: %v" , err )
}
if ! ok {
return candidates
}
candidates = append ( candidates , candidate )
}
}
func assertCandidatePaths ( t * testing . T , got , want [ ] string ) {
t . Helper ( )
if len ( got ) != len ( want ) {
t . Fatalf ( "candidate count = %d, want %d: %#v" , len ( got ) , len ( want ) , got )
}
for i := range want {
if got [ i ] != want [ i ] {
t . Fatalf ( "candidate %d = %q, want %q" , i , got [ i ] , want [ i ] )
}
}
}
2026-10-24 15:46:36 +00:00
func TestResolveSearchSelectsFirstExistingCandidate ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
first := filepath . Join ( root , "first.yml" )
second := filepath . Join ( root , "second.yml" )
writeDiscoveryFile ( t , first , "value: first\n" )
writeDiscoveryFile ( t , second , "value: second\n" )
selected , diagnostics := resolveSearchSource ( Source { } , ptrSearch ( Paths ( filepath . Join ( root , "missing.yml" ) , first , second ) ) , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . kind != sourceFile || selected . path != first {
t . Fatalf ( "selection = %#v, diagnostics %#v" , selected , diagnostics )
}
read , diagnostics := readSource ( selected , "source-1" , defaultSourceLimits ( ) )
if len ( diagnostics ) != 0 || string ( read . data ) != "value: first\n" || read . info . Path != canonicalTestPath ( t , first ) {
t . Fatalf ( "selected source read = %#v, diagnostics %#v" , read , diagnostics )
}
}
func TestResolveSearchNamedExtensionsAndNonregularSelection ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
writeDiscoveryFile ( t , filepath . Join ( root , "app.yml" ) , "value: yml\n" )
writeDiscoveryFile ( t , filepath . Join ( root , "app.yaml" ) , "value: yaml\n" )
selected , diagnostics := resolveSearchSource ( Source { } , ptrSearch ( SearchDirectory ( root ) ) , "app" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . path != filepath . Join ( root , "app.yml" ) {
t . Fatalf ( "named extension selection = %#v, %#v" , selected , diagnostics )
}
directory := filepath . Join ( root , "subdirectory" )
if err := os . Mkdir ( directory , 0 o700 ) ; err != nil {
t . Fatal ( err )
}
good := filepath . Join ( root , "good.yml" )
writeDiscoveryFile ( t , good , "value: later\n" )
selected , diagnostics = resolveSearchSource ( Source { } , ptrSearch ( Paths ( directory , good ) ) , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . path != directory {
t . Fatalf ( "directory candidate selection = %#v, %#v" , selected , diagnostics )
}
_ , diagnostics = readSource ( selected , "source-1" , defaultSourceLimits ( ) )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeSourceNotRegular {
t . Fatalf ( "selected directory read diagnostics = %#v" , diagnostics )
}
}
func TestResolveSearchContainedSymlinkAndNoFallbackAfterReadFailure ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
target := filepath . Join ( root , "actual.yml" )
link := filepath . Join ( root , "linked.yml" )
writeDiscoveryFile ( t , target , "value: linked\n" )
if err := os . Symlink ( target , link ) ; err != nil {
t . Skipf ( "symlinks unavailable: %v" , err )
}
writeDiscoveryFile ( t , filepath . Join ( root , "later.yml" ) , "value: later\n" )
selected , diagnostics := resolveSearchSource ( Source { } , ptrSearch ( Paths ( link , filepath . Join ( root , "later.yml" ) ) ) , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . path != link {
t . Fatalf ( "symlink selection = %#v, %#v" , selected , diagnostics )
}
read , diagnostics := readSource ( selected , "source-1" , defaultSourceLimits ( ) )
if len ( diagnostics ) != 0 || string ( read . data ) != "value: linked\n" || read . info . Path != canonicalTestPath ( t , target ) {
t . Fatalf ( "symlink read = %#v, %#v" , read , diagnostics )
}
if err := os . Remove ( target ) ; err != nil {
t . Fatal ( err )
}
_ , diagnostics = readSource ( selected , "source-1" , defaultSourceLimits ( ) )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeSourceMissing {
t . Fatalf ( "selected source read failure = %#v" , diagnostics )
}
}
func TestResolveSearchRequiredAndOptionalExhaustion ( t * testing . T ) {
t . Parallel ( )
optional , diagnostics := resolveSearchSource ( Source { } , ptrSearch ( Search { } ) , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || optional . kind != sourceNone {
t . Fatalf ( "optional empty search = %#v, %#v" , optional , diagnostics )
}
_ , diagnostics = resolveSearchSource ( Source { } , ptrSearch ( Search { } ) , "" , true , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeSourceMissing || diagnostics [ 0 ] . Primary != nil || ! strings . Contains ( diagnostics [ 0 ] . Message , "search" ) {
t . Fatalf ( "required empty search = %#v" , diagnostics )
}
root := t . TempDir ( )
missing := filepath . Join ( root , "missing.yml" )
optional , diagnostics = resolveSearchSource ( Source { } , ptrSearch ( Paths ( missing ) ) , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || optional . kind != sourceNone {
t . Fatalf ( "optional missing search = %#v, %#v" , optional , diagnostics )
}
_ , diagnostics = resolveSearchSource ( Source { } , ptrSearch ( Paths ( missing ) ) , "" , true , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeSourceMissing {
t . Fatalf ( "required missing search = %#v" , diagnostics )
}
}
func TestResolveSearchLeavesNonSearchSourcesUntouched ( t * testing . T ) {
t . Parallel ( )
source := Bytes ( "memory" , [ ] byte ( "value: unchanged\n" ) )
resolved , diagnostics := resolveSearchSource ( source , nil , "" , false , discoveryDirectoryHelpers { } , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || resolved . kind != sourceBytes || string ( resolved . data ) != "value: unchanged\n" {
t . Fatalf ( "non-search source = %#v, diagnostics = %#v" , resolved , diagnostics )
}
}
func TestResolveSearchStopsOnReachedProbeError ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
first := filepath . Join ( root , "first.yml" )
second := filepath . Join ( root , "second.yml" )
writeDiscoveryFile ( t , first , "value: first\n" )
writeDiscoveryFile ( t , second , "value: second\n" )
statCalls := 0
probe := discoveryHostProbe { stat : func ( path string ) ( fs . FileInfo , error ) {
statCalls ++
return nil , fs . ErrPermission
} }
2026-10-24 19:16:50 +00:00
selected , info , diagnostics := resolveSearchInput ( SearchConfig ( "" , Paths ( first , second ) ) , "source-3" , discoveryDirectoryHelpers { } , probe )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeSourcePermission || statCalls != 1 {
2026-10-24 15:46:36 +00:00
t . Fatalf ( "probe failure = %#v, stat calls = %d" , diagnostics , statCalls )
}
2026-10-24 19:16:50 +00:00
if selected . kind != sourceFile || info == nil || info . Path != canonicalTestPath ( t , first ) || info . Label != first || info . Kind != SourceKindFile || info . ID != "source-3" {
t . Fatalf ( "failed candidate evidence = %#v, %#v" , selected , info )
}
if diagnostics [ 0 ] . Primary == nil || diagnostics [ 0 ] . Primary . SourceID != info . ID {
t . Fatalf ( "failed candidate diagnostic does not identify its file: %#v" , diagnostics )
}
2026-10-24 15:46:36 +00:00
}
func TestResolveSearchLeavesLaterDirectoryUnresolvedAfterSelection ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
first := filepath . Join ( root , "selected.yml" )
writeDiscoveryFile ( t , first , "value: selected\n" )
baseCalls := 0
helpers := discoveryDirectoryHelpers { userConfigDir : func ( ) ( string , error ) {
baseCalls ++
return "" , errors . New ( "later base should not be reached" )
} }
search := ptrSearch ( Searches ( Paths ( first ) , UserConfigDirectory ( ) ) )
selected , diagnostics := resolveSearchSource ( Source { } , search , "app" , false , helpers , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . path != first || baseCalls != 0 {
t . Fatalf ( "selected = %#v, diagnostics = %#v, later base calls = %d" , selected , diagnostics , baseCalls )
}
}
func TestResolveSearchTreatsShellLikeNamesLiterally ( t * testing . T ) {
t . Parallel ( )
root := t . TempDir ( )
paths := [ ] string { "~/literal.yml" , "$CONFIG/literal.yml" , "*.yaml" }
for _ , name := range paths {
writeDiscoveryFile ( t , filepath . Join ( root , name ) , "value: literal\n" )
}
helper := discoveryDirectoryHelpers { workingDir : func ( ) ( string , error ) { return root , nil } }
for _ , name := range paths {
selected , diagnostics := resolveSearchSource ( Source { } , ptrSearch ( Paths ( name ) ) , "" , false , helper , discoveryHostProbe { } )
if len ( diagnostics ) != 0 || selected . path != filepath . Join ( root , name ) {
t . Fatalf ( "literal %q selection = %#v, %#v" , name , selected , diagnostics )
}
}
}
func ptrSearch ( search Search ) * Search { return & search }
func writeDiscoveryFile ( t * testing . T , path , contents string ) {
t . Helper ( )
if err := os . MkdirAll ( filepath . Dir ( path ) , 0 o700 ) ; err != nil {
t . Fatal ( err )
}
if err := os . WriteFile ( path , [ ] byte ( contents ) , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
}