@@ -82,8 +82,10 @@ public async Task ExternalDepthIsCheckedBeforeFetchingBeyondTheLimit(int length,
8282 Assert . Equal ( Math . Min ( length , limit ) , loader . Requests . Count ) ;
8383 if ( succeeds )
8484 {
85+ var document = result . Document ;
86+ Assert . NotNull ( document ) ;
8587 Assert . Empty ( result . Diagnostic . Errors ) ;
86- var reference = Assert . IsType < OpenApiSchemaReference > ( result . Document . Components . Schemas [ "Main" ] ) ;
88+ var reference = Assert . IsType < OpenApiSchemaReference > ( document . Components . Schemas [ "Main" ] ) ;
8789 Assert . Equal ( JsonSchemaType . Object , reference . RecursiveTarget . Type ) ;
8890 }
8991 else
@@ -279,6 +281,72 @@ public async Task AuthoredSchemaSiblingsArePreservedAndTheirReferencesAreLoaded(
279281 Assert . Equal ( JsonSchemaType . String , result . Document . Components . Schemas [ "Main" ] . Properties [ "child" ] . Type ) ;
280282 }
281283
284+ [ Theory ]
285+ [ InlineData ( "$defs" , false ) ]
286+ [ InlineData ( "$defs" , true ) ]
287+ [ InlineData ( "patternProperties" , true ) ]
288+ [ InlineData ( "contains" , true ) ]
289+ public async Task ExternalReferencesBeneathConcreteSchemaChildrenAreLoaded ( string keyword , bool hasReferenceSibling )
290+ {
291+ var loader = new RecordingLoader ( ) ;
292+ var childReference = new Dictionary < string , string > { [ "$ref" ] = "child.json#/components/schemas/A" } ;
293+ var wrapper = new Dictionary < string , object >
294+ {
295+ [ keyword ] = keyword == "contains" ? childReference :
296+ new Dictionary < string , object > { [ "Child" ] = childReference }
297+ } ;
298+ var schema = new Dictionary < string , object >
299+ {
300+ [ "type" ] = "object" ,
301+ [ "properties" ] = new Dictionary < string , object > { [ "wrapper" ] = wrapper }
302+ } ;
303+ if ( hasReferenceSibling )
304+ {
305+ schema [ "$ref" ] = "a.json#/components/schemas/A" ;
306+ }
307+ loader . Documents [ "http://example.test/a.json" ] =
308+ Document ( JsonSerializer . Serialize ( new Dictionary < string , object > { [ "A" ] = schema } ) , "3.1.0" ) ;
309+ loader . Documents [ "http://example.test/child.json" ] = Document ( """{"A":{"type":"string"}}""" , "3.1.0" ) ;
310+ var result = await Load ( Document ( """{"Main":{"$ref":"a.json#/components/schemas/A"}}""" , "3.1.0" ) , Settings ( loader ) ) ;
311+
312+ var document = result . Document ;
313+ Assert . NotNull ( document ) ;
314+ Assert . Empty ( result . Diagnostic . Errors ) ;
315+ Assert . Equal ( 2 , loader . Requests . Count ) ;
316+ var wrapperSchema = document . Components . Schemas [ "Main" ] . Properties [ "wrapper" ] ;
317+ var childSchema = keyword switch
318+ {
319+ "$defs" => wrapperSchema . Definitions [ "Child" ] ,
320+ "patternProperties" => wrapperSchema . PatternProperties [ "Child" ] ,
321+ _ => ( ( IOpenApiSchemaMissingProperties ) wrapperSchema ) . Contains
322+ } ;
323+ Assert . Equal ( JsonSchemaType . String , Assert . IsType < OpenApiSchemaReference > ( childSchema ) . RecursiveTarget . Type ) ;
324+ }
325+
326+ [ Theory ]
327+ [ InlineData ( "properties" ) ]
328+ [ InlineData ( "allOf" ) ]
329+ [ InlineData ( "allOf/-1" ) ]
330+ [ InlineData ( "anyOf/-1" ) ]
331+ [ InlineData ( "oneOf/-1" ) ]
332+ public async Task MalformedExternalSubschemaPointersReturnDiagnostics ( string pointer )
333+ {
334+ var loader = new RecordingLoader ( ) ;
335+ loader . Documents [ "http://example.test/a.json" ] = Document ( """
336+ {"A":{"type":"object","properties":{"name":{"type":"string"}},
337+ "allOf":[{"type":"object"}],"anyOf":[{"type":"object"}],"oneOf":[{"type":"object"}]}}
338+ """ , "3.1.0" ) ;
339+ var settings = Settings ( loader ) ;
340+ settings . RuleSet = new ValidationRuleSet ( ) ;
341+ var result = await Load ( Document ( $$$ """ {"Main":{"$ref":"a.json#/components/schemas/A/{{{ pointer }}} "}}""" , "3.1.0" ) , settings ) ;
342+
343+ Assert . Null ( result . Document ) ;
344+ Assert . Single ( loader . Requests ) ;
345+ Assert . Contains ( result . Diagnostic . Errors , error =>
346+ error . Message . Contains ( "Could not resolve external reference chain" , StringComparison . Ordinal ) &&
347+ error . Message . Contains ( pointer , StringComparison . Ordinal ) ) ;
348+ }
349+
282350 [ Fact ]
283351 public async Task WideGraphsDoNotConsumeTheDepthBudgetAcrossSiblings ( )
284352 {
0 commit comments