Escape schema resources and reuse compiled plans
This commit is contained in:
@@ -3,9 +3,11 @@ package validate
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -330,55 +332,6 @@ func TestStandardValidatorJSONSchemaCompilationError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardValidatorLoadSchemaDocumentSuccess(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(tmp, "schema.json"), []byte(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
}`), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
v := NewStandardValidator(tmp)
|
||||
loader, ok := v.(SchemaDocumentLoader)
|
||||
if !ok {
|
||||
t.Fatal("standard validator must implement SchemaDocumentLoader")
|
||||
}
|
||||
|
||||
doc, err := loader.LoadSchemaDocument(context.Background(), "schema.json")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
obj, ok := doc.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected object document, got %#v", doc)
|
||||
}
|
||||
if obj["type"] != "object" {
|
||||
t.Fatalf("expected schema type=object, got %#v", obj["type"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardValidatorLoadSchemaDocumentInvalidJSON(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(tmp, "schema.json"), []byte(`{`), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
v := NewStandardValidator(tmp)
|
||||
loader, ok := v.(SchemaDocumentLoader)
|
||||
if !ok {
|
||||
t.Fatal("standard validator must implement SchemaDocumentLoader")
|
||||
}
|
||||
|
||||
_, err := loader.LoadSchemaDocument(context.Background(), "schema.json")
|
||||
if err == nil {
|
||||
t.Fatal("expected decode error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSValidatorJSONSchemaSuccess(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/events.schema.json": &fstest.MapFile{Data: []byte(`{
|
||||
@@ -461,17 +414,70 @@ func TestFSValidatorPreparedSchemaSurvivesSourceMutation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSValidatorJSONSchemaRegistrationError(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/%zz.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
func TestFSValidatorEscapesSchemaResourcePath(t *testing.T) {
|
||||
for _, schemaName := range []string{"%zz.json", "space name.json", "hash#.json", "query?.json", "rún.json"} {
|
||||
t.Run(schemaName, func(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/" + schemaName: &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)}, domain.OutputContract{
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)}, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: schemaName,
|
||||
})
|
||||
if err != nil || !res.IsValid {
|
||||
t.Fatalf("validate schema %q: result=%#v error=%v", schemaName, res, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemaReferencesPreserveEscapedFilenames(t *testing.T) {
|
||||
for _, name := range []string{"%2F.json", "space name.json", "hash#.json", "query?.json", "rún.json"} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rootName := "root-" + name
|
||||
childName := "child-" + name
|
||||
childPath := "nested/" + childName
|
||||
reference := (&url.URL{Path: childPath}).EscapedPath()
|
||||
rootSchema := []byte(`{"$ref":` + strconv.Quote(reference) + `}`)
|
||||
childSchema := []byte(`{"type":"integer","minimum":2}`)
|
||||
|
||||
t.Run("fs.FS", func(t *testing.T) {
|
||||
validator := NewFSValidator(fstest.MapFS{
|
||||
"schemas/" + rootName: &fstest.MapFile{Data: rootSchema},
|
||||
"schemas/" + childPath: &fstest.MapFile{Data: childSchema},
|
||||
}, "schemas")
|
||||
assertSchemaValidation(t, validator, rootName)
|
||||
})
|
||||
|
||||
t.Run("operating system files", func(t *testing.T) {
|
||||
if runtime.GOOS == "windows" && strings.ContainsAny(rootName+childName, `<>:"/\|?*`) {
|
||||
t.Skip("filename is not legal on Windows")
|
||||
}
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "nested"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, rootName), rootSchema, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, filepath.FromSlash(childPath)), childSchema, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertSchemaValidation(t, NewStandardValidator(root), rootName)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertSchemaValidation(t *testing.T, validator Validator, schemaPath string) {
|
||||
t.Helper()
|
||||
result, err := validator.Validate(context.Background(), &domain.Artifact{Body: []byte(`2`)}, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "%zz.json",
|
||||
SchemaPath: schemaPath,
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "failed to register JSON schema") {
|
||||
t.Fatalf("expected schema registration error, got result=%#v error=%v", res, err)
|
||||
if err != nil || !result.IsValid {
|
||||
t.Fatalf("validate schema %q: result=%#v error=%v", schemaPath, result, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,25 +567,6 @@ func TestFSValidatorSingleSchemaFileUsesBaseName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSValidatorLoadSchemaDocument(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/schema.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
loader, ok := v.(SchemaDocumentLoader)
|
||||
if !ok {
|
||||
t.Fatal("fs validator must implement SchemaDocumentLoader")
|
||||
}
|
||||
|
||||
doc, err := loader.LoadSchemaDocument(context.Background(), "schema.json")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
obj, ok := doc.(map[string]any)
|
||||
if !ok || obj["type"] != "object" {
|
||||
t.Fatalf("unexpected schema document: %#v", doc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardValidatorJSONSchemaReferenceBoundaries(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(root, "child.json"), []byte(`{
|
||||
|
||||
Reference in New Issue
Block a user