Skip to content

False breaking change when direct array type changes to allOf-wrapped array #920

Description

@DrSatyr

Follow-up to #887 / #916.

#916 fixed the ClassCastException for allOf-wrapped array → direct type: array. The opposite direction does not throw, but reports a breaking change between two equivalent specs.

Reproduction

Using the fixtures added in #916, with the arguments swapped:

OpenApiCompare.fromLocations("issue-887-2.yaml", "issue-887-1.yaml");

Output:

* Changed property `valuations` (array -> array)

#### Result
API changes broke backward compatibility

Left side:

valuations:
  type: array
  items:
    $ref: '#/components/schemas/Valuation'

Right side:

valuations:
  allOf:
    - $ref: '#/components/schemas/Valuations'   # Valuations is `type: array`

Both describe the same array, so the expected result is no change / compatible — which is what the forward direction already returns after #916.

Cause

SchemaDiff.computeDiffForReal selects the diff-result implementation from the right-hand schema only:

SchemaDiffResult result = SchemaDiff.getSchemaDiffResult(right.getClass(), openApiDiff);

resolveComposedSchema merges allOf members into the parent but leaves the instance as a ComposedSchema, so with the allOf spec on the right, ComposedSchemaDiffResult is selected. Its else branch fires because left is an ArraySchema rather than a ComposedSchema:

} else {
  return openApiDiff.getSchemaDiff().getTypeChangedSchema(left, right, context);
}

That reports a type change even though left.getType() and right.getType() are both arraycomputeDiffForReal already verified they match before dispatching.

Suggested fix

In ComposedSchemaDiffResult.diff, when left is not a ComposedSchema, fall through to super.diff(...) instead of declaring a type change — the type/format equality check upstream has already passed at that point. The oneOf/discriminator handling stays behind the existing instanceof guard.

More generally, the dispatch being driven solely by right.getClass() makes composed-vs-plain comparisons asymmetric; worth considering whether the result class should be chosen from both sides.

Note

Issue887Test.testDirectArrayToAllOfArrayDoesNotThrow covers this direction but asserts only that no exception is thrown, so it passes against the current wrong output. It should become assertOpenApiAreEquals(DIRECT_ARRAY, ALLOF_ARRAY) once this is fixed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions