Index CLI override validation
This commit is contained in:
@@ -206,6 +206,132 @@ func TestOverrideValidationRejectsPathsOverlapValuesAndProtectedTargets(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideOverlapIndexPreservesFirstAcceptedSource(t *testing.T) {
|
||||
type config struct {
|
||||
Nested map[string]any `yaml:"nested"`
|
||||
}
|
||||
base := Bytes("override-overlap.yml", []byte("nested: {}\n"))
|
||||
_, err := testLoad(base, Options[config]{Overrides: []Override{
|
||||
{Path: Keys("nested"), Value: map[string]any{}, Flag: "--nested"},
|
||||
{Path: Keys("nested", "first"), Value: "one", Flag: "--first"},
|
||||
{Path: Keys("nested", "second"), Value: "two", Flag: "--second"},
|
||||
}})
|
||||
if err == nil {
|
||||
t.Fatal("load succeeded with overlapping overrides")
|
||||
}
|
||||
var loadErr *LoadError
|
||||
if !errors.As(err, &loadErr) {
|
||||
t.Fatalf("error = %T %v, want *LoadError", err, err)
|
||||
}
|
||||
diagnostics := loadErr.Diagnostics()
|
||||
var conflicts []Diagnostic
|
||||
for _, diagnostic := range diagnostics {
|
||||
if diagnostic.Code == CodeOverrideOverlap {
|
||||
conflicts = append(conflicts, diagnostic)
|
||||
}
|
||||
}
|
||||
if len(conflicts) != 2 {
|
||||
t.Fatalf("overlap diagnostics = %#v, want two conflicts", diagnostics)
|
||||
}
|
||||
for index, path := range []string{"/nested/first", "/nested/second"} {
|
||||
if conflicts[index].Path.String() != path || len(conflicts[index].Related) != 1 || conflicts[index].Related[0].SourceID != "source-2" {
|
||||
t.Fatalf("conflict[%d] = %#v, want path %q related to first CLI source", index, conflicts[index], path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideOverlapIndexIncludesAcceptedPathWithInvalidValue(t *testing.T) {
|
||||
type config struct {
|
||||
Nested map[string]any `yaml:"nested"`
|
||||
}
|
||||
base := Bytes("override-value-overlap.yml", []byte("nested: {}\n"))
|
||||
_, err := testLoad(base, Options[config]{Overrides: []Override{
|
||||
{Path: Keys("nested"), Value: time.Second, Flag: "--nested"},
|
||||
{Path: Keys("nested", "child"), Value: "value", Flag: "--child"},
|
||||
}})
|
||||
if err == nil {
|
||||
t.Fatal("load succeeded with an invalid value and overlapping path")
|
||||
}
|
||||
var loadErr *LoadError
|
||||
if !errors.As(err, &loadErr) {
|
||||
t.Fatalf("error = %T %v, want *LoadError", err, err)
|
||||
}
|
||||
var invalidValue, overlap bool
|
||||
for _, diagnostic := range loadErr.Diagnostics() {
|
||||
switch diagnostic.Code {
|
||||
case CodeOverrideInvalidValue:
|
||||
invalidValue = diagnostic.Path.String() == "/nested"
|
||||
case CodeOverrideOverlap:
|
||||
overlap = diagnostic.Path.String() == "/nested/child" && len(diagnostic.Related) == 1 && diagnostic.Related[0].SourceID == "source-2"
|
||||
}
|
||||
}
|
||||
if !invalidValue || !overlap {
|
||||
t.Fatalf("diagnostics = %#v, want invalid parent value and child overlap related to its source", loadErr.Diagnostics())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectedOverridePathDoesNotCauseOverlap(t *testing.T) {
|
||||
type config struct {
|
||||
Scalar string `yaml:"scalar"`
|
||||
}
|
||||
base := Bytes("override-rejected-overlap.yml", []byte("scalar: value\n"))
|
||||
_, err := testLoad(base, Options[config]{Overrides: []Override{
|
||||
{Path: Keys("scalar", "child"), Value: "invalid", Flag: "--child"},
|
||||
{Path: Keys("scalar"), Value: "valid", Flag: "--scalar"},
|
||||
}})
|
||||
if err == nil {
|
||||
t.Fatal("load succeeded with a path traversing a scalar")
|
||||
}
|
||||
var loadErr *LoadError
|
||||
if !errors.As(err, &loadErr) {
|
||||
t.Fatalf("error = %T %v, want *LoadError", err, err)
|
||||
}
|
||||
diagnostics := loadErr.Diagnostics()
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != CodeOverrideInvalidPath || diagnostics[0].Path.String() != "/scalar/child" {
|
||||
t.Fatalf("diagnostics = %#v, want only the invalid descendant path", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionRejectedOverridePathDoesNotCauseOverlap(t *testing.T) {
|
||||
type config struct {
|
||||
Nested map[string]any `yaml:"nested"`
|
||||
}
|
||||
base := Bytes("override-version-overlap.yml", []byte("nested: {}\n"))
|
||||
version := &VersionRule{Path: Keys("nested", "locked"), Check: func(Version, *Validation) error { return nil }}
|
||||
_, err := testLoad(base, Options[config]{
|
||||
Definition: Definition[config]{Version: version},
|
||||
Overrides: []Override{
|
||||
{Path: Keys("nested"), Value: map[string]any{}, Flag: "--nested"},
|
||||
{Path: Keys("nested", "other"), Value: "allowed", Flag: "--other"},
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("load succeeded despite an override of a schema-version ancestor")
|
||||
}
|
||||
var loadErr *LoadError
|
||||
if !errors.As(err, &loadErr) {
|
||||
t.Fatalf("error = %T %v, want *LoadError", err, err)
|
||||
}
|
||||
diagnostics := loadErr.Diagnostics()
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != CodeOverrideVersion || diagnostics[0].Path.String() != "/nested" {
|
||||
t.Fatalf("diagnostics = %#v, want only the protected ancestor path", diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapedOverrideKeysStayDistinctFromMultipleSegments(t *testing.T) {
|
||||
type config struct {
|
||||
Nested map[string]any `yaml:"nested"`
|
||||
}
|
||||
base := Bytes("override-escaped-keys.yml", []byte("nested: {}\n"))
|
||||
_, err := testLoad(base, Options[config]{Overrides: []Override{
|
||||
{Path: Keys("nested", "a/b"), Value: "single key", Flag: "--single"},
|
||||
{Path: Keys("nested", "a", "b"), Value: "nested key", Flag: "--nested"},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("load rejected distinct escaped path tokens: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideCannotReplaceSchemaVersionAncestor(t *testing.T) {
|
||||
type schemaConfig struct {
|
||||
Version string `yaml:"version"`
|
||||
|
||||
Reference in New Issue
Block a user