@@ -288,4 +288,81 @@ void aloneCombinatorStillReturnsCombinatorRecord() {
288288 assertThat (s ).isInstanceOf (OneOfSchema .class );
289289 assertThat (((OneOfSchema ) s ).options ()).hasSize (2 );
290290 }
291+
292+ @ Test
293+ void refWithSiblingsIsParsedSolo () {
294+ // Deliberate limitation: $ref returns immediately and ignores sibling keywords.
295+ // JSON Schema 2020-12 allows $ref + siblings but that interaction is a separate gap.
296+ Schema s =
297+ SchemaParser .parse (
298+ Map .of (
299+ "$ref" , "#/components/schemas/Foo" ,
300+ "type" , "string" ,
301+ "minLength" , 5 ));
302+ assertThat (s ).isInstanceOf (RefSchema .class );
303+ assertThat (((RefSchema ) s ).pointer ()).isEqualTo ("#/components/schemas/Foo" );
304+ }
305+
306+ @ Test
307+ void nestedAllOfIsNotFlattenedWhenBasePresent () {
308+ // Flattening is one-level: when a base assertion plus an outer allOf coexist,
309+ // the outer allOf branches are pulled into the assertions list, but any
310+ // allOf nested inside one of those branches stays wrapped.
311+ Schema s =
312+ SchemaParser .parse (
313+ Map .of (
314+ "type" ,
315+ "object" ,
316+ "allOf" ,
317+ List .of (
318+ Map .of (
319+ "allOf" , List .of (Map .of ("type" , "string" ), Map .of ("type" , "integer" ))))));
320+ assertThat (s ).isInstanceOf (AllOfSchema .class );
321+ AllOfSchema all = (AllOfSchema ) s ;
322+ assertThat (all .parts ()).hasSize (2 );
323+ assertThat (all .parts ().get (0 )).isInstanceOf (ObjectSchema .class );
324+ assertThat (all .parts ().get (1 )).isInstanceOf (AllOfSchema .class );
325+ assertThat (((AllOfSchema ) all .parts ().get (1 )).parts ()).hasSize (2 );
326+ }
327+
328+ @ Test
329+ void constWithSiblingAllOfWrapsInImplicitAllOf () {
330+ // const acts as a base assertion, so a sibling combinator wraps both in AllOf.
331+ Schema s =
332+ SchemaParser .parse (
333+ Map .of ("const" , 5 , "allOf" , List .of (Map .of ("type" , "integer" , "minimum" , 0 ))));
334+ assertThat (s ).isInstanceOf (AllOfSchema .class );
335+ AllOfSchema all = (AllOfSchema ) s ;
336+ assertThat (all .parts ()).hasSize (2 );
337+ assertThat (all .parts ().get (0 )).isInstanceOf (ConstSchema .class );
338+ assertThat (((ConstSchema ) all .parts ().get (0 )).value ()).isEqualTo (5 );
339+ assertThat (all .parts ().get (1 )).isInstanceOf (IntegerSchema .class );
340+ }
341+
342+ @ Test
343+ void notWithEmptyInnerSchemaWrapsPermissiveObject () {
344+ // not: {} parses the inner empty schema as the permissive ObjectSchema.
345+ Schema s = SchemaParser .parse (Map .of ("not" , Map .of ()));
346+ assertThat (s ).isInstanceOf (NotSchema .class );
347+ assertThat (((NotSchema ) s ).schema ()).isInstanceOf (ObjectSchema .class );
348+ }
349+
350+ @ Test
351+ void oneOfContainingNestedAnyOfRecurses () {
352+ // Pins that combinator branches are themselves passed through parse(),
353+ // so nested combinators survive intact.
354+ Schema s =
355+ SchemaParser .parse (
356+ Map .of (
357+ "oneOf" ,
358+ List .of (
359+ Map .of ("anyOf" , List .of (Map .of ("type" , "string" ), Map .of ("type" , "integer" ))),
360+ Map .of ("type" , "boolean" ))));
361+ assertThat (s ).isInstanceOf (OneOfSchema .class );
362+ OneOfSchema one = (OneOfSchema ) s ;
363+ assertThat (one .options ()).hasSize (2 );
364+ assertThat (one .options ().get (0 )).isInstanceOf (AnyOfSchema .class );
365+ assertThat (((AnyOfSchema ) one .options ().get (0 )).options ()).hasSize (2 );
366+ assertThat (one .options ().get (1 )).isInstanceOf (BooleanSchema .class );
367+ }
291368}
0 commit comments