Files
ConfigKit/internal/engine/dynamic_descriptor_transaction_test.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)
}
}