2026-09-12 15:58:06 +00:00
package engine
import (
"errors"
"os"
"path/filepath"
"reflect"
"strings"
"sync/atomic"
"testing"
"time"
)
func TestLoadSingleSourceKindsAndDefaultsOnly ( t * testing . T ) {
t . Parallel ( )
type config struct {
Name string ` yaml:"name" `
}
input := [ ] byte ( "name: configured\n" )
temporary := t . TempDir ( )
filename := filepath . Join ( temporary , "config.yml" )
if err := os . WriteFile ( filename , input , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
tests := [ ] struct {
name string
source func ( ) Source
want string
wantSources int
} {
{ name : "file" , source : func ( ) Source { return File ( filename ) } , want : "configured" , wantSources : 1 } ,
{ name : "bytes" , source : func ( ) Source { return Bytes ( "memory.yml" , input ) } , want : "configured" , wantSources : 1 } ,
{ name : "reader" , source : func ( ) Source { return Reader ( "reader.yml" , strings . NewReader ( string ( input ) ) ) } , want : "configured" , wantSources : 1 } ,
{ name : "no source" , source : NoSource , want : "default" , wantSources : 0 } ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
var defaultsCalls atomic . Int32
2026-09-12 16:04:31 +00:00
result , err := testLoad ( test . source ( ) , Options [ config ] { Definition : Definition [ config ] {
2026-09-12 15:58:06 +00:00
Defaults : func ( ) config {
defaultsCalls . Add ( 1 )
return config { Name : "default" }
} ,
} } )
if err != nil {
t . Fatal ( err )
}
if result . Config . Name != test . want || defaultsCalls . Load ( ) != 1 || len ( result . Sources ( ) ) != test . wantSources {
t . Fatalf ( "result = %#v, calls = %d, sources = %#v" , result . Config , defaultsCalls . Load ( ) , result . Sources ( ) )
}
} )
}
}
func TestLoadVersionStatesWarningsAndSafeFailures ( t * testing . T ) {
t . Parallel ( )
type config struct {
SchemaVersion * string ` yaml:"schema_version" `
}
versionPath := Keys ( "schema_version" )
tests := [ ] struct {
name string
source Source
want Version
wantLine int
wantErr Code
returnOnly bool
} {
{ name : "absent" , source : Bytes ( "absent.yml" , [ ] byte ( "{}\n" ) ) , want : Version { } , wantLine : 1 } ,
{ name : "no source absent" , source : NoSource ( ) , want : Version { } } ,
{ name : "null" , source : Bytes ( "null.yml" , [ ] byte ( "schema_version: null\n" ) ) , want : Version { Present : true , Null : true } , wantLine : 1 } ,
{ name : "string" , source : Bytes ( "string.yml" , [ ] byte ( "schema_version: app.v1\n" ) ) , want : Version { Present : true , Text : "app.v1" } , wantLine : 1 } ,
{ name : "integer" , source : Bytes ( "integer.yml" , [ ] byte ( "schema_version: 1\n" ) ) , want : Version { Present : true , Text : "1" } , wantLine : 1 } ,
{ name : "boolean" , source : Bytes ( "boolean.yml" , [ ] byte ( "schema_version: true\n" ) ) , wantErr : CodeVersionType } ,
{ name : "sequence" , source : Bytes ( "sequence.yml" , [ ] byte ( "schema_version: []\n" ) ) , wantErr : CodeVersionType } ,
{ name : "returned error" , source : Bytes ( "return.yml" , [ ] byte ( "{}\n" ) ) , wantErr : CodeVersionHookFailed , returnOnly : true } ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
called := false
rule := & VersionRule { Path : versionPath , Check : func ( version Version , validation * Validation ) error {
called = true
if test . returnOnly {
return errors . New ( "CANARY_VERSION_ERROR" )
}
if version != test . want {
t . Errorf ( "version = %#v, want %#v" , version , test . want )
}
validation . Warning ( versionPath , "review schema compatibility" , Keys ( "missing_related" ) )
return nil
} }
2026-09-12 16:04:31 +00:00
result , err := testLoad ( test . source , Options [ config ] { Definition : Definition [ config ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if test . wantErr != "" {
if result != nil || err == nil {
t . Fatalf ( "result, err = %#v, %v" , result , err )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) {
t . Fatalf ( "error type = %T" , err )
}
diagnostics := loadError . Diagnostics ( )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != test . wantErr || strings . Contains ( diagnostics [ 0 ] . Message , "CANARY" ) {
t . Fatalf ( "diagnostics = %#v" , diagnostics )
}
if test . wantErr == CodeVersionType && called {
t . Fatal ( "checker ran for an unsupported version kind" )
}
return
}
if err != nil {
t . Fatal ( err )
}
diagnostics := result . Diagnostics ( )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeVersionWarning || diagnostics [ 0 ] . Severity != SeverityWarning {
t . Fatalf ( "diagnostics = %#v" , diagnostics )
}
if test . wantLine == 0 && ( diagnostics [ 0 ] . Primary != nil || len ( diagnostics [ 0 ] . Related ) != 0 ) {
t . Fatalf ( "synthetic diagnostic locations = %#v" , diagnostics [ 0 ] )
}
if test . wantLine != 0 && ( diagnostics [ 0 ] . Primary == nil || diagnostics [ 0 ] . Primary . Line != test . wantLine || len ( diagnostics [ 0 ] . Related ) != 1 ) {
t . Fatalf ( "diagnostic locations = %#v" , diagnostics [ 0 ] )
}
} )
}
}
func TestLoadDecodesIntegerVersionOnlyAtConfiguredStringPath ( t * testing . T ) {
t . Parallel ( )
type stringConfig struct {
Version string ` yaml:"version" `
}
type pointerConfig struct {
Version * string ` yaml:"version" `
}
versionPath := Keys ( "version" )
for _ , test := range [ ] struct {
name string
source string
load func ( Source , * VersionRule ) ( string , error )
} {
{ name : "integer into string" , source : "version: 4\n" , load : func ( source Source , rule * VersionRule ) ( string , error ) {
2026-09-12 16:04:31 +00:00
result , err := testLoad ( source , Options [ stringConfig ] { Definition : Definition [ stringConfig ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
return "" , err
}
return result . Config . Version , nil
} } ,
{ name : "string into string" , source : "version: \"4\"\n" , load : func ( source Source , rule * VersionRule ) ( string , error ) {
2026-09-12 16:04:31 +00:00
result , err := testLoad ( source , Options [ stringConfig ] { Definition : Definition [ stringConfig ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
return "" , err
}
return result . Config . Version , nil
} } ,
{ name : "integer into pointer" , source : "version: 4\n" , load : func ( source Source , rule * VersionRule ) ( string , error ) {
2026-09-12 16:04:31 +00:00
result , err := testLoad ( source , Options [ pointerConfig ] { Definition : Definition [ pointerConfig ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
return "" , err
}
if result . Config . Version == nil {
t . Fatal ( "version pointer is nil" )
}
return * result . Config . Version , nil
} } ,
{ name : "string into pointer" , source : "version: \"4\"\n" , load : func ( source Source , rule * VersionRule ) ( string , error ) {
2026-09-12 16:04:31 +00:00
result , err := testLoad ( source , Options [ pointerConfig ] { Definition : Definition [ pointerConfig ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
return "" , err
}
if result . Config . Version == nil {
t . Fatal ( "version pointer is nil" )
}
return * result . Config . Version , nil
} } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
var checked Version
rule := & VersionRule { Path : versionPath , Check : func ( version Version , _ * Validation ) error {
checked = version
return nil
} }
got , err := test . load ( Bytes ( "version.yml" , [ ] byte ( test . source ) ) , rule )
if err != nil {
t . Fatal ( err )
}
if got != "4" || checked != ( Version { Present : true , Text : "4" } ) {
t . Fatalf ( "value = %q, checked = %#v" , got , checked )
}
} )
}
2026-09-12 16:04:31 +00:00
if result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ stringConfig ] { } ) ; result != nil {
2026-09-12 15:58:06 +00:00
t . Fatalf ( "load without rule = %#v, %v" , result , err )
} else {
var loadError * LoadError
if ! errors . As ( err , & loadError ) || len ( loadError . Diagnostics ( ) ) != 1 || loadError . Diagnostics ( ) [ 0 ] . Code != CodeDecodeType {
t . Fatalf ( "load without rule error = %v" , err )
}
}
type unrelatedConfig struct {
Version string ` yaml:"version" `
Label string ` yaml:"label" `
}
rule := & VersionRule { Path : versionPath , Check : func ( Version , * Validation ) error { return nil } }
2026-09-12 16:04:31 +00:00
if result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\nlabel: 5\n" ) ) , Options [ unrelatedConfig ] { Definition : Definition [ unrelatedConfig ] { Version : rule } } ) ; result != nil {
2026-09-12 15:58:06 +00:00
t . Fatalf ( "unrelated coercion = %#v, %v" , result , err )
} else {
var loadError * LoadError
if ! errors . As ( err , & loadError ) || len ( loadError . Diagnostics ( ) ) != 1 || loadError . Diagnostics ( ) [ 0 ] . Code != CodeDecodeType || loadError . Diagnostics ( ) [ 0 ] . Path . String ( ) != "/label" {
t . Fatalf ( "unrelated coercion error = %v" , err )
}
}
}
func TestLoadVersionScalarAndPathBoundaries ( t * testing . T ) {
t . Parallel ( )
type config struct {
Version string ` yaml:"version" `
}
for _ , test := range [ ] struct {
name string
value string
want string
} {
{ name : "zero" , value : "0" , want : "0" } ,
{ name : "negative" , value : "-7" , want : "-7" } ,
{ name : "negative zero" , value : "-0" , want : "-0" } ,
{ name : "beyond int64" , value : "123456789012345678901234567890" , want : "123456789012345678901234567890" } ,
{ name : "empty string" , value : ` "" ` , want : "" } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
var checked Version
rule := & VersionRule { Path : Keys ( "version" ) , Check : func ( version Version , _ * Validation ) error {
checked = version
return nil
} }
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: " + test . value + "\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
t . Fatal ( err )
}
if result . Config . Version != test . want || checked . Text != test . want || ! checked . Present || checked . Null {
t . Fatalf ( "configuration = %#v, checked = %#v" , result . Config , checked )
}
} )
}
for _ , test := range [ ] struct {
name string
value string
} {
{ name : "fraction" , value : "4.0" } ,
{ name : "exponent" , value : "4e0" } ,
{ name : "boolean" , value : "true" } ,
{ name : "mapping" , value : "{}" } ,
{ name : "sequence" , value : "[]" } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
called := false
rule := & VersionRule { Path : Keys ( "version" ) , Check : func ( Version , * Validation ) error {
called = true
return nil
} }
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: " + test . value + "\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if result != nil || called {
t . Fatalf ( "result = %#v, checker called = %v" , result , called )
}
requireDiagnostic ( t , err , CodeVersionType , "/version" )
} )
}
type namedVersion string
type nested struct {
Versions map [ string ] * * namedVersion ` yaml:"versions" `
Sibling namedVersion ` yaml:"sibling" `
}
parsed , err := ParsePath ( "/versions/0" )
if err != nil {
t . Fatal ( err )
}
rule := & VersionRule { Path : parsed , Check : func ( version Version , _ * Validation ) error {
if version . Text != "4" {
t . Errorf ( "version = %#v" , version )
}
return nil
} }
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "nested.yml" , [ ] byte ( "versions:\n \"0\": 4\nsibling: quoted\n" ) ) , Options [ nested ] { Definition : Definition [ nested ] { Version : rule } } )
2026-09-12 15:58:06 +00:00
if err != nil {
t . Fatal ( err )
}
if result . Config . Versions [ "0" ] == nil || * result . Config . Versions [ "0" ] == nil || * * result . Config . Versions [ "0" ] != "4" || result . Config . Sibling != "quoted" {
t . Fatalf ( "nested configuration = %#v" , result . Config )
}
type sequenceConfig struct {
Versions [ ] namedVersion ` yaml:"versions" `
}
2026-09-12 16:04:31 +00:00
sequenceResult , err := testLoad ( Bytes ( "sequence.yml" , [ ] byte ( "versions: [6]\n" ) ) , Options [ sequenceConfig ] { Definition : Definition [ sequenceConfig ] { Version : & VersionRule { Path : parsed , Check : func ( Version , * Validation ) error { return nil } } } } )
2026-09-12 15:58:06 +00:00
if err != nil || len ( sequenceResult . Config . Versions ) != 1 || sequenceResult . Config . Versions [ 0 ] != "6" {
t . Fatalf ( "sequence configuration = %#v, %v" , sequenceResult , err )
}
type escaped struct {
Versions map [ string ] namedVersion ` yaml:"versions" `
}
escapedPath , err := ParsePath ( "/versions/a~1b" )
if err != nil {
t . Fatal ( err )
}
2026-09-12 16:04:31 +00:00
escapedResult , err := testLoad ( Bytes ( "escaped.yml" , [ ] byte ( "versions:\n a/b: 9\n" ) ) , Options [ escaped ] { Definition : Definition [ escaped ] { Version : & VersionRule { Path : escapedPath , Check : func ( Version , * Validation ) error { return nil } } } } )
2026-09-12 15:58:06 +00:00
if err != nil || escapedResult . Config . Versions [ "a/b" ] != "9" {
t . Fatalf ( "escaped configuration = %#v, %v" , escapedResult , err )
}
siblingRule := & VersionRule { Path : Keys ( "versions" , "target" ) , Check : func ( Version , * Validation ) error { return nil } }
2026-09-12 16:04:31 +00:00
if result , err := testLoad ( Bytes ( "sibling.yml" , [ ] byte ( "versions:\n target: 4\n other: 5\n" ) ) , Options [ escaped ] { Definition : Definition [ escaped ] { Version : siblingRule } } ) ; result != nil {
2026-09-12 15:58:06 +00:00
t . Fatalf ( "same-type sibling result = %#v, %v" , result , err )
} else {
requireDiagnostic ( t , err , CodeDecodeType , "/versions/other" )
}
}
func TestLoadVersionDestinationTypesKeepOrdinaryDecoding ( t * testing . T ) {
t . Parallel ( )
check := func ( Version , * Validation ) error { return nil }
t . Run ( "integer destination" , func ( t * testing . T ) {
type config struct {
Version int8 ` yaml:"version" `
}
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
if err != nil || result . Config . Version != 4 {
t . Fatalf ( "result = %#v, %v" , result , err )
}
2026-09-12 16:04:31 +00:00
result , err = testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 999\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
if result != nil {
t . Fatalf ( "range result = %#v" , result )
}
requireDiagnostic ( t , err , CodeDecodeRange , "/version" )
2026-09-12 16:04:31 +00:00
result , err = testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: \"4\"\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
if result != nil {
t . Fatalf ( "quoted integer result = %#v" , result )
}
requireDiagnostic ( t , err , CodeDecodeType , "/version" )
} )
t . Run ( "dynamic destination" , func ( t * testing . T ) {
type config struct {
Version any ` yaml:"version" `
}
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
if err != nil || result . Config . Version != int64 ( 4 ) {
t . Fatalf ( "result = %#v, %v" , result , err )
}
2026-09-12 16:04:31 +00:00
result , err = testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 123456789012345678901234567890\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
if result != nil {
t . Fatalf ( "range result = %#v" , result )
}
requireDiagnostic ( t , err , CodeDecodeRange , "/version" )
} )
for _ , test := range [ ] struct {
name string
load func ( ) error
} {
{ name : "text codec" , load : func ( ) error {
type config struct {
Version validatedText ` yaml:"version" `
}
2026-09-12 16:04:31 +00:00
_ , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
return err
} } ,
{ name : "duration" , load : func ( ) error {
type config struct {
Version time . Duration ` yaml:"version" `
}
2026-09-12 16:04:31 +00:00
_ , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
return err
} } ,
{ name : "secret" , load : func ( ) error {
type config struct {
Version Secret ` yaml:"version" `
}
2026-09-12 16:04:31 +00:00
_ , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) , Check : check } } } )
2026-09-12 15:58:06 +00:00
return err
} } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
requireDiagnostic ( t , test . load ( ) , CodeDecodeType , "/version" )
} )
}
}
func TestLoadVersionPolicyStopsLaterHooks ( t * testing . T ) {
t . Parallel ( )
type config struct {
Version string ` yaml:"version" `
}
var defaultsCalled , resolutionCalled , validationCalled bool
rule := & VersionRule { Path : Keys ( "version" ) , Check : func ( version Version , validation * Validation ) error {
if version . Text != "4" {
t . Errorf ( "version = %#v" , version )
}
validation . Error ( Keys ( "version" ) , "unsupported configuration version" )
return nil
} }
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "version: 4\n" ) ) , Options [ config ] { Definition : Definition [ config ] {
2026-09-12 15:58:06 +00:00
Defaults : func ( ) config { defaultsCalled = true ; return config { } } ,
Version : rule ,
Resolve : func ( * config , * Resolution ) error { resolutionCalled = true ; return nil } ,
Validate : func ( * config , * Validation ) { validationCalled = true } ,
} } )
if result != nil || defaultsCalled || resolutionCalled || validationCalled {
t . Fatalf ( "result = %#v, hooks = %v/%v/%v" , result , defaultsCalled , resolutionCalled , validationCalled )
}
requireDiagnostic ( t , err , CodeVersionError , "/version" )
}
func requireDiagnostic ( t * testing . T , err error , code Code , path string ) {
t . Helper ( )
var loadError * LoadError
if ! errors . As ( err , & loadError ) {
t . Fatalf ( "error = %T %v" , err , err )
}
diagnostics := loadError . Diagnostics ( )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != code || diagnostics [ 0 ] . Path . String ( ) != path || diagnostics [ 0 ] . Primary == nil {
t . Fatalf ( "diagnostics = %#v" , diagnostics )
}
}
func TestLoadVersionReportSuppressesReturnedErrorTextAndDefaults ( t * testing . T ) {
t . Parallel ( )
type config struct {
Name string ` yaml:"name" `
}
var defaultsCalls atomic . Int32
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "name: value\n" ) ) , Options [ config ] { Definition : Definition [ config ] {
2026-09-12 15:58:06 +00:00
Defaults : func ( ) config {
defaultsCalls . Add ( 1 )
return config { Name : "default" }
} ,
Version : & VersionRule { Path : Keys ( "schema_version" ) , Check : func ( _ Version , validation * Validation ) error {
validation . Error ( Keys ( "schema_version" ) , "schema_version is required" )
return errors . New ( "CANARY_RETURNED_ERROR" )
} } ,
} } )
if result != nil || err == nil || defaultsCalls . Load ( ) != 0 {
t . Fatalf ( "result = %#v, err = %v, calls = %d" , result , err , defaultsCalls . Load ( ) )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) {
t . Fatalf ( "error type = %T" , err )
}
diagnostics := loadError . Diagnostics ( )
if len ( diagnostics ) != 1 || diagnostics [ 0 ] . Code != CodeVersionError || strings . Contains ( diagnostics [ 0 ] . Message , "CANARY" ) {
t . Fatalf ( "diagnostics = %#v" , diagnostics )
}
}
2026-10-15 15:04:57 +00:00
func TestVersionWarningDoesNotSuppressReturnedError ( t * testing . T ) {
t . Parallel ( )
type config struct { }
versionPath := Keys ( "schema_version" )
result , err := testLoad ( Bytes ( "version.yml" , [ ] byte ( "{}\n" ) ) , Options [ config ] {
Definition : Definition [ config ] { Version : & VersionRule {
Path : versionPath ,
Check : func ( _ Version , validation * Validation ) error {
validation . Warning ( versionPath , "review schema compatibility" )
return errors . New ( "CANARY_VERSION_ERROR" )
} ,
} } ,
} )
if result != nil || err == nil {
t . Fatalf ( "result = %#v, err = %v" , result , err )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) {
t . Fatalf ( "error type = %T" , err )
}
diagnostics := loadError . Diagnostics ( )
var warning , hookFailure bool
for _ , diagnostic := range diagnostics {
if strings . Contains ( diagnostic . Message , "CANARY" ) {
t . Fatalf ( "returned checker error text was exposed: %#v" , diagnostic )
}
warning = warning || diagnostic . Code == CodeVersionWarning && diagnostic . Severity == SeverityWarning
hookFailure = hookFailure || diagnostic . Code == CodeVersionHookFailed && diagnostic . Severity == SeverityError
}
if ! warning || ! hookFailure {
t . Fatalf ( "diagnostics = %#v" , diagnostics )
}
}
2026-09-12 15:58:06 +00:00
func TestLoadMetadataReflectsDefaultsInputAndDetachedRecords ( t * testing . T ) {
t . Parallel ( )
type nested struct {
Kept string ` yaml:"kept" `
Set string ` yaml:"set" `
}
type config struct {
Nested nested ` yaml:"nested" `
Nullable map [ string ] string ` yaml:"nullable" `
}
2026-09-12 16:04:31 +00:00
result , err := testLoad ( Bytes ( "metadata.yml" , [ ] byte ( "nested: {set: configured}\nnullable: null\n" ) ) , Options [ config ] { Definition : Definition [ config ] {
2026-09-12 15:58:06 +00:00
Defaults : func ( ) config {
return config { Nested : nested { Kept : "default" , Set : "default" } , Nullable : map [ string ] string { "key" : "value" } }
} ,
} } )
if err != nil {
t . Fatal ( err )
}
metadata := result . Metadata ( )
kept , ok := metadata . Lookup ( Keys ( "nested" , "kept" ) )
if ! ok || kept . Explicit || kept . Origin . Layer != LayerDefault {
t . Fatalf ( "kept = %#v, %v" , kept , ok )
}
set , ok := metadata . Lookup ( Keys ( "nested" , "set" ) )
if ! ok || ! set . Explicit || set . Origin . Layer != LayerBase || len ( set . Origin . Locations ) != 1 {
t . Fatalf ( "set = %#v, %v" , set , ok )
}
nullable , ok := metadata . Lookup ( Keys ( "nullable" ) )
if ! ok || ! nullable . Explicit || ! nullable . Null || nullable . Origin . Layer != LayerBase {
t . Fatalf ( "nullable = %#v, %v" , nullable , ok )
}
if metadata . IsExplicit ( Keys ( "nested" , "kept" ) ) || ! metadata . IsExplicit ( Keys ( "nested" , "set" ) ) {
t . Fatal ( "explicit presence disagrees with lookup" )
}
if history := metadata . History ( Keys ( "nested" , "set" ) ) ; len ( history ) != 2 || history [ 0 ] . Origin . Layer != LayerDefault || history [ 1 ] . Origin . Layer != LayerBase || len ( history [ 1 ] . Previous ) != 1 {
t . Fatalf ( "history = %#v" , history )
}
set . Origin . Locations [ 0 ] . Line = 99
history := metadata . History ( Keys ( "nested" , "set" ) )
history [ 1 ] . Origin . Locations [ 0 ] . Line = 99
sources := result . Sources ( )
sources [ 0 ] . Label = "changed"
diagnostics := result . Diagnostics ( )
if diagnostics != nil {
diagnostics [ 0 ] . Message = "changed"
}
fresh , _ := result . Metadata ( ) . Lookup ( Keys ( "nested" , "set" ) )
if fresh . Origin . Locations [ 0 ] . Line == 99 || result . Sources ( ) [ 0 ] . Label != "metadata.yml" || result . Diagnostics ( ) != nil {
t . Fatal ( "result inspection aliases internal records" )
}
}
func TestLoadRejectsUnavailableOptionsAndDisabledDirectives ( t * testing . T ) {
t . Parallel ( )
type config struct {
Name string ` yaml:"name" `
}
validSource := func ( ) Source { return Bytes ( "options.yml" , [ ] byte ( "name: value\n" ) ) }
tests := [ ] struct {
name string
source Source
options [ ] Options [ config ]
code Code
} {
{ name : "multiple options" , source : validSource ( ) , options : [ ] Options [ config ] { { } , { } } , code : CodeOptionsInvalid } ,
{ name : "imports without filesystem context" , source : validSource ( ) , options : [ ] Options [ config ] { { Imports : true } } , code : CodeOptionsInvalid } ,
{ name : "incomplete filesystem" , source : validSource ( ) , options : [ ] Options [ config ] { { Filesystem : & FilesystemContext { } } } , code : CodeOptionsInvalid } ,
{ name : "missing version checker" , source : validSource ( ) , options : [ ] Options [ config ] { { Definition : Definition [ config ] { Version : & VersionRule { Path : Keys ( "version" ) } } } } , code : CodeOptionsInvalid } ,
{ name : "no-source filesystem" , source : NoSource ( ) , options : [ ] Options [ config ] { { Filesystem : & FilesystemContext { } } } , code : CodeOptionsInvalid } ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2026-09-12 16:04:31 +00:00
result , err := testLoad ( test . source , test . options ... )
2026-09-12 15:58:06 +00:00
if result != nil || err == nil {
t . Fatalf ( "result, err = %#v, %v" , result , err )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) || len ( loadError . Diagnostics ( ) ) == 0 || loadError . Diagnostics ( ) [ 0 ] . Code != test . code {
t . Fatalf ( "error = %#v" , err )
}
} )
}
2026-09-12 16:04:31 +00:00
result , err := testLoad [ config ] ( Bytes ( "directive.yml" , [ ] byte ( "nested:\n configkit: {}\n" ) ) )
2026-09-12 15:58:06 +00:00
if result != nil || err == nil {
t . Fatalf ( "result, err = %#v, %v" , result , err )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) {
t . Fatal ( err )
}
diagnostic := loadError . Diagnostics ( ) [ 0 ]
if diagnostic . Code != CodeImportDisabled || diagnostic . Path . String ( ) != "/nested/configkit" || diagnostic . Primary == nil || diagnostic . Primary . Line != 2 {
t . Fatalf ( "diagnostic = %#v" , diagnostic )
}
}
func TestLoadReturnsNilForSourceSchemaAndDecodeFailures ( t * testing . T ) {
t . Parallel ( )
type invalidSchema struct {
Channel chan int ` yaml:"channel" `
}
type validConfig struct {
Known int ` yaml:"known" `
}
tests := [ ] struct {
name string
run func ( ) ( any , error )
code Code
} {
2026-09-12 16:04:31 +00:00
{ name : "source" , run : func ( ) ( any , error ) { return testLoad [ validConfig ] ( File ( filepath . Join ( t . TempDir ( ) , "missing.yml" ) ) ) } , code : CodeSourceMissing } ,
{ name : "schema" , run : func ( ) ( any , error ) { return testLoad [ invalidSchema ] ( Bytes ( "schema.yml" , [ ] byte ( "{}\n" ) ) ) } , code : CodeSchemaUnsupportedType } ,
{ name : "decode" , run : func ( ) ( any , error ) { return testLoad [ validConfig ] ( Bytes ( "decode.yml" , [ ] byte ( "unknown: 1\n" ) ) ) } , code : CodeDecodeUnknownField } ,
2026-09-12 15:58:06 +00:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
result , err := test . run ( )
if ! isNilInterface ( result ) || err == nil {
t . Fatalf ( "result, err = %#v, %v" , result , err )
}
var loadError * LoadError
if ! errors . As ( err , & loadError ) || len ( loadError . Diagnostics ( ) ) != 1 || loadError . Diagnostics ( ) [ 0 ] . Code != test . code {
t . Fatalf ( "error = %#v" , err )
}
} )
}
}
func isNilInterface ( value any ) bool {
if value == nil {
return true
}
reflected := reflect . ValueOf ( value )
return reflected . Kind ( ) == reflect . Pointer && reflected . IsNil ( )
}