Files
ConfigKit/facade_source_version_mode_test.go

176 lines
6.3 KiB
Go

package configkit_test
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
)
type sourceVersionModeReader struct {
*strings.Reader
reads int
}
func (reader *sourceVersionModeReader) Read(data []byte) (int, error) {
reader.reads++
return reader.Reader.Read(data)
}
func TestFacadeSourceVersionModesAcrossOrdinaryAndLayeredLoads(t *testing.T) {
t.Parallel()
var checked []configkit.Version
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
Path: configkit.Keys("meta", "version"),
Check: func(version configkit.Version, _ *configkit.Validation) error {
checked = append(checked, version)
return nil
},
}}
ordinary, err := configkit.Load(configkit.Bytes("ordinary", []byte("meta: {version: ordinary-v}\n")), configkit.Options[map[string]any]{
Definition: definition, SourceVersionMode: configkit.SourceVersionModeIfPresent,
})
if err != nil || ordinary == nil {
t.Fatalf("ordinary enabled load = %#v, %v", ordinary, err)
}
if got := ordinary.Config["meta"].(map[string]any)["version"]; got != "ordinary-v" {
t.Fatalf("main declaration was not retained: %#v", ordinary.Config)
}
directory := t.TempDir()
mainPath := filepath.Join(directory, "main.yml")
if err := os.WriteFile(mainPath, []byte("meta: {version: main-v}\n"), 0o600); err != nil {
t.Fatal(err)
}
layered, err := configkit.LoadLayered(configkit.Input{Source: configkit.File(mainPath)}, configkit.LayeredOptions[map[string]any]{
Definition: definition, SourceVersionMode: configkit.SourceVersionModeRequired,
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{
Source: configkit.Mapping("underlay", map[string]any{
"meta": map[string]any{"version": "underlay-v"}, "fallback": "present",
}),
Destination: configkit.Keys("defaults"),
}}}},
})
if err != nil || layered == nil {
t.Fatalf("layered enabled load = %#v, %v", layered, err)
}
defaults, ok := layered.Config["defaults"].(map[string]any)
if !ok || defaults["fallback"] != "present" {
t.Fatalf("layered underlay payload = %#v", layered.Config["defaults"])
}
if meta, ok := defaults["meta"].(map[string]any); !ok || len(meta) != 0 {
t.Fatalf("accepted underlay declaration was not consumed: %#v", defaults["meta"])
}
if len(checked) != 3 {
t.Fatalf("checker calls across separate mode uses = %#v", checked)
}
for _, want := range []string{"ordinary-v", "main-v", "underlay-v"} {
found := false
for _, version := range checked {
found = found || version.Present && version.Text == want
}
if !found {
t.Errorf("checked versions %#v do not include %q", checked, want)
}
}
}
func TestFacadeSourceVersionModeZeroPreservesDefaultBehavior(t *testing.T) {
t.Parallel()
checks := 0
result, err := configkit.Load(configkit.Bytes("default", []byte("version: v1\n")), configkit.Options[map[string]any]{
Definition: configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
Path: configkit.Keys("version"),
Check: func(version configkit.Version, _ *configkit.Validation) error {
checks++
if version != (configkit.Version{Present: true, Text: "v1"}) {
t.Errorf("default version = %#v", version)
}
return nil
},
}},
})
if err != nil || result == nil || result.Config["version"] != "v1" || checks != 1 {
t.Fatalf("zero-mode load = %#v, %v; checks = %d", result, err, checks)
}
}
func TestFacadeSourceVersionModeValidationPrecedesSourceReads(t *testing.T) {
t.Parallel()
validDefinition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil },
}}
for _, test := range []struct {
name string
mode configkit.SourceVersionMode
def configkit.Definition[map[string]any]
layered bool
}{
{name: "unknown ordinary mode", mode: configkit.SourceVersionMode(255), def: validDefinition},
{name: "ordinary mode requires checker", mode: configkit.SourceVersionModeIfPresent},
{name: "unknown layered mode", mode: configkit.SourceVersionMode(255), def: validDefinition, layered: true},
{name: "layered mode requires checker", mode: configkit.SourceVersionModeRequired, layered: true},
} {
t.Run(test.name, func(t *testing.T) {
reader := &sourceVersionModeReader{Reader: strings.NewReader("version: v1\n")}
source := configkit.Reader("version.yml", reader)
var err error
if test.layered {
_, err = configkit.LoadLayered(configkit.Input{Source: source}, configkit.LayeredOptions[map[string]any]{
Definition: test.def, SourceVersionMode: test.mode,
})
} else {
_, err = configkit.Load(source, configkit.Options[map[string]any]{
Definition: test.def, SourceVersionMode: test.mode,
})
}
var loadError *configkit.LoadError
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != configkit.CodeOptionsInvalid {
t.Fatalf("invalid mode error = %#v", err)
}
if reader.reads != 0 {
t.Fatalf("invalid options read source %d times", reader.reads)
}
})
}
}
func TestFacadeSourceVersionDefinitionCanBeReusedWithDifferentModes(t *testing.T) {
t.Parallel()
checks := 0
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
Path: configkit.Keys("version"),
Check: func(version configkit.Version, _ *configkit.Validation) error {
checks++
if version.Present {
t.Errorf("missing declaration reached checker as %#v", version)
}
return nil
},
}}
source := configkit.Bytes("reused", []byte("value: retained\n"))
ifPresent, err := configkit.Load(source, configkit.Options[map[string]any]{
Definition: definition, SourceVersionMode: configkit.SourceVersionModeIfPresent,
})
if err != nil || ifPresent == nil || checks != 1 {
t.Fatalf("if-present load = %#v, %v; checks = %d", ifPresent, err, checks)
}
required, err := configkit.Load(source, configkit.Options[map[string]any]{
Definition: definition, SourceVersionMode: configkit.SourceVersionModeRequired,
})
var loadError *configkit.LoadError
if required != nil || !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != configkit.CodeVersionError {
t.Fatalf("required load = %#v, %v", required, err)
}
if checks != 1 {
t.Fatalf("required absence reached checker: calls = %d", checks)
}
}