2026-10-24 15:39:59 +00:00
package engine
import (
2026-10-24 15:43:10 +00:00
"errors"
"path/filepath"
2026-10-24 15:39:59 +00:00
"runtime"
"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" {
for _ , value := range [ ] string { "CON" , "nul.txt" , "name." , "bad:name" } {
if validFilenameComponent ( value ) {
t . Errorf ( "Windows component %q unexpectedly accepted" , value )
}
}
} else {
for _ , value := range [ ] string { "name with spaces" , "naïve" } {
if ! validFilenameComponent ( value ) {
t . Errorf ( "host-valid component %q rejected" , value )
}
}
}
if diagnostics := validateSearch ( SearchDirectory ( "configs" , DirectorySearchOptions { Filenames : [ ] FilenameGroup { CustomFilenames ( "bad/name" ) } } ) , "" ) ; len ( diagnostics ) == 0 {
t . Fatal ( "invalid custom filename was not retained for validation" )
}
for _ , value := range [ ] string { "CON" , "nul.txt" , "COM¹.log" , "LPT³.txt" , "name." , "bad:name" , "bad\x01name" } {
if validWindowsFilenameComponent ( value ) {
t . Errorf ( "Windows component %q unexpectedly accepted" , value )
}
}
for _ , value := range [ ] string { "name with spaces" , "naïve" } {
if ! validWindowsFilenameComponent ( value ) {
t . Errorf ( "valid Windows component %q rejected" , value )
}
}
}
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 ] )
}
}
}