You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After #867 bumps kin-openapi to v0.136.0, resolveNumberRange in flatten/allof/merge_allof.go correctly preserves OpenAPI 3.0 exclusive-bound behavior but does not handle OpenAPI 3.1 numeric exclusiveMinimum / exclusiveMaximum values when merging allOf schemas.
In v0.136.0, Schema.ExclusiveMin and Schema.ExclusiveMax are now ExclusiveBound (a union of *bool for 3.0 style and *float64 for 3.1 style). The merge function still iterates only over the Min / Max slices and ignores the ExclusiveMin.Value / ExclusiveMax.Value axis.
Failure cases
Case 1: 3.1 spec with exclusiveMinimum only (no minimum)
Current behavior: collection.Min[0] and collection.Min[1] are both nil, so the merge loop body never runs. Output has Min == nil and ExclusiveMin == ExclusiveBound{}. Both exclusive minimums are silently lost. The merged schema would accept any number, even ones below 10.
Expected behavior: merged schema should have exclusiveMinimum: 10 (the more restrictive of the two).
Case 2: 3.1 spec mixing minimum and exclusiveMinimum
Current behavior: merge picks Min: 10 from schema 2 and copies its empty ExclusiveMin. The result happens to be correct (>= 10 is more restrictive than > 5), but only by coincidence; the comparison logic isn't actually consulting the exclusive bound values.
The mirror problem applies to ExclusiveMax.
Proposed fix
resolveNumberRange should compare across both axes:
Translate each schema's effective minimum to a single comparable value (treating exclusiveMinimum: x as x + ε).
Pick the maximum effective minimum across the candidate set.
Emit the result in 3.1 form (ExclusiveMin.Value) when the most-restrictive bound came from a numeric exclusive, or 3.0 form (Min + ExclusiveMin.Bool) otherwise.
Mirror logic for ExclusiveMax.
A more conservative approach: keep 3.0 logic unchanged, and add a separate 3.1-aware merge path triggered when any input has ExclusiveBound.Value set. Either approach is valid; the cleaner option is probably to unify them.
Test cases to add
allOf with two exclusiveMinimum values, both 3.1-style numeric. Expect the higher one in the result.
allOf mixing minimum: x and exclusiveMinimum: y where y > x. Expect exclusiveMinimum: y in the result.
allOf mixing minimum: x and exclusiveMinimum: y where x > y. Expect minimum: x in the result.
All three above mirrored for exclusiveMaximum and maximum.
Ensure existing 3.0-style tests (boolean exclusiveMinimum/exclusiveMaximum) still pass.
Context
After #867 bumps kin-openapi to v0.136.0,
resolveNumberRangeinflatten/allof/merge_allof.gocorrectly preserves OpenAPI 3.0 exclusive-bound behavior but does not handle OpenAPI 3.1 numericexclusiveMinimum/exclusiveMaximumvalues when merging allOf schemas.In v0.136.0,
Schema.ExclusiveMinandSchema.ExclusiveMaxare nowExclusiveBound(a union of*boolfor 3.0 style and*float64for 3.1 style). The merge function still iterates only over theMin/Maxslices and ignores theExclusiveMin.Value/ExclusiveMax.Valueaxis.Failure cases
Case 1: 3.1 spec with
exclusiveMinimumonly (nominimum)Current behavior:
collection.Min[0]andcollection.Min[1]are bothnil, so the merge loop body never runs. Output hasMin == nilandExclusiveMin == ExclusiveBound{}. Both exclusive minimums are silently lost. The merged schema would accept any number, even ones below 10.Expected behavior: merged schema should have
exclusiveMinimum: 10(the more restrictive of the two).Case 2: 3.1 spec mixing
minimumandexclusiveMinimumCurrent behavior: merge picks
Min: 10from schema 2 and copies its emptyExclusiveMin. The result happens to be correct (>= 10is more restrictive than> 5), but only by coincidence; the comparison logic isn't actually consulting the exclusive bound values.The mirror problem applies to
ExclusiveMax.Proposed fix
resolveNumberRangeshould compare across both axes:exclusiveMinimum: xasx + ε).ExclusiveMin.Value) when the most-restrictive bound came from a numeric exclusive, or 3.0 form (Min+ExclusiveMin.Bool) otherwise.Mirror logic for
ExclusiveMax.A more conservative approach: keep 3.0 logic unchanged, and add a separate 3.1-aware merge path triggered when any input has
ExclusiveBound.Valueset. Either approach is valid; the cleaner option is probably to unify them.Test cases to add
allOfwith twoexclusiveMinimumvalues, both 3.1-style numeric. Expect the higher one in the result.allOfmixingminimum: xandexclusiveMinimum: ywherey > x. ExpectexclusiveMinimum: yin the result.allOfmixingminimum: xandexclusiveMinimum: ywherex > y. Expectminimum: xin the result.exclusiveMaximumandmaximum.exclusiveMinimum/exclusiveMaximum) still pass.Related
flatten/allofso far. Other parts of oasdiff that readExclusiveMin/ExclusiveMaxshould be audited for similar 3.1 gaps.