@@ -6666,3 +6666,214 @@ 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+ }
6872+
6873+ func TestStrictValidator_CheckReadWriteOnlyViolation_NilSchema (t * testing.T ) {
6874+ opts := config .NewValidationOptions (config .WithStrictMode (), config .WithStrictRejectReadOnly ())
6875+ v := NewValidator (opts , 3.1 )
6876+
6877+ _ , ok := v .checkReadWriteOnlyViolation ("$.body.x" , "x" , "val" , nil , DirectionRequest )
6878+ assert .False (t , ok )
6879+ }
0 commit comments