package engine import ( "errors" "reflect" "testing" ) 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 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) } 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) } } }