Check source versions in direct underlays
This commit is contained in:
@@ -2,6 +2,7 @@ package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||
@@ -754,6 +755,225 @@ configkit:
|
||||
})
|
||||
}
|
||||
|
||||
func TestSourceVersionUnderlaysCheckDistinctMountedDeclarations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var checked []string
|
||||
definition := Definition[map[string]any]{Version: &VersionRule{
|
||||
Path: Keys("meta", "version"),
|
||||
Check: func(version Version, validation *Validation) error {
|
||||
checked = append(checked, version.Text)
|
||||
if version.Text == "peer-a" || version.Text == "peer-c" {
|
||||
validation.Warning(Path{}, "accepted with advisory")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
main := Input{
|
||||
Source: Bytes("main", []byte("meta: {version: main-v}\nservice: primary\n")),
|
||||
Destination: Keys("service"),
|
||||
}
|
||||
first := Input{
|
||||
Source: Bytes("peer-a", []byte("meta:\n version: peer-a\nvalues: {first: one}\n")),
|
||||
Destination: Keys("defaults", "first"),
|
||||
}
|
||||
second := Input{
|
||||
Source: Bytes("peer-b", []byte("meta:\n version: peer-b\nvalues: {second: two}\n")),
|
||||
Destination: Keys("defaults", "second"),
|
||||
}
|
||||
third := Input{
|
||||
Source: Bytes("peer-c", []byte("meta:\n version: peer-c\nvalues: {third: three}\n")),
|
||||
Destination: Keys("defaults", "third"),
|
||||
}
|
||||
loaded, err := LoadLayered(main, testSecretBinding(), LayeredOptions[map[string]any]{
|
||||
Definition: definition, SourceVersionMode: sourceVersionModeRequired,
|
||||
Underlays: []Underlay{{Inputs: []Input{first, second}}, {Inputs: []Input{third}}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("LoadLayered: %v", err)
|
||||
}
|
||||
if len(checked) != 4 {
|
||||
t.Fatalf("checked declarations = %#v, want main and all underlays", checked)
|
||||
}
|
||||
for _, want := range []string{"main-v", "peer-a", "peer-b", "peer-c"} {
|
||||
found := false
|
||||
for _, got := range checked {
|
||||
found = found || got == want
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("checked declarations %#v do not include %q", checked, want)
|
||||
}
|
||||
}
|
||||
if len(loaded.Inspection.Diagnostics()) != 2 || loaded.Inspection.Diagnostics()[0].Code != CodeVersionWarning || loaded.Inspection.Diagnostics()[1].Code != CodeVersionWarning {
|
||||
t.Fatalf("accepted underlay warning was not retained: %#v", loaded.Inspection.Diagnostics())
|
||||
}
|
||||
sources := loaded.Inspection.Sources()
|
||||
if len(sources) != 4 || sources[1].Layer != LayerUnderlay || sources[1].UnderlayIndex != 1 || sources[2].Layer != LayerUnderlay || sources[2].UnderlayIndex != 1 || sources[3].Layer != LayerUnderlay || sources[3].UnderlayIndex != 2 {
|
||||
t.Fatalf("underlay source records = %#v", sources)
|
||||
}
|
||||
defaults, ok := loaded.Config["defaults"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("defaults = %#v", loaded.Config["defaults"])
|
||||
}
|
||||
firstValue, ok := defaults["first"].(map[string]any)
|
||||
if !ok || firstValue["values"] == nil {
|
||||
t.Fatalf("first mounted contribution = %#v", defaults["first"])
|
||||
}
|
||||
if meta, ok := firstValue["meta"].(map[string]any); !ok || len(meta) != 0 {
|
||||
t.Fatalf("consumed nested declaration did not preserve its empty ancestor: %#v", firstValue["meta"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceVersionUnderlaysRejectDeclarationsHiddenByMain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var checked []string
|
||||
loaded, err := LoadLayered(Input{Source: Bytes("main", []byte("meta: {version: main-v}\nservice: primary\n"))}, testSecretBinding(), LayeredOptions[map[string]any]{
|
||||
Definition: Definition[map[string]any]{Version: &VersionRule{
|
||||
Path: Keys("meta", "version"),
|
||||
Check: func(version Version, _ *Validation) error {
|
||||
checked = append(checked, version.Text)
|
||||
if version.Text == "rejected-peer" {
|
||||
return errors.New("peer declaration is not accepted")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}},
|
||||
SourceVersionMode: sourceVersionModeIfPresent,
|
||||
Underlays: []Underlay{{Inputs: []Input{{Source: Bytes("underlay", []byte("meta: {version: rejected-peer}\nservice: shadowed\n"))}}}},
|
||||
})
|
||||
if err == nil || loaded != nil {
|
||||
t.Fatalf("LoadLayered = %#v, %v, want hidden underlay rejection", loaded, err)
|
||||
}
|
||||
loadErr, ok := err.(*LoadError)
|
||||
if !ok || len(loadErr.Diagnostics()) != 1 || loadErr.Diagnostics()[0].Code != CodeVersionHookFailed {
|
||||
t.Fatalf("underlay rejection diagnostics = %#v", err)
|
||||
}
|
||||
if len(checked) != 2 || checked[0] != "main-v" || checked[1] != "rejected-peer" {
|
||||
t.Fatalf("checked declarations = %#v", checked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceVersionUnderlaysRequireActualHeadersButSkipAbsentSources(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newOptions := func(underlays []Underlay) LayeredOptions[map[string]any] {
|
||||
return LayeredOptions[map[string]any]{
|
||||
Definition: Definition[map[string]any]{Version: &VersionRule{
|
||||
Path: Keys("meta", "version"),
|
||||
Check: func(Version, *Validation) error { return nil },
|
||||
}},
|
||||
SourceVersionMode: sourceVersionModeRequired,
|
||||
Underlays: underlays,
|
||||
}
|
||||
}
|
||||
main := Input{Source: Bytes("main", []byte("meta: {version: main-v}\n"))}
|
||||
|
||||
t.Run("required absence", func(t *testing.T) {
|
||||
loaded, err := LoadLayered(main, testSecretBinding(), newOptions([]Underlay{{Inputs: []Input{{Source: Bytes("missing", []byte("value: present\n"))}}}}))
|
||||
if err == nil || loaded != nil {
|
||||
t.Fatalf("LoadLayered = %#v, %v, want missing underlay declaration", loaded, err)
|
||||
}
|
||||
loadErr, ok := err.(*LoadError)
|
||||
if !ok || len(loadErr.Diagnostics()) != 1 || loadErr.Diagnostics()[0].Code != CodeVersionError {
|
||||
t.Fatalf("missing underlay diagnostics = %#v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty group and NoSource", func(t *testing.T) {
|
||||
loaded, err := LoadLayered(main, testSecretBinding(), newOptions([]Underlay{{}, {Inputs: []Input{{Source: NoSource()}}}}))
|
||||
if err != nil {
|
||||
t.Fatalf("LoadLayered with absent contributions: %v", err)
|
||||
}
|
||||
if sources := loaded.Inspection.Sources(); len(sources) != 1 {
|
||||
t.Fatalf("empty groups and NoSource created source records: %#v", sources)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSourceVersionUnderlayImportsKeepLocalHeadersAndMountedDestinations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
directory := t.TempDir()
|
||||
underlayPath := writeImportFile(t, directory, "underlay.yml", `meta: {version: peer-root}
|
||||
configkit:
|
||||
imports:
|
||||
- path: nested.yml
|
||||
destination: [nested]
|
||||
root: retained
|
||||
`)
|
||||
writeImportFile(t, directory, "nested.yml", "meta: {version: peer-nested}\nname: nested\n")
|
||||
var checked []string
|
||||
loaded, err := LoadLayered(Input{
|
||||
Source: Bytes("main", []byte("meta: {version: main-v}\n")), Destination: Keys("service"),
|
||||
}, testSecretBinding(), LayeredOptions[map[string]any]{
|
||||
Definition: Definition[map[string]any]{Version: &VersionRule{
|
||||
Path: Keys("meta", "version"),
|
||||
Check: func(version Version, _ *Validation) error {
|
||||
checked = append(checked, version.Text)
|
||||
return nil
|
||||
},
|
||||
}},
|
||||
SourceVersionMode: sourceVersionModeRequired,
|
||||
Underlays: []Underlay{{Inputs: []Input{{
|
||||
Source: File(underlayPath), Imports: true, Destination: Keys("defaults"),
|
||||
}}}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("LoadLayered: %v", err)
|
||||
}
|
||||
if len(checked) != 3 {
|
||||
t.Fatalf("checked source declarations = %#v", checked)
|
||||
}
|
||||
for _, want := range []string{"main-v", "peer-root", "peer-nested"} {
|
||||
found := false
|
||||
for _, got := range checked {
|
||||
found = found || got == want
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("checked source declarations %#v do not include %q", checked, want)
|
||||
}
|
||||
}
|
||||
sources := loaded.Inspection.Sources()
|
||||
if len(sources) != 3 || sources[1].Path != filepath.Clean(underlayPath) || sources[2].Destination.String() != "/defaults/nested" {
|
||||
t.Fatalf("underlay and nested import records = %#v", sources)
|
||||
}
|
||||
if sources[1].Layer != LayerUnderlay || sources[1].UnderlayIndex != 1 || sources[2].Layer != LayerUnderlay || sources[2].UnderlayIndex != 1 {
|
||||
t.Fatalf("underlay and nested import tiers = %#v", sources)
|
||||
}
|
||||
defaults, ok := loaded.Config["defaults"].(map[string]any)
|
||||
if !ok || defaults["root"] != "retained" {
|
||||
t.Fatalf("mounted underlay root = %#v", loaded.Config["defaults"])
|
||||
}
|
||||
nested, ok := defaults["nested"].(map[string]any)
|
||||
if !ok || nested["name"] != "nested" {
|
||||
t.Fatalf("mounted nested import = %#v", defaults["nested"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceVersionUnderlaysProtectRemainingMountedPayload(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loaded, err := LoadLayered(Input{
|
||||
Source: Bytes("main", []byte("meta: {version: main-v}\n")), Destination: Keys("service"),
|
||||
}, testSecretBinding(), LayeredOptions[map[string]any]{
|
||||
Definition: Definition[map[string]any]{Version: &VersionRule{
|
||||
Path: Keys("meta", "version"), Check: func(Version, *Validation) error { return nil },
|
||||
}},
|
||||
SourceVersionMode: sourceVersionModeRequired,
|
||||
Underlays: []Underlay{{Inputs: []Input{{Source: Bytes("underlay", []byte(
|
||||
"meta: {version: accepted}\nservice: {meta: {version: payload}}\n",
|
||||
))}}}},
|
||||
})
|
||||
if err == nil || loaded != nil {
|
||||
t.Fatalf("LoadLayered = %#v, %v, want protected remaining payload rejection", loaded, err)
|
||||
}
|
||||
loadErr, ok := err.(*LoadError)
|
||||
if !ok || len(loadErr.Diagnostics()) != 1 || loadErr.Diagnostics()[0].Code != CodeUnderlayVersion {
|
||||
t.Fatalf("remaining payload diagnostics = %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceVersionImportAccountingIncludesConsumedHeaderNodes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user