161 lines
5.9 KiB
Go
161 lines
5.9 KiB
Go
package engine
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
)
|
|
|
|
type unsupportedDynamicRoot struct {
|
|
Child unsupportedDynamicChild `yaml:"child"`
|
|
Invalid chan int `yaml:"invalid"`
|
|
}
|
|
|
|
type unsupportedDynamicChild struct {
|
|
Parent *unsupportedDynamicRoot `yaml:"parent"`
|
|
}
|
|
|
|
type secretDynamicRoot struct {
|
|
Nested secretDynamicNested `yaml:"nested"`
|
|
}
|
|
|
|
type secretDynamicNested struct {
|
|
Token Secret `yaml:"token"`
|
|
}
|
|
|
|
type dynamicTransactionConfig struct {
|
|
First any `yaml:"first"`
|
|
Second any `yaml:"second"`
|
|
Good any `yaml:"good"`
|
|
}
|
|
|
|
func TestDynamicDescriptorFailuresRollBackProvisionalTypes(t *testing.T) {
|
|
called := false
|
|
result, err := testLoad[dynamicTransactionConfig](NoSource(), Options[dynamicTransactionConfig]{Definition: Definition[dynamicTransactionConfig]{
|
|
Resolve: func(config *dynamicTransactionConfig, resolution *Resolution) error {
|
|
called = true
|
|
Normalize(resolution, Keys("first"), &config.First, any((*unsupportedDynamicRoot)(nil)), "try typed nil")
|
|
Normalize(resolution, Keys("second"), &config.Second, any(&unsupportedDynamicRoot{}), "try non-nil")
|
|
Normalize(resolution, Keys("good"), &config.Good, any(map[string]string{"valid": "value"}), "continue with supported value")
|
|
return nil
|
|
},
|
|
}})
|
|
var loadErr *LoadError
|
|
if !called || result != nil || !errors.As(err, &loadErr) {
|
|
t.Fatalf("result = %#v, called=%v, error=%v", result, called, err)
|
|
}
|
|
paths := make(map[string]Code)
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
paths[diagnostic.Path.String()] = diagnostic.Code
|
|
}
|
|
for _, path := range []string{"/first", "/second"} {
|
|
if paths[path] != CodeResolveInvalidValue {
|
|
t.Errorf("diagnostic at %s = %q, want safe invalid-value failure", path, paths[path])
|
|
}
|
|
}
|
|
|
|
schema := mustSchemaFor[dynamicTransactionConfig](t)
|
|
operation := valueOperation{schema: schema}
|
|
prior, issue := operation.dynamicDescriptor(reflect.TypeFor[map[string]string](), Keys("prior"))
|
|
if issue != nil {
|
|
t.Fatalf("prior valid dynamic descriptor issue = %#v", issue)
|
|
}
|
|
if _, issue := operation.dynamicDescriptor(reflect.TypeFor[unsupportedDynamicRoot](), Keys("failure")); issue == nil || issue.code != CodeSchemaDynamicType {
|
|
t.Fatalf("unsupported dynamic descriptor issue = %#v", issue)
|
|
}
|
|
if _, approved := operation.approvedDynamic[reflect.TypeFor[unsupportedDynamicRoot]()]; approved {
|
|
t.Fatal("unsupported dynamic descriptor was cached as eligible")
|
|
}
|
|
if schema.types[reflect.TypeFor[map[string]string]()] != prior {
|
|
t.Fatal("failed build removed a previously completed descriptor")
|
|
}
|
|
for _, current := range []reflect.Type{
|
|
reflect.TypeFor[unsupportedDynamicRoot](),
|
|
reflect.TypeFor[unsupportedDynamicChild](),
|
|
reflect.TypeFor[*unsupportedDynamicRoot](),
|
|
} {
|
|
if _, exists := schema.types[current]; exists {
|
|
t.Errorf("failed build retained provisional descriptor for %v", current)
|
|
}
|
|
}
|
|
if _, issue := operation.dynamicDescriptor(reflect.TypeFor[unsupportedDynamicRoot](), Keys("again")); issue == nil || issue.path.String() != "/again" {
|
|
t.Fatalf("repeated failure issue = %#v", issue)
|
|
}
|
|
|
|
secretSchema, schemaIssue := buildSchema(reflect.TypeFor[dynamicTransactionConfig](), testSecretBinding())
|
|
if schemaIssue != nil {
|
|
t.Fatalf("secret schema issue = %#v", schemaIssue)
|
|
}
|
|
secretOperation := valueOperation{schema: secretSchema}
|
|
if _, issue := secretOperation.dynamicDescriptor(reflect.TypeFor[secretDynamicRoot](), Keys("secret")); issue == nil || issue.code != CodeSchemaDynamicType {
|
|
t.Fatalf("dynamic secret eligibility issue = %#v", issue)
|
|
}
|
|
if len(secretOperation.approvedDynamic) != 0 {
|
|
t.Fatalf("secret-bearing dynamic descriptor was cached as eligible: %#v", secretOperation.approvedDynamic)
|
|
}
|
|
for _, current := range []reflect.Type{reflect.TypeFor[secretDynamicRoot](), reflect.TypeFor[secretDynamicNested](), reflect.TypeFor[Secret]()} {
|
|
if _, exists := secretSchema.types[current]; exists {
|
|
t.Errorf("secret eligibility failure retained provisional descriptor for %v", current)
|
|
}
|
|
}
|
|
}
|
|
|
|
type wideDynamicEligibilityValue struct {
|
|
Alpha string
|
|
Bravo string
|
|
Charlie string
|
|
Delta string
|
|
Echo string
|
|
Foxtrot string
|
|
Golf string
|
|
Hotel string
|
|
India string
|
|
Juliet string
|
|
Kilo string
|
|
Lima string
|
|
Mike string
|
|
November string
|
|
Oscar string
|
|
Papa string
|
|
}
|
|
|
|
type repeatedNilDynamicEligibilityConfig struct {
|
|
Values []any
|
|
}
|
|
|
|
func TestDynamicEligibilityIsReusedWithinProjectionOperation(t *testing.T) {
|
|
schema, schemaIssue := buildSchema(reflect.TypeFor[repeatedNilDynamicEligibilityConfig](), testSecretBinding())
|
|
if schemaIssue != nil {
|
|
t.Fatalf("schema issue = %#v", schemaIssue)
|
|
}
|
|
values := make([]any, 32)
|
|
for index := range values {
|
|
values[index] = (*wideDynamicEligibilityValue)(nil)
|
|
}
|
|
configuration := repeatedNilDynamicEligibilityConfig{Values: values}
|
|
operation := valueOperation{schema: schema, limits: defaultProjectionLimits(), active: make(map[valueVisit]bool)}
|
|
node, issue := operation.project(reflect.ValueOf(configuration), schema.root, Path{}, 0)
|
|
if issue != nil {
|
|
t.Fatalf("repeated typed-nil projection issue = %#v", issue)
|
|
}
|
|
if len(node.Mapping) != 1 || len(node.Mapping[0].Value.Sequence) != len(values) {
|
|
t.Fatalf("projected repeated values = %#v", node)
|
|
}
|
|
for index, child := range node.Mapping[0].Value.Sequence {
|
|
if child.Kind != document.Null {
|
|
t.Errorf("projected value %d kind = %v, want null", index, child.Kind)
|
|
}
|
|
}
|
|
current := reflect.TypeFor[*wideDynamicEligibilityValue]()
|
|
if len(operation.approvedDynamic) != 1 || operation.approvedDynamic[current] == nil ||
|
|
operation.approvedDynamic[current] != schema.types[current] {
|
|
t.Fatalf("operation eligibility cache = %#v", operation.approvedDynamic)
|
|
}
|
|
otherOperation := valueOperation{schema: schema, limits: defaultProjectionLimits(), active: make(map[valueVisit]bool)}
|
|
if len(otherOperation.approvedDynamic) != 0 {
|
|
t.Fatalf("dynamic eligibility leaked into another operation: %#v", otherOperation.approvedDynamic)
|
|
}
|
|
}
|