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) } }