package engine import ( "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) } } }