Expose resolution input provenance

This commit is contained in:
2026-09-12 13:13:07 +00:00
parent 88f21d09c7
commit e20a0a0998
8 changed files with 184 additions and 8 deletions

View File

@@ -280,6 +280,83 @@ func TestResolutionRejectsInvalidProjectedValuesAndSuppressesReturnedErrors(t *t
}
}
func TestResolutionEntryQueriesExposeDetachedLoadInput(t *testing.T) {
t.Parallel()
type config struct {
Declared string `yaml:"declared"`
Default string `yaml:"default"`
Optional *string `yaml:"optional"`
}
filename := writeImportFile(t, t.TempDir(), "config.yml", "declared: file\noptional: null\n")
var savedField FieldInfo
var savedSource SourceInfo
result, err := Load[config](File(filename), Options[config]{Definition: Definition[config]{
Defaults: func() config { return config{Default: "retained"} },
Resolve: func(_ *config, resolution *Resolution) error {
for _, path := range []Path{Keys("declared"), Keys("default"), Keys("optional")} {
info, found := resolution.InputInfo(path)
if !found || info.Explicit != resolution.IsExplicit(path) {
return errors.New("entry metadata does not agree with explicitness")
}
}
defaultInfo, _ := resolution.InputInfo(Keys("default"))
optionalInfo, _ := resolution.InputInfo(Keys("optional"))
if defaultInfo.Explicit || defaultInfo.Origin.Layer != LayerDefault || !optionalInfo.Explicit || !optionalInfo.Null {
return errors.New("entry metadata lost default or null state")
}
if _, found := resolution.InputInfo(Keys("missing")); found {
return errors.New("missing entry path was found")
}
declared, _ := resolution.InputInfo(Keys("declared"))
if len(declared.Origin.Locations) != 1 {
return errors.New("declared source location is missing")
}
source, found := resolution.Source(declared.Origin.Locations[0].SourceID)
if !found || source.Path != canonicalTestPath(t, filename) || source.ID != "source-1" {
return errors.New("declared source record is missing")
}
if _, found := resolution.Source(""); found {
return errors.New("empty source identity was found")
}
if _, found := resolution.Source("unknown"); found {
return errors.New("unknown source identity was found")
}
savedField, savedSource = declared, source
return nil
},
}})
if err != nil {
t.Fatal(err)
}
if result.Config.Declared != "file" || result.Config.Default != "retained" || result.Config.Optional != nil {
t.Fatalf("configuration = %#v", result.Config)
}
savedField.Origin.Locations[0].SourceID = "mutated"
savedSource.Label = "mutated"
field, _ := result.Metadata().Lookup(Keys("declared"))
if field.Origin.Locations[0].SourceID != "source-1" || result.Sources()[0].Label == "mutated" {
t.Fatal("saved resolution records alias a completed result")
}
noSource, err := Load[config](NoSource(), Options[config]{Definition: Definition[config]{
Defaults: func() config { return config{Default: "retained"} },
Resolve: func(_ *config, resolution *Resolution) error {
info, found := resolution.InputInfo(Keys("default"))
if !found || info.Explicit || resolution.IsExplicit(Keys("default")) {
return errors.New("defaults-only entry metadata is wrong")
}
if _, found := resolution.Source("source-1"); found {
return errors.New("defaults-only load invented a source")
}
return nil
},
}})
if err != nil || noSource.Config.Default != "retained" {
t.Fatalf("defaults-only load = %#v, %v", noSource, err)
}
}
func TestResolutionContextPanicsAfterCallback(t *testing.T) {
t.Parallel()
@@ -301,6 +378,45 @@ func TestResolutionContextPanicsAfterCallback(t *testing.T) {
retained.IsExplicit(Keys("value"))
}
func TestResolutionEntryQueriesRequireAnActiveCallback(t *testing.T) {
t.Parallel()
type config struct {
Value int `yaml:"value"`
}
requireClosed := func(t *testing.T, call func()) {
t.Helper()
defer func() {
if recovered := recover(); recovered != closedCallbackContextMessage {
t.Fatalf("panic = %#v", recovered)
}
}()
call()
}
requireClosed(t, func() { (*Resolution)(nil).InputInfo(Keys("value")) })
requireClosed(t, func() { (&Resolution{}).Source("source-1") })
var successful *Resolution
if _, err := Load(NoSource(), Options[config]{Definition: Definition[config]{Resolve: func(_ *config, resolution *Resolution) error {
successful = resolution
return nil
}}}); err != nil {
t.Fatal(err)
}
requireClosed(t, func() { successful.InputInfo(Keys("value")) })
requireClosed(t, func() { successful.Source("source-1") })
var failed *Resolution
if _, err := Load(NoSource(), Options[config]{Definition: Definition[config]{Resolve: func(_ *config, resolution *Resolution) error {
failed = resolution
return errors.New("resolver failed")
}}}); err == nil {
t.Fatal("failed resolver succeeded")
}
requireClosed(t, func() { failed.InputInfo(Keys("value")) })
requireClosed(t, func() { failed.Source("source-1") })
}
func TestValidationContextPanicsAfterVersionCallback(t *testing.T) {
t.Parallel()