Integrate source version checks for main inputs

This commit is contained in:
2026-10-15 15:24:53 +00:00
parent 49218a836b
commit 23ae700194
8 changed files with 337 additions and 29 deletions

View File

@@ -1,11 +1,24 @@
package engine
import (
"errors"
"testing"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
type sourceVersionMountedService struct {
Version string `yaml:"version"`
}
type sourceVersionMountedConfig struct {
Service sourceVersionMountedService `yaml:"service"`
}
type sourceVersionNullConfig struct {
Version *string `yaml:"version"`
}
func sourceVersionTestDocument(t *testing.T, input string) *document.Node {
t.Helper()
parsed, diagnostics := parseSource(Bytes("source-version.yml", []byte(input)), "source-version", defaultParseLimits())
@@ -374,6 +387,211 @@ func TestSourceVersionNoSourceMainRetainsAbsentCallback(t *testing.T) {
}
}
func TestSourceVersionLoadChecksMountedMainLocallyAndProtectsEffectivePath(t *testing.T) {
t.Parallel()
checks := 0
definition := Definition[sourceVersionMountedConfig]{Version: &VersionRule{
Path: Keys("version"),
Check: func(version Version, validation *Validation) error {
checks++
if !version.Present || version.Null || version.Text != "9007199254740993" {
return errors.New("unexpected source-local version")
}
validation.Warning(Path{}, "advisory")
return nil
},
}}
main := Input{Source: Bytes("main.yml", []byte("version: 9007199254740993\n")), Destination: Keys("service")}
loaded, err := LoadLayered(main, testSecretBinding(), LayeredOptions[sourceVersionMountedConfig]{
Definition: definition, SourceVersionMode: sourceVersionModeRequired,
})
if err != nil || loaded == nil {
t.Fatalf("mounted main load = %#v, %v", loaded, err)
}
if loaded.Config.Service.Version != "9007199254740993" || checks != 1 {
t.Fatalf("mounted main config/callbacks = %#v, %d", loaded.Config, checks)
}
warnings := testLoaded(loaded).Diagnostics()
if len(warnings) != 1 || warnings[0].Code != CodeVersionWarning || warnings[0].Severity != SeverityWarning {
t.Fatalf("mounted main warnings = %#v", warnings)
}
checks = 0
_, err = LoadLayered(main, testSecretBinding(), LayeredOptions[sourceVersionMountedConfig]{
Definition: definition, SourceVersionMode: sourceVersionModeRequired,
Overrides: []Override{{Path: Keys("service").Key("version"), Value: "4", Flag: "--version"}},
})
var loadErr *LoadError
if !errors.As(err, &loadErr) {
t.Fatalf("mounted protected override error = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeOverrideVersion, "/service/version")
if checks != 1 {
t.Fatalf("mounted protected override callbacks = %d, want 1", checks)
}
}
func TestSourceVersionLoadChecksNullAndNoSourceMain(t *testing.T) {
t.Parallel()
t.Run("required actual main absence fails before the checker", func(t *testing.T) {
t.Parallel()
checks := 0
_, err := Load(Bytes("main.yml", []byte("other: value\n")), testSecretBinding(), Options[sourceVersionNullConfig]{
SourceVersionMode: sourceVersionModeRequired,
Definition: Definition[sourceVersionNullConfig]{Version: &VersionRule{
Path: Keys("version"), Check: func(Version, *Validation) error { checks++; return nil },
}},
})
var loadErr *LoadError
if !errors.As(err, &loadErr) {
t.Fatalf("missing required main version = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeVersionError, "/version")
if checks != 0 {
t.Fatalf("missing required main invoked checker %d times", checks)
}
})
t.Run("required null is passed to the checker", func(t *testing.T) {
t.Parallel()
checks := 0
loaded, err := Load(Bytes("main.yml", []byte("version: null\n")), testSecretBinding(), Options[sourceVersionNullConfig]{
SourceVersionMode: sourceVersionModeRequired,
Definition: Definition[sourceVersionNullConfig]{Version: &VersionRule{
Path: Keys("version"),
Check: func(version Version, _ *Validation) error {
checks++
if !version.Present || !version.Null || version.Text != "" {
return errors.New("unexpected null version")
}
return nil
},
}},
})
if err != nil || loaded == nil || checks != 1 || loaded.Config.Version != nil {
t.Fatalf("required null load = %#v, callbacks %d, %v", loaded, checks, err)
}
})
for _, mode := range []sourceVersionMode{sourceVersionModeIfPresent, sourceVersionModeRequired} {
mode := mode
t.Run(map[sourceVersionMode]string{sourceVersionModeIfPresent: "if-present", sourceVersionModeRequired: "required"}[mode], func(t *testing.T) {
t.Parallel()
checks := 0
loaded, err := Load(NoSource(), testSecretBinding(), Options[sourceVersionNullConfig]{
SourceVersionMode: mode,
Definition: Definition[sourceVersionNullConfig]{Version: &VersionRule{
Path: Keys("version"), Check: func(version Version, _ *Validation) error {
checks++
if version.Present || version.Null {
return errors.New("NoSource should report absence")
}
return nil
},
}},
})
if err != nil || loaded == nil || checks != 1 || len(testLoaded(loaded).Sources()) != 0 {
t.Fatalf("NoSource load = %#v, callbacks %d, %v", loaded, checks, err)
}
})
}
}
func TestSourceVersionLoadReportsMountedMainErrorsAtLocalPath(t *testing.T) {
t.Parallel()
checks := 0
_, err := LoadLayered(Input{
Source: Bytes("main.yml", []byte("version: 5\n")), Destination: Keys("service"),
}, testSecretBinding(), LayeredOptions[sourceVersionMountedConfig]{
SourceVersionMode: sourceVersionModeRequired,
Definition: Definition[sourceVersionMountedConfig]{Version: &VersionRule{
Path: Keys("version"),
Check: func(Version, *Validation) error { checks++; return errors.New("rejected") },
}},
})
var loadErr *LoadError
if !errors.As(err, &loadErr) {
t.Fatalf("mounted main rejection = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeVersionHookFailed, "/version")
if checks != 1 {
t.Fatalf("mounted main checker calls = %d, want 1", checks)
}
}
func TestSourceVersionLoadChecksMainBeforeLayeredAcquisition(t *testing.T) {
t.Parallel()
checks := 0
_, err := LoadLayered(Input{Source: Bytes("main.yml", []byte("version: 5\n"))}, testSecretBinding(), LayeredOptions[sourceVersionNullConfig]{
SourceVersionMode: sourceVersionModeIfPresent,
Definition: Definition[sourceVersionNullConfig]{Version: &VersionRule{
Path: Keys("version"), Check: func(Version, *Validation) error { checks++; return errors.New("rejected") },
}},
Underlays: []Underlay{{Inputs: []Input{{Source: Bytes("unread.yml", []byte("[malformed"))}}}},
})
var loadErr *LoadError
if !errors.As(err, &loadErr) {
t.Fatalf("main rejection = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeVersionHookFailed, "/version")
if checks != 1 {
t.Fatalf("main checker calls = %d, want 1", checks)
}
}
func TestSourceVersionEnabledBuildsSchemaBeforeMainCallback(t *testing.T) {
t.Parallel()
checks := 0
_, err := Load(Bytes("main.yml", []byte("version: 5\n")), testSecretBinding(), Options[int]{
SourceVersionMode: sourceVersionModeIfPresent,
Definition: Definition[int]{Version: &VersionRule{
Path: Keys("version"), Check: func(Version, *Validation) error { checks++; return nil },
}},
})
if err == nil || checks != 0 {
t.Fatalf("invalid schema load = %v, checker calls %d; want schema failure before callback", err, checks)
}
}
func TestSourceVersionLoadRejectsOptionsBeforeReadingMain(t *testing.T) {
t.Parallel()
checkerCalls := 0
definition := Definition[sourceVersionNullConfig]{Version: &VersionRule{
Path: Keys("version"), Check: func(Version, *Validation) error { checkerCalls++; return nil },
}}
_, err := Load(Bytes("malformed.yml", []byte("[not valid")), testSecretBinding(), Options[sourceVersionNullConfig]{
Definition: definition, SourceVersionMode: sourceVersionMode(255),
})
var loadErr *LoadError
if !errors.As(err, &loadErr) {
t.Fatalf("invalid-mode error = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeOptionsInvalid, "")
if checkerCalls != 0 {
t.Fatalf("invalid mode invoked checker %d times", checkerCalls)
}
_, err = Load(Bytes("malformed.yml", []byte("[not valid")), testSecretBinding(), Options[sourceVersionNullConfig]{SourceVersionMode: sourceVersionModeRequired})
if !errors.As(err, &loadErr) {
t.Fatalf("missing-rule error = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeOptionsInvalid, "")
_, err = LoadLayered(Input{Source: Bytes("malformed.yml", []byte("[not valid"))}, testSecretBinding(), LayeredOptions[sourceVersionNullConfig]{
SourceVersionMode: sourceVersionModeRequired,
})
if !errors.As(err, &loadErr) {
t.Fatalf("layered missing-rule error = %T %v, want *LoadError", err, err)
}
requireSourceVersionDiagnostic(t, loadErr.Diagnostics(), CodeOptionsInvalid, "")
}
func mustParseSourceVersionPath(t *testing.T, input string) Path {
t.Helper()
path, err := ParsePath(input)