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
This commit was created on GitHub.com and signed with GitHub’s verified signature.
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.