230 lines
8.2 KiB
Go
230 lines
8.2 KiB
Go
package configkit_test
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
|
)
|
|
|
|
func TestResultSourceLookup(t *testing.T) {
|
|
directory, err := filepath.EvalSymlinks(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mainPath := writeSourceLookupFile(t, directory, "main.yml", `version: MAIN-SOURCE-CANARY
|
|
configkit:
|
|
imports:
|
|
- path: imported.yml
|
|
- dir: absent
|
|
main_value: main
|
|
`)
|
|
importedPath := writeSourceLookupFile(t, directory, "imported.yml", `version: IMPORTED-SOURCE-CANARY
|
|
imported_value: imported
|
|
`)
|
|
underlayPath := writeSourceLookupFile(t, directory, "underlay.yml", "underlay_value: underlay\n")
|
|
definition := configkit.Definition[map[string]any]{
|
|
Version: &configkit.VersionRule{
|
|
Path: configkit.Keys("version"),
|
|
Check: func(configkit.Version, *configkit.Validation) error { return nil },
|
|
},
|
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/version" },
|
|
}
|
|
result, err := configkit.LoadLayered(configkit.Input{Source: configkit.File(mainPath), Imports: true}, configkit.LayeredOptions[map[string]any]{
|
|
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.File(underlayPath)}}}},
|
|
Definition: definition, SourceVersionMode: configkit.SourceVersionModeIfPresent,
|
|
})
|
|
if err != nil || result == nil {
|
|
var loadError *configkit.LoadError
|
|
if errors.As(err, &loadError) {
|
|
t.Fatalf("LoadLayered = %#v, %v, diagnostics %#v", result, err, loadError.Diagnostics())
|
|
}
|
|
t.Fatalf("LoadLayered = %#v, %v", result, err)
|
|
}
|
|
|
|
sources := result.Sources()
|
|
if len(sources) != 3 {
|
|
t.Fatalf("sources = %#v", sources)
|
|
}
|
|
for _, want := range sources {
|
|
got, ok := result.Source(want.ID)
|
|
if !ok || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("Source(%q) = %#v, %v; Sources entry = %#v", want.ID, got, ok, want)
|
|
}
|
|
}
|
|
main, ok := result.Source(sources[0].ID)
|
|
if !ok || main.Layer != configkit.LayerMain || len(main.EmptyImportSelections) != 1 ||
|
|
main.VersionEvidence == nil || !main.VersionEvidence.Redacted || main.VersionEvidence.Version.Text != "" ||
|
|
main.VersionEvidence.Location == nil || main.VersionEvidence.Location.SourceID != main.ID {
|
|
t.Fatalf("main source evidence = %#v, %v", main, ok)
|
|
}
|
|
var imported, underlay bool
|
|
for _, source := range sources {
|
|
if source.Path == importedPath {
|
|
imported = source.Layer == configkit.LayerMain && source.ImportedFrom != nil &&
|
|
source.VersionEvidence != nil && source.VersionEvidence.Location != nil &&
|
|
source.VersionEvidence.Location.SourceID == source.ID
|
|
}
|
|
if source.Path == underlayPath {
|
|
underlay = source.Layer == configkit.LayerUnderlay
|
|
}
|
|
}
|
|
if !imported || !underlay {
|
|
t.Fatalf("imported or underlay source evidence missing: %#v", sources)
|
|
}
|
|
if got, ok := result.Source(""); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("empty source ID = %#v, %v", got, ok)
|
|
}
|
|
if got, ok := result.Source("unknown-source"); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("unknown source ID = %#v, %v", got, ok)
|
|
}
|
|
var nilResult *configkit.Result[map[string]any]
|
|
if got, ok := nilResult.Source("source-1"); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("nil result lookup = %#v, %v", got, ok)
|
|
}
|
|
if got, ok := (&configkit.Result[map[string]any]{}).Source("source-1"); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("zero result lookup = %#v, %v", got, ok)
|
|
}
|
|
|
|
main.EmptyImportSelections[0].Path = configkit.Keys("mutated")
|
|
main.EmptyImportSelections[0].Location.Line = 99
|
|
main.VersionEvidence.Path = configkit.Keys("mutated")
|
|
main.VersionEvidence.Location.Line = 99
|
|
main.VersionEvidence.Version.Text = "mutated"
|
|
for _, source := range sources {
|
|
if source.ImportedFrom != nil {
|
|
found, _ := result.Source(source.ID)
|
|
found.ImportedFrom.Line = 99
|
|
break
|
|
}
|
|
}
|
|
if got := result.Sources(); !reflect.DeepEqual(got, sources) {
|
|
t.Fatalf("lookup mutations changed enumeration: %#v", got)
|
|
}
|
|
for _, want := range sources {
|
|
if got, ok := result.Source(want.ID); !ok || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("repeated Source(%q) = %#v, %v; want %#v", want.ID, got, ok, want)
|
|
}
|
|
}
|
|
for _, path := range []string{mainPath, importedPath, underlayPath} {
|
|
if err := os.Remove(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if got, ok := result.Source(sources[0].ID); !ok || !reflect.DeepEqual(got, sources[0]) {
|
|
t.Fatalf("source lookup after inputs were removed = %#v, %v", got, ok)
|
|
}
|
|
}
|
|
|
|
func TestLoadErrorSourceLookup(t *testing.T) {
|
|
directory := t.TempDir()
|
|
mainPath := writeSourceLookupFile(t, directory, "main.yml", `configkit:
|
|
imports:
|
|
- path: item.yml
|
|
destination: [items]
|
|
placement: sequence-item
|
|
`)
|
|
writeSourceLookupFile(t, directory, "item.yml", `configkit:
|
|
imports:
|
|
- path: helper.yml
|
|
destination: [metadata]
|
|
`)
|
|
const canary = "FAILED-SOURCE-LOOKUP-CANARY"
|
|
helperPath := writeSourceLookupFile(t, directory, "helper.yml", `version: `+canary+`
|
|
configkit:
|
|
imports:
|
|
- dir: absent
|
|
- path: missing.yml
|
|
required: true
|
|
key: value
|
|
`)
|
|
definition := configkit.Definition[map[string]any]{
|
|
Version: &configkit.VersionRule{
|
|
Path: configkit.Keys("version"),
|
|
Check: func(configkit.Version, *configkit.Validation) error { return nil },
|
|
},
|
|
Sensitive: func(path configkit.Path) bool { return strings.HasSuffix(path.String(), "/version") },
|
|
}
|
|
_, err := configkit.Load(configkit.File(mainPath), configkit.Options[map[string]any]{
|
|
Imports: true, Definition: definition, SourceVersionMode: configkit.SourceVersionModeIfPresent,
|
|
})
|
|
var loadError *configkit.LoadError
|
|
if !errors.As(err, &loadError) {
|
|
t.Fatalf("load error = %T %v", err, err)
|
|
}
|
|
if strings.Contains(err.Error(), canary) {
|
|
t.Fatalf("load error disclosed source-version canary: %v", err)
|
|
}
|
|
sources := loadError.Sources()
|
|
if len(sources) < 3 {
|
|
t.Fatalf("partial source inventory = %#v", sources)
|
|
}
|
|
for _, want := range sources {
|
|
got, ok := loadError.Source(want.ID)
|
|
if !ok || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("Source(%q) = %#v, %v; Sources entry = %#v", want.ID, got, ok, want)
|
|
}
|
|
}
|
|
canonicalHelper, err := filepath.EvalSymlinks(helperPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var helper configkit.SourceInfo
|
|
for _, source := range sources {
|
|
if source.Path == canonicalHelper {
|
|
helper = source
|
|
break
|
|
}
|
|
}
|
|
if helper.ID == "" || !helper.DestinationIsFallback || helper.Destination.String() != "/items" ||
|
|
len(helper.EmptyImportSelections) != 1 || helper.VersionEvidence == nil ||
|
|
!helper.VersionEvidence.Redacted || helper.VersionEvidence.Version.Text != "" ||
|
|
helper.VersionEvidence.Location == nil {
|
|
t.Fatalf("partial helper source evidence = %#v", helper)
|
|
}
|
|
if got, ok := loadError.Source(""); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("empty source ID = %#v, %v", got, ok)
|
|
}
|
|
if got, ok := loadError.Source("unknown-source"); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("unknown source ID = %#v, %v", got, ok)
|
|
}
|
|
var nilError *configkit.LoadError
|
|
if got, ok := nilError.Source(helper.ID); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("nil load error lookup = %#v, %v", got, ok)
|
|
}
|
|
if got, ok := (&configkit.LoadError{}).Source(helper.ID); ok || !reflect.DeepEqual(got, configkit.SourceInfo{}) {
|
|
t.Fatalf("zero load error lookup = %#v, %v", got, ok)
|
|
}
|
|
|
|
mutated, ok := loadError.Source(helper.ID)
|
|
if !ok {
|
|
t.Fatal("partial helper lookup missing")
|
|
}
|
|
mutated.Destination = configkit.Keys("mutated")
|
|
mutated.ImportedFrom.Line = 99
|
|
mutated.EmptyImportSelections[0].Path = configkit.Keys("mutated")
|
|
mutated.EmptyImportSelections[0].Location.Line = 99
|
|
mutated.VersionEvidence.Path = configkit.Keys("mutated")
|
|
mutated.VersionEvidence.Location.Line = 99
|
|
mutated.VersionEvidence.Version.Text = "mutated"
|
|
if got := loadError.Sources(); !reflect.DeepEqual(got, sources) {
|
|
t.Fatalf("lookup mutations changed error enumeration: %#v", got)
|
|
}
|
|
if got, ok := loadError.Source(helper.ID); !ok || !reflect.DeepEqual(got, helper) {
|
|
t.Fatalf("repeated partial lookup = %#v, %v; want %#v", got, ok, helper)
|
|
}
|
|
}
|
|
|
|
func writeSourceLookupFile(t *testing.T, directory, name, contents string) string {
|
|
t.Helper()
|
|
path := filepath.Join(directory, name)
|
|
if err := os.WriteFile(path, []byte(contents), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|