Skip to content

Commit 39485c1

Browse files
committed
Address #247
1 parent 39db7ef commit 39485c1

6 files changed

Lines changed: 98 additions & 7 deletions

File tree

parameters/validate_parameter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func ValidateParameterSchema(
9696
var validationErrors []*errors.ValidationError
9797

9898
// 1. build a JSON render of the schema.
99-
renderCtx := base.NewInlineRenderContext()
99+
renderCtx := base.NewInlineRenderContextForValidation()
100100
renderedSchema, _ := schema.RenderInlineWithContext(renderCtx)
101101
jsonSchema, _ := utils.ConvertYAMLtoJSON(renderedSchema)
102102

@@ -238,7 +238,7 @@ func formatJsonSchemaValidationError(schema *base.Schema, scErrs *jsonschema.Val
238238
OriginalJsonSchemaError: scErrs,
239239
}
240240
if schema != nil {
241-
renderCtx := base.NewInlineRenderContext()
241+
renderCtx := base.NewInlineRenderContextForValidation()
242242
rendered, err := schema.RenderInlineWithContext(renderCtx)
243243
if err == nil && rendered != nil {
244244
renderedBytes, _ := json.Marshal(rendered)

requests/validate_request.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
7979

8080
// Cache miss or no cache - render and compile
8181
if compiledSchema == nil {
82-
renderCtx := base.NewInlineRenderContext()
82+
renderCtx := base.NewInlineRenderContextForValidation()
8383
var renderErr error
8484
renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx)
8585
referenceSchema = string(renderedSchema)

responses/validate_response.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func ValidateResponseSchema(input *ValidateResponseSchemaInput) (bool, []*errors
8181

8282
// Cache miss or no cache - render and compile
8383
if compiledSchema == nil {
84-
renderCtx := base.NewInlineRenderContext()
84+
renderCtx := base.NewInlineRenderContextForValidation()
8585
var renderErr error
8686
renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx)
8787
referenceSchema = string(renderedSchema)

strict/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ func NewValidator(options *config.ValidationOptions, version float32) *Validator
330330
logger: logger,
331331
localCache: make(map[string]*jsonschema.Schema),
332332
patternCache: make(map[string]*regexp.Regexp),
333-
renderCtx: base.NewInlineRenderContext(),
333+
renderCtx: base.NewInlineRenderContextForValidation(),
334334
version: version,
335335
}
336336

validator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ func warmMediaTypeSchema(mediaType *v3.MediaType, schemaCache cache.SchemaCache,
492492
if _, exists := schemaCache.Load(hash); !exists {
493493
schema := mediaType.Schema.Schema()
494494
if schema != nil {
495-
renderCtx := base.NewInlineRenderContext()
495+
renderCtx := base.NewInlineRenderContextForValidation()
496496
renderedInline, _ := schema.RenderInlineWithContext(renderCtx)
497497
referenceSchema := string(renderedInline)
498498
renderedJSON, _ := utils.ConvertYAMLtoJSON(renderedInline)
@@ -545,7 +545,7 @@ func warmParameterSchema(param *v3.Parameter, schemaCache cache.SchemaCache, opt
545545

546546
if schema != nil {
547547
if _, exists := schemaCache.Load(hash); !exists {
548-
renderCtx := base.NewInlineRenderContext()
548+
renderCtx := base.NewInlineRenderContextForValidation()
549549
renderedInline, _ := schema.RenderInlineWithContext(renderCtx)
550550
referenceSchema := string(renderedInline)
551551
renderedJSON, _ := utils.ConvertYAMLtoJSON(renderedInline)

validator_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,3 +2657,94 @@ paths:
26572657
assert.True(t, validB)
26582658
assert.Len(t, errsB, 0)
26592659
}
2660+
2661+
// TestValidateHttpResponse_DiscriminatorOneOf tests that response validation works correctly
2662+
// when the response schema uses oneOf with a discriminator and $ref to component schemas.
2663+
// This is a regression test for https://github.com/pb33f/libopenapi-validator/issues/247
2664+
// where the validator would fail with "json-pointer not found" because discriminator refs
2665+
// were preserved (Bundle mode) instead of fully inlined (Validation mode).
2666+
func TestValidateHttpResponse_DiscriminatorOneOf(t *testing.T) {
2667+
spec := `openapi: 3.1.0
2668+
info:
2669+
title: Discriminator OneOf Response Test
2670+
version: 1.0.0
2671+
paths:
2672+
/fields:
2673+
get:
2674+
responses:
2675+
'200':
2676+
description: OK
2677+
content:
2678+
application/json:
2679+
schema:
2680+
$ref: '#/components/schemas/Field'
2681+
components:
2682+
schemas:
2683+
BooleanField:
2684+
type: object
2685+
required:
2686+
- field_type
2687+
- default_value
2688+
properties:
2689+
field_type:
2690+
type: string
2691+
const: boolean
2692+
default_value:
2693+
type: boolean
2694+
StringField:
2695+
type: object
2696+
required:
2697+
- field_type
2698+
- max_length
2699+
properties:
2700+
field_type:
2701+
type: string
2702+
const: string
2703+
max_length:
2704+
type: integer
2705+
minimum: 1
2706+
Field:
2707+
oneOf:
2708+
- $ref: '#/components/schemas/BooleanField'
2709+
- $ref: '#/components/schemas/StringField'
2710+
discriminator:
2711+
propertyName: field_type`
2712+
2713+
doc, err := libopenapi.NewDocument([]byte(spec))
2714+
require.NoError(t, err)
2715+
2716+
v, errs := NewValidator(doc)
2717+
require.Empty(t, errs)
2718+
2719+
// Valid BooleanField response
2720+
body, _ := json.Marshal(map[string]interface{}{
2721+
"field_type": "boolean",
2722+
"default_value": true,
2723+
})
2724+
request, _ := http.NewRequest(http.MethodGet, "https://example.com/fields", nil)
2725+
response := &http.Response{
2726+
StatusCode: 200,
2727+
Header: http.Header{"Content-Type": []string{"application/json"}},
2728+
Body: io.NopCloser(bytes.NewBuffer(body)),
2729+
}
2730+
2731+
valid, validationErrors := v.ValidateHttpResponse(request, response)
2732+
assert.True(t, valid, "response validation should pass for valid discriminator oneOf payload")
2733+
assert.Empty(t, validationErrors, "no validation errors expected")
2734+
2735+
// Valid StringField response
2736+
body2, _ := json.Marshal(map[string]interface{}{
2737+
"field_type": "string",
2738+
"max_length": 255,
2739+
})
2740+
request2, _ := http.NewRequest(http.MethodGet, "https://example.com/fields", nil)
2741+
response2 := &http.Response{
2742+
StatusCode: 200,
2743+
Header: http.Header{"Content-Type": []string{"application/json"}},
2744+
Body: io.NopCloser(bytes.NewBuffer(body2)),
2745+
}
2746+
2747+
valid2, validationErrors2 := v.ValidateHttpResponse(request2, response2)
2748+
assert.True(t, valid2, "response validation should pass for valid StringField payload")
2749+
assert.Empty(t, validationErrors2, "no validation errors expected for StringField")
2750+
}

0 commit comments

Comments
 (0)