143 lines
5.2 KiB
Go
143 lines
5.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"testing/fstest"
|
|
)
|
|
|
|
func TestLayeredDestinationDiagnosticsUseEffectivePathsAndAuthoredLocations(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("strict conflict retains both source locations", func(t *testing.T) {
|
|
t.Parallel()
|
|
loaded, err := LoadLayered(
|
|
Input{Source: NoSource()},
|
|
testSecretBinding(),
|
|
LayeredOptions[map[string]any]{Underlays: []Underlay{{Inputs: []Input{
|
|
{Source: Bytes("left", []byte("section:\n value: left\n")), Destination: Keys("application")},
|
|
{Source: Bytes("right", []byte("section:\n value: right\n")), Destination: Keys("application")},
|
|
}}}},
|
|
)
|
|
if loaded != nil {
|
|
t.Fatalf("conflicting load returned a result: %#v", loaded)
|
|
}
|
|
diagnostic := firstDestinationDiagnostic(t, err)
|
|
if diagnostic.Code != CodeComposeConflict || diagnostic.Path.String() != "/application/section/value" {
|
|
t.Fatalf("conflict diagnostic = %#v", diagnostic)
|
|
}
|
|
if diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-1" || diagnostic.Primary.Line != 2 {
|
|
t.Fatalf("primary location = %#v", diagnostic.Primary)
|
|
}
|
|
if len(diagnostic.Related) != 1 || diagnostic.Related[0].SourceID != "source-2" || diagnostic.Related[0].Line != 2 {
|
|
t.Fatalf("related locations = %#v", diagnostic.Related)
|
|
}
|
|
})
|
|
|
|
t.Run("decode failure keeps effective path and content line", func(t *testing.T) {
|
|
t.Parallel()
|
|
type section struct {
|
|
Count int `yaml:"count"`
|
|
}
|
|
type config struct {
|
|
Application struct {
|
|
Section section `yaml:"section"`
|
|
} `yaml:"application"`
|
|
}
|
|
loaded, err := LoadLayered(
|
|
Input{Source: Bytes("main", []byte("section:\n count: invalid\n")), Destination: Keys("application")},
|
|
testSecretBinding(),
|
|
LayeredOptions[config]{},
|
|
)
|
|
if loaded != nil {
|
|
t.Fatalf("invalid load returned a result: %#v", loaded)
|
|
}
|
|
diagnostic := firstDestinationDiagnostic(t, err)
|
|
if diagnostic.Code != CodeDecodeType || diagnostic.Path.String() != "/application/section/count" {
|
|
t.Fatalf("decode diagnostic = %#v", diagnostic)
|
|
}
|
|
if diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-1" || diagnostic.Primary.Line != 2 {
|
|
t.Fatalf("decode location = %#v", diagnostic.Primary)
|
|
}
|
|
})
|
|
|
|
t.Run("imported version failure uses effective path and import source", func(t *testing.T) {
|
|
t.Parallel()
|
|
directory := t.TempDir()
|
|
writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: fragment.yml}]}\n")
|
|
writeImportFile(t, directory, "fragment.yml", "version: forbidden\n")
|
|
rule := &VersionRule{Path: Keys("application", "version"), Check: func(Version, *Validation) error { return nil }}
|
|
loaded, err := LoadLayered(
|
|
Input{Source: NoSource()},
|
|
testSecretBinding(),
|
|
LayeredOptions[map[string]any]{
|
|
Definition: Definition[map[string]any]{Version: rule},
|
|
Underlays: []Underlay{{Inputs: []Input{{
|
|
Source: File(filepath.Join(directory, "main.yml")), Imports: true, Destination: Keys("application"),
|
|
}}}},
|
|
},
|
|
)
|
|
if loaded != nil {
|
|
t.Fatalf("invalid import returned a result: %#v", loaded)
|
|
}
|
|
diagnostic := firstDestinationDiagnostic(t, err)
|
|
if diagnostic.Code != CodeImportVersion || diagnostic.Path.String() != "/application/version" {
|
|
t.Fatalf("import version diagnostic = %#v", diagnostic)
|
|
}
|
|
if diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-2" || diagnostic.Primary.Line != 1 {
|
|
t.Fatalf("import version location = %#v", diagnostic.Primary)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid destination has no fictional source location", func(t *testing.T) {
|
|
t.Parallel()
|
|
loaded, err := LoadLayered(
|
|
Input{Source: Bytes("main", []byte("value: present\n")), Destination: Keys("configkit")},
|
|
testSecretBinding(),
|
|
LayeredOptions[map[string]any]{},
|
|
)
|
|
if loaded != nil {
|
|
t.Fatalf("invalid destination returned a result: %#v", loaded)
|
|
}
|
|
diagnostic := firstDestinationDiagnostic(t, err)
|
|
if diagnostic.Code != CodeOptionsInvalid || diagnostic.Path.String() != "/configkit" || diagnostic.Primary != nil {
|
|
t.Fatalf("invalid destination diagnostic = %#v", diagnostic)
|
|
}
|
|
})
|
|
|
|
t.Run("malformed source directive keeps its local path and location", func(t *testing.T) {
|
|
t.Parallel()
|
|
filesystem := NewFilesystem("bundle", fstest.MapFS{
|
|
"main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [true]}\nvalue: present\n")},
|
|
})
|
|
loaded, err := LoadLayered(
|
|
Input{Source: filesystem.File("main.yml"), Imports: true, Destination: Keys("application")},
|
|
testSecretBinding(),
|
|
LayeredOptions[map[string]any]{},
|
|
)
|
|
if loaded != nil {
|
|
t.Fatalf("malformed directive returned a result: %#v", loaded)
|
|
}
|
|
diagnostic := firstDestinationDiagnostic(t, err)
|
|
if diagnostic.Code != CodeImportInvalid || diagnostic.Path.String() != "/configkit/imports/0" {
|
|
t.Fatalf("malformed directive diagnostic = %#v", diagnostic)
|
|
}
|
|
if diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-1" || diagnostic.Primary.Line != 1 {
|
|
t.Fatalf("malformed directive location = %#v", diagnostic.Primary)
|
|
}
|
|
})
|
|
}
|
|
|
|
func firstDestinationDiagnostic(t *testing.T, err error) Diagnostic {
|
|
t.Helper()
|
|
loadError, ok := err.(*LoadError)
|
|
if !ok {
|
|
t.Fatalf("load error = %#v", err)
|
|
}
|
|
diagnostics := loadError.Diagnostics()
|
|
if len(diagnostics) == 0 {
|
|
t.Fatal("load error has no diagnostics")
|
|
}
|
|
return diagnostics[0]
|
|
}
|