Skip to content

Commit f5def80

Browse files
thcedclaude
andcommitted
test: Pin SchemaParser corner cases
Add five tests for parser semantics that previously survived only by code inspection: $ref ignores siblings, nested allOf is not auto- flattened past one level, const acts as a base assertion when siblings exist, not: {} wraps the permissive object, and combinator branches recurse through parse() so nested combinators stay intact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 29ba164 commit f5def80

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

src/test/java/com/retailsvc/http/spec/schema/SchemaParserTest.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)