@@ -1544,4 +1544,62 @@ public void oneOf_issue_23276() {
15441544 assertNotNull (payload .getOneOf ());
15451545 }
15461546
1547+ @ Test
1548+ public void ensureInheritanceForDiscriminatorMapping_prependsParentRef_childDefaultWins () {
1549+ // When a child already has allOf items (e.g. a base schema carrying "base_default"),
1550+ // the normalizer must INSERT the parent $ref at position 0 — not append at the end.
1551+ // With last-writer-wins semantics in resolveDefault(), appending at the end would make
1552+ // the parent's default the winner, incorrectly overriding the child's own default.
1553+ OpenAPI openAPI = TestUtils .createOpenAPI ();
1554+
1555+ Schema <?> parent = new StringSchema ();
1556+ parent .setDefault ("parent_default" );
1557+
1558+ Schema <?> base = new StringSchema ();
1559+ base .setDefault ("base_default" );
1560+ openAPI .getComponents ().addSchemas ("Base" , base );
1561+
1562+ // Child: allOf → [Base]; no Parent ref yet — use mutable list (parser always produces ArrayList)
1563+ Schema <?> child = new Schema <>().allOf (new ArrayList <>(List .of (new Schema <>().$ref ("#/components/schemas/Base" ))));
1564+ openAPI .getComponents ().addSchemas ("Child" , child );
1565+ openAPI .getComponents ().addSchemas ("Parent" , parent );
1566+
1567+ OpenAPINormalizer normalizer = new OpenAPINormalizer (openAPI , Map .of ());
1568+ normalizer .ensureInheritanceForDiscriminatorMapping (parent , child , "Parent" , new HashSet <>());
1569+
1570+ // Parent ref must be prepended at index 0; Base ref stays at index 1
1571+ assertEquals (((Schema <?>) child .getAllOf ().get (0 )).get$ref (), "#/components/schemas/Parent" );
1572+ assertEquals (((Schema <?>) child .getAllOf ().get (1 )).get$ref (), "#/components/schemas/Base" );
1573+
1574+ // resolveDefault walks top-to-bottom, last non-null wins → "base_default"
1575+ assertEquals (ModelUtils .resolveDefault (openAPI , child ), "base_default" );
1576+ }
1577+
1578+ @ Test
1579+ public void ensureInheritanceForDiscriminatorMapping_noExistingAllOf_noProperties_directDefaultPreserved () {
1580+ // When a child has no allOf and no properties but does have a direct default,
1581+ // the normalizer creates allOf with just the parent $ref. The child's direct default
1582+ // must NOT be cleared — resolveDefault() short-circuits on it before inspecting allOf.
1583+ OpenAPI openAPI = TestUtils .createOpenAPI ();
1584+
1585+ Schema <?> parent = new StringSchema ();
1586+ parent .setDefault ("parent_default" );
1587+ openAPI .getComponents ().addSchemas ("Parent" , parent );
1588+
1589+ // Child: direct default only, no allOf, no properties
1590+ Schema <?> child = new StringSchema ();
1591+ child .setDefault ("child_direct_default" );
1592+ openAPI .getComponents ().addSchemas ("Child" , child );
1593+
1594+ OpenAPINormalizer normalizer = new OpenAPINormalizer (openAPI , Map .of ());
1595+ normalizer .ensureInheritanceForDiscriminatorMapping (parent , child , "Parent" , new HashSet <>());
1596+
1597+ // Child now has allOf (parent ref only), and still holds its direct default
1598+ assertNotNull (child .getAllOf ());
1599+ assertEquals (child .getDefault (), "child_direct_default" );
1600+
1601+ // resolveDefault short-circuits on the direct default — child wins
1602+ assertEquals (ModelUtils .resolveDefault (openAPI , child ), "child_direct_default" );
1603+ }
1604+
15471605}
0 commit comments