Skip to content

Commit c9e4272

Browse files
committed
Add coverage tests for readOnly/writeOnly rejection paths
Cover checkReadWriteOnlyViolation calls in: - recurseIntoDeclaredProperties (explicit + pattern properties) - validateVariantWithParent (oneOf) - recurseIntoDeclaredPropertiesWithMerged (oneOf + additionalProperties: false) - recurseIntoAllOfDeclaredProperties (allOf + additionalProperties: false) - WithStrictRejectReadOnly/WithStrictRejectWriteOnly option functions - WithExistingOpts copy of new fields
1 parent 13e2498 commit c9e4272

3 files changed

Lines changed: 219 additions & 2 deletions

File tree

config/config_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ func TestWithLogger(t *testing.T) {
466466
func TestWithExistingOpts_StrictFields(t *testing.T) {
467467
original := &ValidationOptions{
468468
StrictMode: true,
469+
StrictRejectReadOnly: true,
470+
StrictRejectWriteOnly: true,
469471
StrictIgnorePaths: []string{"$.body.*"},
470472
StrictIgnoredHeaders: []string{"x-custom"},
471473
strictIgnoredHeadersMerge: true,
@@ -475,12 +477,24 @@ func TestWithExistingOpts_StrictFields(t *testing.T) {
475477
opts := NewValidationOptions(WithExistingOpts(original))
476478

477479
assert.True(t, opts.StrictMode)
480+
assert.True(t, opts.StrictRejectReadOnly)
481+
assert.True(t, opts.StrictRejectWriteOnly)
478482
assert.Equal(t, original.StrictIgnorePaths, opts.StrictIgnorePaths)
479483
assert.Equal(t, original.StrictIgnoredHeaders, opts.StrictIgnoredHeaders)
480484
assert.True(t, opts.strictIgnoredHeadersMerge)
481485
assert.Equal(t, original.Logger, opts.Logger)
482486
}
483487

488+
func TestWithStrictRejectReadOnly(t *testing.T) {
489+
opts := NewValidationOptions(WithStrictRejectReadOnly())
490+
assert.True(t, opts.StrictRejectReadOnly)
491+
}
492+
493+
func TestWithStrictRejectWriteOnly(t *testing.T) {
494+
opts := NewValidationOptions(WithStrictRejectWriteOnly())
495+
assert.True(t, opts.StrictRejectWriteOnly)
496+
}
497+
484498
func TestStrictModeWithIgnorePaths(t *testing.T) {
485499
paths := []string{"$.body.metadata.*"}
486500
opts := NewValidationOptions(

strict/validator_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6666,3 +6666,206 @@ components:
66666666
assert.Equal(t, "password", resultResponse.UndeclaredValues[0].Name)
66676667
assert.Equal(t, TypeWriteOnlyProperty, resultResponse.UndeclaredValues[0].Type)
66686668
}
6669+
6670+
func TestStrictValidator_RejectReadOnly_AdditionalPropertiesFalse(t *testing.T) {
6671+
// Covers schema_walker.go recurseIntoDeclaredProperties:
6672+
// explicit property path and patternProperties path
6673+
yml := `openapi: "3.1.0"
6674+
info:
6675+
title: Test
6676+
version: "1.0"
6677+
paths: {}
6678+
components:
6679+
schemas:
6680+
Strict:
6681+
type: object
6682+
additionalProperties: false
6683+
properties:
6684+
id:
6685+
type: string
6686+
readOnly: true
6687+
name:
6688+
type: string
6689+
patternProperties:
6690+
"^x-":
6691+
type: string
6692+
readOnly: true
6693+
`
6694+
model := buildSchemaFromYAML(t, yml)
6695+
schema := getSchema(t, model, "Strict")
6696+
6697+
opts := config.NewValidationOptions(config.WithStrictMode(), config.WithStrictRejectReadOnly())
6698+
v := NewValidator(opts, 3.1)
6699+
6700+
data := map[string]any{
6701+
"id": "user-1",
6702+
"name": "John",
6703+
"x-custom": "value",
6704+
}
6705+
6706+
result := v.Validate(Input{
6707+
Schema: schema,
6708+
Data: data,
6709+
Direction: DirectionRequest,
6710+
Options: opts,
6711+
BasePath: "$.body",
6712+
Version: 3.1,
6713+
})
6714+
6715+
assert.False(t, result.Valid)
6716+
assert.Len(t, result.UndeclaredValues, 2)
6717+
6718+
names := []string{result.UndeclaredValues[0].Name, result.UndeclaredValues[1].Name}
6719+
assert.Contains(t, names, "id")
6720+
assert.Contains(t, names, "x-custom")
6721+
for _, uv := range result.UndeclaredValues {
6722+
assert.Equal(t, TypeReadOnlyProperty, uv.Type)
6723+
}
6724+
}
6725+
6726+
func TestStrictValidator_RejectReadOnly_OneOf(t *testing.T) {
6727+
// Covers polymorphic.go validateVariantWithParent path
6728+
yml := `openapi: "3.1.0"
6729+
info:
6730+
title: Test
6731+
version: "1.0"
6732+
paths: {}
6733+
components:
6734+
schemas:
6735+
OneOfSchema:
6736+
type: object
6737+
oneOf:
6738+
- type: object
6739+
properties:
6740+
id:
6741+
type: string
6742+
readOnly: true
6743+
email:
6744+
type: string
6745+
`
6746+
model := buildSchemaFromYAML(t, yml)
6747+
schema := getSchema(t, model, "OneOfSchema")
6748+
6749+
opts := config.NewValidationOptions(config.WithStrictMode(), config.WithStrictRejectReadOnly())
6750+
v := NewValidator(opts, 3.1)
6751+
6752+
data := map[string]any{
6753+
"id": "user-1",
6754+
"email": "test@example.com",
6755+
}
6756+
6757+
result := v.Validate(Input{
6758+
Schema: schema,
6759+
Data: data,
6760+
Direction: DirectionRequest,
6761+
Options: opts,
6762+
BasePath: "$.body",
6763+
Version: 3.1,
6764+
})
6765+
6766+
assert.False(t, result.Valid)
6767+
require.Len(t, result.UndeclaredValues, 1)
6768+
assert.Equal(t, "id", result.UndeclaredValues[0].Name)
6769+
assert.Equal(t, TypeReadOnlyProperty, result.UndeclaredValues[0].Type)
6770+
}
6771+
6772+
func TestStrictValidator_RejectReadOnly_OneOfAdditionalPropertiesFalse(t *testing.T) {
6773+
// Covers polymorphic.go recurseIntoDeclaredPropertiesWithMerged path
6774+
yml := `openapi: "3.1.0"
6775+
info:
6776+
title: Test
6777+
version: "1.0"
6778+
paths: {}
6779+
components:
6780+
schemas:
6781+
OneOfStrict:
6782+
type: object
6783+
additionalProperties: false
6784+
properties:
6785+
name:
6786+
type: string
6787+
oneOf:
6788+
- type: object
6789+
additionalProperties: false
6790+
properties:
6791+
name:
6792+
type: string
6793+
id:
6794+
type: string
6795+
readOnly: true
6796+
data:
6797+
type: string
6798+
`
6799+
model := buildSchemaFromYAML(t, yml)
6800+
schema := getSchema(t, model, "OneOfStrict")
6801+
6802+
opts := config.NewValidationOptions(config.WithStrictMode(), config.WithStrictRejectReadOnly())
6803+
v := NewValidator(opts, 3.1)
6804+
6805+
data := map[string]any{
6806+
"name": "test",
6807+
"id": "should-be-rejected",
6808+
"data": "valid",
6809+
}
6810+
6811+
result := v.Validate(Input{
6812+
Schema: schema,
6813+
Data: data,
6814+
Direction: DirectionRequest,
6815+
Options: opts,
6816+
BasePath: "$.body",
6817+
Version: 3.1,
6818+
})
6819+
6820+
assert.False(t, result.Valid)
6821+
require.Len(t, result.UndeclaredValues, 1)
6822+
assert.Equal(t, "id", result.UndeclaredValues[0].Name)
6823+
assert.Equal(t, TypeReadOnlyProperty, result.UndeclaredValues[0].Type)
6824+
}
6825+
6826+
func TestStrictValidator_RejectReadOnly_AllOfAdditionalPropertiesFalse(t *testing.T) {
6827+
// Covers polymorphic.go recurseIntoAllOfDeclaredProperties path
6828+
yml := `openapi: "3.1.0"
6829+
info:
6830+
title: Test
6831+
version: "1.0"
6832+
paths: {}
6833+
components:
6834+
schemas:
6835+
AllOfStrict:
6836+
type: object
6837+
additionalProperties: false
6838+
allOf:
6839+
- type: object
6840+
properties:
6841+
id:
6842+
type: string
6843+
readOnly: true
6844+
name:
6845+
type: string
6846+
`
6847+
model := buildSchemaFromYAML(t, yml)
6848+
schema := getSchema(t, model, "AllOfStrict")
6849+
6850+
opts := config.NewValidationOptions(config.WithStrictMode(), config.WithStrictRejectReadOnly())
6851+
v := NewValidator(opts, 3.1)
6852+
6853+
data := map[string]any{
6854+
"id": "should-be-rejected",
6855+
"name": "John",
6856+
}
6857+
6858+
result := v.Validate(Input{
6859+
Schema: schema,
6860+
Data: data,
6861+
Direction: DirectionRequest,
6862+
Options: opts,
6863+
BasePath: "$.body",
6864+
Version: 3.1,
6865+
})
6866+
6867+
assert.False(t, result.Valid)
6868+
require.Len(t, result.UndeclaredValues, 1)
6869+
assert.Equal(t, "id", result.UndeclaredValues[0].Name)
6870+
assert.Equal(t, TypeReadOnlyProperty, result.UndeclaredValues[0].Type)
6871+
}

validator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,7 +2784,7 @@ components:
27842784
})
27852785
}
27862786

2787-
func TestStrictRejectReadOnly_RequestIntegration(t *testing.T) {
2787+
func TestStrictMode_RejectReadOnly_RequestIntegration(t *testing.T) {
27882788
spec := `openapi: 3.1.0
27892789
paths:
27902790
/users:
@@ -2834,7 +2834,7 @@ paths:
28342834
assert.True(t, foundReadOnly, "should report readOnly violation")
28352835
}
28362836

2837-
func TestStrictRejectWriteOnly_ResponseIntegration(t *testing.T) {
2837+
func TestStrictMode_RejectWriteOnly_ResponseIntegration(t *testing.T) {
28382838
spec := `openapi: 3.1.0
28392839
paths:
28402840
/users/{id}:

0 commit comments

Comments
 (0)