Add validator chain provenance
This commit is contained in:
@@ -2,88 +2,116 @@ package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||
)
|
||||
|
||||
func TestValidatorRegistryBehavior(t *testing.T) {
|
||||
runRegistryBehaviorTests(t, registryBehaviorCase[contracts.Validator]{
|
||||
name: "ValidatorRegistry",
|
||||
key: "generic-validator",
|
||||
stage: StageValidate,
|
||||
wrongStage: StageExtract,
|
||||
newRegistry: func() any {
|
||||
return NewValidatorRegistry()
|
||||
},
|
||||
register: func(registry any, key string, constructor func() (contracts.Validator, error)) error {
|
||||
return registry.(*ValidatorRegistry).Register(key, constructor)
|
||||
},
|
||||
registerWithSpec: func(registry any, spec ModuleSpec, constructor func() (contracts.Validator, error)) error {
|
||||
return registry.(*ValidatorRegistry).RegisterWithSpec(spec, constructor)
|
||||
},
|
||||
build: func(registry any, key string) (contracts.Validator, error) {
|
||||
return registry.(*ValidatorRegistry).Build(key)
|
||||
},
|
||||
spec: func(registry any, key string) (ModuleSpec, bool) {
|
||||
return registry.(*ValidatorRegistry).Spec(key)
|
||||
},
|
||||
registeredKeys: func(registry any) []string {
|
||||
return registry.(*ValidatorRegistry).RegisteredKeys()
|
||||
},
|
||||
nilRegister: func(key string, constructor func() (contracts.Validator, error)) error {
|
||||
var registry *ValidatorRegistry
|
||||
return registry.Register(key, constructor)
|
||||
},
|
||||
nilBuild: func(key string) (contracts.Validator, error) {
|
||||
var registry *ValidatorRegistry
|
||||
return registry.Build(key)
|
||||
},
|
||||
nilSpec: func(key string) (ModuleSpec, bool) {
|
||||
var registry *ValidatorRegistry
|
||||
return registry.Spec(key)
|
||||
},
|
||||
nilRegisteredKey: func() []string {
|
||||
var registry *ValidatorRegistry
|
||||
return registry.RegisteredKeys()
|
||||
},
|
||||
constructor: func(key string) func() (contracts.Validator, error) {
|
||||
return func() (contracts.Validator, error) {
|
||||
return registryValidator{name: key}, nil
|
||||
}
|
||||
},
|
||||
moduleKey: func(module contracts.Validator) string {
|
||||
return module.Name()
|
||||
},
|
||||
})
|
||||
registry := NewValidatorRegistry()
|
||||
if err := registry.Register(" generic-validator ", validatorConstructor("generic-validator", contracts.ExecutionClassDeterministic)); err != nil {
|
||||
t.Fatalf("Register() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
validator, err := registry.Build("generic-validator")
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v, want nil", err)
|
||||
}
|
||||
if validator.Name() != "generic-validator" {
|
||||
t.Fatalf("validator name = %q, want generic-validator", validator.Name())
|
||||
}
|
||||
|
||||
spec, ok := registry.Spec(" generic-validator ")
|
||||
if !ok {
|
||||
t.Fatal("Spec() ok = false, want true")
|
||||
}
|
||||
want := ValidatorSpec{Key: "generic-validator", ExecutionClass: contracts.ExecutionClassDeterministic}
|
||||
if !reflect.DeepEqual(spec, want) {
|
||||
t.Fatalf("Spec() = %#v, want %#v", spec, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorRegistryRegistersSpecs(t *testing.T) {
|
||||
registry := NewValidatorRegistry()
|
||||
spec := ValidatorSpec{Key: " llm-validator ", ExecutionClass: contracts.ExecutionClassLLMBacked}
|
||||
if err := registry.RegisterWithSpec(spec, validatorConstructor("llm-validator", contracts.ExecutionClassLLMBacked)); err != nil {
|
||||
t.Fatalf("RegisterWithSpec() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
got, ok := registry.Spec("llm-validator")
|
||||
if !ok {
|
||||
t.Fatal("Spec() ok = false, want true")
|
||||
}
|
||||
want := ValidatorSpec{Key: "llm-validator", ExecutionClass: contracts.ExecutionClassLLMBacked}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("Spec() = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorRegistryRegisteredSpecsAreSorted(t *testing.T) {
|
||||
registry := NewValidatorRegistry()
|
||||
for _, key := range []string{"zeta", "alpha"} {
|
||||
if err := registry.Register(key, validatorConstructor(key, contracts.ExecutionClassDeterministic)); err != nil {
|
||||
t.Fatalf("Register(%q) error = %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
specs := registry.RegisteredSpecs()
|
||||
if len(specs) != 2 || specs[0].Key != "alpha" || specs[1].Key != "zeta" {
|
||||
t.Fatalf("RegisteredSpecs() = %#v, want sorted specs", specs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorRegistryRejectsUnsupportedExecutionClass(t *testing.T) {
|
||||
registry := NewValidatorRegistry()
|
||||
if err := registry.Register("invalid-validator", func() (contracts.Validator, error) {
|
||||
return invalidExecutionClassValidator{name: "invalid-validator"}, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Register() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
_, err := registry.Build("invalid-validator")
|
||||
err := registry.RegisterWithSpec(
|
||||
ValidatorSpec{Key: "invalid-validator", ExecutionClass: contracts.ExecutionClass("unsupported")},
|
||||
validatorConstructor("invalid-validator", contracts.ExecutionClass("unsupported")),
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("Build() error = nil, want unsupported execution class error")
|
||||
t.Fatal("RegisterWithSpec() error = nil, want unsupported execution class error")
|
||||
}
|
||||
}
|
||||
|
||||
type invalidExecutionClassValidator struct {
|
||||
name string
|
||||
func TestValidatorRegistryRejectsConstructorExecutionClassMismatch(t *testing.T) {
|
||||
registry := NewValidatorRegistry()
|
||||
if err := registry.RegisterWithSpec(
|
||||
ValidatorSpec{Key: "validator", ExecutionClass: contracts.ExecutionClassDeterministic},
|
||||
validatorConstructor("validator", contracts.ExecutionClassLLMBacked),
|
||||
); err != nil {
|
||||
t.Fatalf("RegisterWithSpec() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
_, err := registry.Build("validator")
|
||||
if err == nil {
|
||||
t.Fatal("Build() error = nil, want execution class mismatch")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "execution class") {
|
||||
t.Fatalf("Build() error = %q, want execution class context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (validator invalidExecutionClassValidator) Name() string {
|
||||
type testValidator struct {
|
||||
name string
|
||||
executionClass contracts.ExecutionClass
|
||||
}
|
||||
|
||||
func validatorConstructor(name string, executionClass contracts.ExecutionClass) ValidatorConstructor {
|
||||
return func() (contracts.Validator, error) {
|
||||
return testValidator{name: name, executionClass: executionClass}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (validator testValidator) Name() string {
|
||||
return validator.name
|
||||
}
|
||||
|
||||
func (validator invalidExecutionClassValidator) ExecutionClass() contracts.ExecutionClass {
|
||||
return contracts.ExecutionClass("unsupported")
|
||||
func (validator testValidator) ExecutionClass() contracts.ExecutionClass {
|
||||
return validator.executionClass
|
||||
}
|
||||
|
||||
func (validator invalidExecutionClassValidator) Validate(ctx context.Context, req contracts.ValidationRequest) (contracts.ValidationResult, error) {
|
||||
func (validator testValidator) Validate(ctx context.Context, req contracts.ValidationRequest) (contracts.ValidationResult, error) {
|
||||
return contracts.ValidationResult{Approved: true}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user