Skip to content

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 12 Sep 15:34
· 16 commits to master since this release
bdd85fe
  • Added. ContractSuite::negativeCoverage() answers, per selected
    operation, which misuses the negative phase can reach and why it reaches
    nothing where it cannot — computed from the document, without drawing. A
    category was either constructible or silently absent, and nothing told the
    two apart; a green suite looks identical whether a category is generating
    and the application is correctly rejecting it, or the category was never
    constructed. Sampling negativeCases() recovers part of the answer but
    cannot prove a negative and cannot say why one is missing, which the
    skipped reason does.
  • Changed. A value category's witness is verified rather than asserted. A
    candidate is kept only when the property's schema rejects it and the same
    schema without the category's keywords accepts it, checked through
    Contract::accepts() — the rules that will judge the generated request, not
    a second copy of them. The three hand-maintained lists of mutually-excluded
    keywords are gone with it, and so is their inconsistency: they had different
    contents in each probe and none at all on the boundary probe, and relaxing
    one meant reasoning about keyword interactions by hand.
  • Changed. Every negative category draws its target among all the eligible
    ones instead of always taking the first. Selection used to be deterministic
    and position-based: an operation declaring per_page and page, both
    bounded, had all of its boundary cases land on per_page, and swapping
    the two entries in the document swapped which bound was ever checked —
    drawing more did not help, because it re-drew the same target. The count
    stayed healthy, so only a per-name breakdown revealed that one parameter
    absorbed the whole category. The same applied to missing-required across
    required components and to every body value category across a body's
    properties. Targets are listed in declaration order and drawn with
    Gen::elements(), so shrinking converges on the first eligible one and the
    minimal counterexample is what the first-match search used to return.
  • Changed. A format refusal names its cause. The probe answered every
    refusal with a bare null, which reads the same whether the schema declares
    no format at all — nothing to do there — or declares one this package holds
    no witness for. Those call for opposite responses, and only the second is a
    gap here. The refusal now names the formats no witness is held for, and the
    seven that are supported are documented rather than discoverable only by
    reading private const.
  • Changed. rasuvaeff/openapi-contract is required at ^0.11, for
    Contract::accepts() and Operation::$dialect.
  • Fixed. A format declared beside a minLength/maxLength bound yields
    a format case whenever the fixed witness fits the window. format: email
    with maxLength: 255, the most common string shape in a real document, used
    to get neither a format case nor a length one, because each probe
    refused on the other's keyword; 'not-an-email' is twelve characters and
    provably violates only the format.
  • Fixed. enum and const offer typed alternatives, so an enum of
    integers or a const beside a maxLength keeps its category. The single
    marker string those categories used to write contradicts the type as well as
    the enum, and is longer than a short string bound — which the verification
    now catches, and which the alternatives answer.
  • Fixed. An over-long array witness is built from values the items
    schema admits, drawn from the item schema when no fixed filler fits. A list
    of nulls contradicts typed items as well as maxItems, so what the
    length category produced for an array body was never the pure length
    mismatch it recorded.
  • Fixed. A JSON body property whose name is a decimal integer — "12" —
    is a witness candidate on both sides. PHP stores such a name as an int
    array key, and JsonBodyWitness::candidates() kept only string keys, so the
    seven request body value categories and the response body categories never
    contradicted it and an operation whose only constrained property is numeric
    had no constructible body value category at all. The response side now
    writes the witness with array_replace() for the same reason:
    array_merge() renumbers integer keys, so the write landed under a fresh
    index and left the declared property untouched.