Migrate validator tests to prepared plans
This commit is contained in:
@@ -17,12 +17,21 @@ import (
|
||||
"go.maximumdirect.net/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
func prepareForTest(t testing.TB, preparer ValidationPreparer, contract domain.OutputContract) PreparedValidation {
|
||||
t.Helper()
|
||||
plan, err := preparer.PrepareValidation(context.Background(), contract)
|
||||
if err != nil {
|
||||
t.Fatalf("prepare validation: %v", err)
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
func TestStandardValidatorNoneSkipped(t *testing.T) {
|
||||
v := NewStandardValidator("")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte("ignored")}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationNone,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte("ignored")})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -44,9 +53,9 @@ func TestStandardValidatorBasicSuccess(t *testing.T) {
|
||||
{name: "invalid UTF-8 byte", body: []byte{0xff}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: tc.body}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationBasic,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: tc.body})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -68,9 +77,9 @@ func TestStandardValidatorBasicFailureEmpty(t *testing.T) {
|
||||
{name: "Unicode whitespace", body: []byte("\u2003\u00a0\u3000")},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: tc.body}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationBasic,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: tc.body})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -110,7 +119,7 @@ func TestStandardValidatorReportsZeroRepairAttempts(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(tc.body)}, tc.contract)
|
||||
res, err := prepareForTest(t, v, tc.contract).Validate(context.Background(), &domain.Artifact{Body: []byte(tc.body)})
|
||||
if err != nil {
|
||||
t.Fatalf("validate: %v", err)
|
||||
}
|
||||
@@ -124,9 +133,9 @@ func TestStandardValidatorReportsZeroRepairAttempts(t *testing.T) {
|
||||
func TestStandardValidatorJSONSuccess(t *testing.T) {
|
||||
v := NewStandardValidator("")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"ok":true}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSON,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"ok":true}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -138,9 +147,9 @@ func TestStandardValidatorJSONSuccess(t *testing.T) {
|
||||
func TestStandardValidatorJSONFailure(t *testing.T) {
|
||||
v := NewStandardValidator("")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"ok":`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSON,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"ok":`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -175,9 +184,9 @@ func TestJSONValidationChecksCompleteSyntaxWithoutChangingArtifact(t *testing.T)
|
||||
before := append([]byte(nil), body...)
|
||||
artifact := &domain.Artifact{Body: body}
|
||||
|
||||
result, err := validator.Validate(context.Background(), artifact, domain.OutputContract{
|
||||
result, err := prepareForTest(t, validator, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSON,
|
||||
})
|
||||
}).Validate(context.Background(), artifact)
|
||||
if err != nil {
|
||||
t.Fatalf("validate JSON: %v", err)
|
||||
}
|
||||
@@ -213,10 +222,10 @@ func TestStandardValidatorJSONSchemaSuccess(t *testing.T) {
|
||||
|
||||
v := NewStandardValidator(tmp)
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -303,10 +312,10 @@ func TestStandardValidatorJSONSchemaNestedSchemaPathSuccess(t *testing.T) {
|
||||
|
||||
v := NewStandardValidator(tmp)
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: filepath.Join("dnd", "schema.json"),
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -318,7 +327,7 @@ func TestStandardValidatorJSONSchemaNestedSchemaPathSuccess(t *testing.T) {
|
||||
func TestStandardValidatorJSONSchemaNestedSchemaPathMissing(t *testing.T) {
|
||||
v := NewStandardValidator(t.TempDir())
|
||||
|
||||
_, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)}, domain.OutputContract{
|
||||
_, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: filepath.Join("dnd", "missing.json"),
|
||||
})
|
||||
@@ -343,10 +352,10 @@ func TestStandardValidatorJSONSchemaFailure(t *testing.T) {
|
||||
|
||||
v := NewStandardValidator(tmp)
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"count":1}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"count":1}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -361,7 +370,7 @@ func TestStandardValidatorJSONSchemaFailure(t *testing.T) {
|
||||
func TestStandardValidatorJSONSchemaSchemaLoadError(t *testing.T) {
|
||||
v := NewStandardValidator(t.TempDir())
|
||||
|
||||
_, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"name":"eris"}`)}, domain.OutputContract{
|
||||
_, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "missing.json",
|
||||
})
|
||||
@@ -377,7 +386,7 @@ func TestStandardValidatorJSONSchemaCompilationError(t *testing.T) {
|
||||
}
|
||||
v := NewStandardValidator(tmp)
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)}, domain.OutputContract{
|
||||
res, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
@@ -398,10 +407,10 @@ func TestFSValidatorJSONSchemaSuccess(t *testing.T) {
|
||||
}`)},
|
||||
}, "schemas")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "events.schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -472,10 +481,10 @@ func TestFSValidatorEscapesSchemaResourcePath(t *testing.T) {
|
||||
"schemas/" + schemaName: &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: schemaName,
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)})
|
||||
if err != nil || !res.IsValid {
|
||||
t.Fatalf("validate schema %q: result=%#v error=%v", schemaName, res, err)
|
||||
}
|
||||
@@ -521,9 +530,9 @@ func TestSchemaReferencesPreserveEscapedFilenames(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertSchemaValidation(t *testing.T, validator Validator, schemaPath string) {
|
||||
func assertSchemaValidation(t *testing.T, validator ValidationPreparer, schemaPath string) {
|
||||
t.Helper()
|
||||
result, err := validator.Validate(context.Background(), &domain.Artifact{Body: []byte(`2`)}, domain.OutputContract{
|
||||
result, err := validator.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: schemaPath,
|
||||
})
|
||||
@@ -544,10 +553,10 @@ func TestFSValidatorJSONSchemaPathContainment(t *testing.T) {
|
||||
}`)},
|
||||
}, "schemas")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "nested/events.schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -573,7 +582,7 @@ func TestFSValidatorJSONSchemaPathContainment(t *testing.T) {
|
||||
"schemas/outside.schema.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
|
||||
_, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
_, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: tc.schemaPath,
|
||||
})
|
||||
@@ -598,10 +607,10 @@ func TestFSValidatorSingleSchemaFileUsesBaseName(t *testing.T) {
|
||||
}`)},
|
||||
}, "events.schema.json")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
res, err := prepareForTest(t, v, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "events.schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -609,7 +618,7 @@ func TestFSValidatorSingleSchemaFileUsesBaseName(t *testing.T) {
|
||||
t.Fatalf("expected passed/valid, got status=%q valid=%v", res.Status, res.IsValid)
|
||||
}
|
||||
|
||||
_, err = v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
_, err = v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "other.schema.json",
|
||||
})
|
||||
@@ -654,11 +663,15 @@ func TestStandardValidatorJSONSchemaReferenceBoundaries(t *testing.T) {
|
||||
}
|
||||
|
||||
v := NewStandardValidator(root)
|
||||
result, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`"value"`)}, domain.OutputContract{
|
||||
plan, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "root.json",
|
||||
})
|
||||
if tc.wantError == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("prepare schema: %v", err)
|
||||
}
|
||||
result, err := plan.Validate(context.Background(), &domain.Artifact{Body: []byte(`"value"`)})
|
||||
if err != nil || !result.IsValid {
|
||||
t.Fatalf("expected contained reference to validate, got result=%#v error=%v", result, err)
|
||||
}
|
||||
@@ -695,11 +708,15 @@ func TestFSValidatorJSONSchemaReferenceBoundaries(t *testing.T) {
|
||||
"schemas/child.json": &fstest.MapFile{Data: []byte(`{"type":"string"}`)},
|
||||
"outside.json": &fstest.MapFile{Data: []byte(`{"type":"string"}`)},
|
||||
}, "schemas")
|
||||
result, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`"value"`)}, domain.OutputContract{
|
||||
plan, err := v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "root.json",
|
||||
})
|
||||
if tc.wantError == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("prepare schema: %v", err)
|
||||
}
|
||||
result, err := plan.Validate(context.Background(), &domain.Artifact{Body: []byte(`"value"`)})
|
||||
if err != nil || !result.IsValid {
|
||||
t.Fatalf("expected supported reference to validate, got result=%#v error=%v", result, err)
|
||||
}
|
||||
@@ -780,10 +797,10 @@ func TestJSONSchemaNumericConstraintsRetainJSONPrecision(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
validator := source.new(t, []byte(tc.schema))
|
||||
result, err := validator.Validate(context.Background(), &domain.Artifact{Body: []byte(tc.instance)}, domain.OutputContract{
|
||||
result, err := prepareForTest(t, validator, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(tc.instance)})
|
||||
if err != nil {
|
||||
t.Fatalf("validate JSON Schema instance: %v", err)
|
||||
}
|
||||
@@ -801,7 +818,7 @@ func TestJSONSchemaDecodingRequiresOneCompleteDocument(t *testing.T) {
|
||||
t.Run(source.name, func(t *testing.T) {
|
||||
for _, schema := range []string{`{"type":`, `{} {}`} {
|
||||
validator := source.new(t, []byte(schema))
|
||||
_, err := validator.Validate(context.Background(), &domain.Artifact{Body: []byte(`1`)}, domain.OutputContract{
|
||||
_, err := validator.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
@@ -812,10 +829,10 @@ func TestJSONSchemaDecodingRequiresOneCompleteDocument(t *testing.T) {
|
||||
|
||||
validator := source.new(t, []byte(`{}`))
|
||||
for _, instance := range []string{`{"value":`, `1 2`} {
|
||||
result, err := validator.Validate(context.Background(), &domain.Artifact{Body: []byte(instance)}, domain.OutputContract{
|
||||
result, err := prepareForTest(t, validator, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
}).Validate(context.Background(), &domain.Artifact{Body: []byte(instance)})
|
||||
if err != nil {
|
||||
t.Fatalf("instance %q: expected completed validation, got %v", instance, err)
|
||||
}
|
||||
@@ -844,11 +861,7 @@ func TestPreparedSchemaDocumentsRetainExactNumbers(t *testing.T) {
|
||||
for _, source := range jsonSchemaValidatorSources() {
|
||||
t.Run(source.name, func(t *testing.T) {
|
||||
validator := source.new(t, []byte(schema))
|
||||
preparer, ok := validator.(ValidationPreparer)
|
||||
if !ok {
|
||||
t.Fatal("validator does not support validation preparation")
|
||||
}
|
||||
prepared, err := preparer.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
prepared, err := validator.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
@@ -874,14 +887,14 @@ func TestPreparedSchemaDocumentsRetainExactNumbers(t *testing.T) {
|
||||
|
||||
type jsonSchemaValidatorSource struct {
|
||||
name string
|
||||
new func(*testing.T, []byte) Validator
|
||||
new func(*testing.T, []byte) ValidationPreparer
|
||||
}
|
||||
|
||||
func jsonSchemaValidatorSources() []jsonSchemaValidatorSource {
|
||||
return []jsonSchemaValidatorSource{
|
||||
{
|
||||
name: "operating system files",
|
||||
new: func(t *testing.T, schema []byte) Validator {
|
||||
new: func(t *testing.T, schema []byte) ValidationPreparer {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(root, "schema.json"), schema, 0o644); err != nil {
|
||||
@@ -892,7 +905,7 @@ func jsonSchemaValidatorSources() []jsonSchemaValidatorSource {
|
||||
},
|
||||
{
|
||||
name: "fs.FS",
|
||||
new: func(t *testing.T, schema []byte) Validator {
|
||||
new: func(t *testing.T, schema []byte) ValidationPreparer {
|
||||
t.Helper()
|
||||
return NewFSValidator(fstest.MapFS{
|
||||
"schema.json": &fstest.MapFile{Data: schema},
|
||||
@@ -914,6 +927,7 @@ func BenchmarkJSONValidation(b *testing.B) {
|
||||
}
|
||||
validator := NewStandardValidator("")
|
||||
contract := domain.OutputContract{ValidationMode: domain.ValidationJSON}
|
||||
plan := prepareForTest(b, validator, contract)
|
||||
|
||||
for _, benchmark := range benchmarks {
|
||||
b.Run(benchmark.name, func(b *testing.B) {
|
||||
@@ -922,7 +936,7 @@ func BenchmarkJSONValidation(b *testing.B) {
|
||||
b.SetBytes(int64(len(benchmark.body)))
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
result, err := validator.Validate(context.Background(), artifact, contract)
|
||||
result, err := plan.Validate(context.Background(), artifact)
|
||||
if err != nil || !result.IsValid {
|
||||
b.Fatalf("validate JSON: result=%+v error=%v", result, err)
|
||||
}
|
||||
@@ -950,11 +964,12 @@ func BenchmarkBasicValidation(b *testing.B) {
|
||||
validator := NewStandardValidator("")
|
||||
contract := domain.OutputContract{ValidationMode: domain.ValidationBasic}
|
||||
ctx := context.Background()
|
||||
plan := prepareForTest(b, validator, contract)
|
||||
|
||||
for _, benchmark := range benchmarks {
|
||||
b.Run(benchmark.name, func(b *testing.B) {
|
||||
artifact := &domain.Artifact{Body: benchmark.body}
|
||||
result, err := validator.Validate(ctx, artifact, contract)
|
||||
result, err := plan.Validate(ctx, artifact)
|
||||
if err != nil || result.IsValid != benchmark.wantValid {
|
||||
b.Fatalf("validate basic output: result=%+v error=%v", result, err)
|
||||
}
|
||||
@@ -964,7 +979,7 @@ func BenchmarkBasicValidation(b *testing.B) {
|
||||
// scanning the body, so report latency rather than bytes per second.
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
result, err := validator.Validate(ctx, artifact, contract)
|
||||
result, err := plan.Validate(ctx, artifact)
|
||||
if err != nil || result.IsValid != benchmark.wantValid {
|
||||
b.Fatalf("validate basic output: result=%+v error=%v", result, err)
|
||||
}
|
||||
@@ -998,7 +1013,7 @@ func TestJSONSchemaDialectIsDraft2020(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schema.json": &fstest.MapFile{Data: data},
|
||||
}, ".")
|
||||
_, err = v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{}`)}, domain.OutputContract{
|
||||
_, err = v.PrepareValidation(context.Background(), domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "schema.json",
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user