@@ -193,6 +193,81 @@ export function resolveMockValue({
193193 : { nullable : schemaReference . nullable } ) ,
194194 } as MockSchemaObject ;
195195
196+ // When a discriminator parent ($ref-loaded schema with both `discriminator`
197+ // and `oneOf`) is being expanded inside an `allOf` chain AND the chain
198+ // is rooted at one of that parent's mapping targets (i.e. the current
199+ // schema *is* a variant via `allOf: [parent, ...extras]`), the parent's
200+ // `oneOf` is descriptive of the union, not additive to this specific
201+ // variant. Re-expanding it inlines sibling factory calls into the derived
202+ // variant's mock body (#2155). Drop the `oneOf` side here; the parent
203+ // still contributes its own `properties` and other base attributes
204+ // through the remaining schema fields.
205+ //
206+ // The mapping-target check guards against cases like
207+ // `someField: allOf: [<discriminator parent>]` (e.g. #one-of-nested
208+ // `Example2.expiry`), where the surrounding schema is NOT a variant of
209+ // the parent and we still need the full union to randomize over.
210+ //
211+ // Symmetrically with the oneOf-side fix in `combineSchemasMock` (#3429),
212+ // also drop the discriminator key from the parent's `properties`: each
213+ // variant already carries a constrained discriminator value via
214+ // `resolveDiscriminators`, so leaving the parent's free-choice enum in
215+ // would just emit dead code (immediately shadowed by the variant's
216+ // constrained value through spread merge).
217+ if (
218+ combine ?. separator === 'allOf' &&
219+ newSchema . discriminator &&
220+ newSchema . oneOf
221+ ) {
222+ const parentDiscriminator = newSchema . discriminator as {
223+ propertyName ?: string ;
224+ mapping ?: Record < string , string > ;
225+ } ;
226+ const mappingTargetNames = parentDiscriminator . mapping
227+ ? Object . values ( parentDiscriminator . mapping ) . map ( ( ref ) =>
228+ pascal ( ref . split ( '/' ) . pop ( ) ?? '' ) ,
229+ )
230+ : [ ] ;
231+ const expandingAsVariant = existingReferencedProperties . some ( ( refName ) =>
232+ mappingTargetNames . includes ( refName ) ,
233+ ) ;
234+
235+ if ( expandingAsVariant ) {
236+ const mutableSchema = newSchema as Record < string , unknown > ;
237+ delete mutableSchema . oneOf ;
238+ const parentProperties = newSchema . properties as
239+ | Record < string , unknown >
240+ | undefined ;
241+ if (
242+ parentDiscriminator . propertyName &&
243+ parentProperties &&
244+ parentDiscriminator . propertyName in parentProperties
245+ ) {
246+ const remainingProperties = Object . fromEntries (
247+ Object . entries ( parentProperties ) . filter (
248+ ( [ key ] ) => key !== parentDiscriminator . propertyName ,
249+ ) ,
250+ ) ;
251+ if ( Object . keys ( remainingProperties ) . length === 0 ) {
252+ delete mutableSchema . properties ;
253+ } else {
254+ mutableSchema . properties = remainingProperties ;
255+ }
256+ const parentRequired = newSchema . required as string [ ] | undefined ;
257+ if ( Array . isArray ( parentRequired ) ) {
258+ const filteredRequired = parentRequired . filter (
259+ ( key ) => key !== parentDiscriminator . propertyName ,
260+ ) ;
261+ if ( filteredRequired . length === 0 ) {
262+ delete mutableSchema . required ;
263+ } else {
264+ mutableSchema . required = filteredRequired ;
265+ }
266+ }
267+ }
268+ }
269+ }
270+
196271 const newSeparator = newSchema . allOf
197272 ? 'allOf'
198273 : newSchema . oneOf
0 commit comments