Skip to content

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 23:07
· 39 commits to master since this release
0892a1e
  • Fixed. spaceDelimited and pipeDelimited query parameters generate
    again (#58). The serializer refuses a value carrying the style's own
    separator — the style has no escape for it — but nothing constrained the
    generated strings, and Gen::stringOf() draws from printable ASCII, which
    contains both a space and a |. Four materializations in five aborted with
    an error that cannot shrink and never reaches the corpus. The alphabet is
    narrowed at compile time instead, an unusable enum member is dropped and a
    const fails closed; the run-time filter now only guards a pattern, whose
    alphabet neither can see. Measured over 200 runs, the abort rate goes from
    163/200 and 144/200 to 0.

  • Fixed. allowReserved no longer changes what the wire says (#59). The
    reserved set is style-aware: a character the chosen style uses as a separator
    stays percent-encoded, so a form list carrying a comma is no longer read back
    as two items. Across every style, explode and value shape — 212 combinations
    serialized here and parsed by openapi-contract — the round trip went from
    12 broken to none. The flag is also applied where the specification defines
    it, in: query, rather than everywhere but the path.

  • Fixed. A parameter whose type is an OAS 3.1 union containing null
    generates (#62). A parameter travels as text and has no representation for
    the null branch, which is why 3.0 nullable is dropped; the 3.1 spelling was
    not, so a free-form type: ["object", "null"] reached the generator and the
    wire conversion then rejected the map it produced. Internal\SchemaShape
    reads a type union as membership rather than identity, which is what it
    exists to decide in one place.

  • Changed. Requires rasuvaeff/openapi-contract ^0.6, which fixes the
    contract-side halves of this wave — among them OAS 3.0 documents using a
    boolean additionalProperties, and properties that were silently not
    validated at all.

  • Internal. The zoo gains delimited.get, reserved.get and unions.get,
    and ZooContracts::suite() derives its selection from VALID_OPERATIONS
    instead of repeating it — an operation added to one list and not the other
    was selected nowhere, and the property that exercises it aborted before it
    could say why.

  • Fixed. A required multipart array property is generated non-empty (#60).
    An empty array became zero parts, and a multipart entity with no parts is not
    one — RFC 2046 §5.1.1 requires at least one, and openapi-contract rejects
    the payload with request.body.decode, as does
    league/openapi-psr7-validator. About 9% of runs therefore broke the
    invariant this package states about itself: a materialized valid case must
    pass Contract::validateRequest(). Form bodies already forced a required
    container non-empty; multipart does now too. Measured over 80 runs, empty
    bodies go from 7 to 0.

  • Fixed. Every supported request body media type is generated, not only the
    first one recognised (#66). A body declaring application/json beside
    application/x-www-form-urlencoded was always exercised through one of them,
    so the other was declared and never sent.

  • Internal. The zoo gains uploads.create and dual.create, and the
    end-to-end property gates on both media types of the dual body actually being
    chosen.

  • Fixed. A property whose name is numeric is generated, materialized and
    serialized (#61). PHP normalizes the array key "2020" to an integer, and
    every place that read a member name checked is_string(): the compiler
    dropped such a property from the generated object, and the JSON and form
    encoders refused the case outright — with a message saying properties must
    be an object, which it was. The name is cast back where it goes on the wire,
    which is the only place PHP can carry it as a string.

  • Fixed. array_merge($result, [$key => $value]), the idiom used to quiet
    psalm's MixedAssignment, is replaced by array_replace (#65).
    array_merge renumbers integer-like keys, so the very names the fix above
    restores were the ones the idiom lost: ['5' => 'a', '3' => 'b'] came out as
    [0 => 'a', 1 => 'b']. Where the value's type can be stated instead, it is —
    a precise return type removes the warning at its source, which is what
    openapi-contract did for the same idiom.

  • Fixed. Internal\DirectionalSchemas drops the last property together
    with properties itself, as openapi-contract's effective schema does, and
    its docblock now describes what it does rather than what the contract used to
    (#67). An empty properties forbids nothing, so the two readings agreed in
    effect — but the sentence claiming they were identical was not true, and a
    reader following it drew the wrong conclusion.

  • Fixed. RedactionPolicy::bodyPaths applies to form and multipart bodies,
    not only JSON (#63). It was a silent no-op for two of the three encodings, in
    a feature whose only job is to keep a secret out of the reproducer: a
    password field of a form body was rendered in full in the curl command.

  • Fixed. A redacted parameter keeps its shape (#64). The marker replaced
    the whole value with a string, so deepObject — which requires an object —
    and the delimited styles — which require a list — could no longer be
    serialized: reproduce() threw, and the caller printed
    (no reproducer: …). Protecting a secret removed the reproducer for exactly
    the failure it was needed on. A string is redacted to a string, a list to a
    single-element list, an object to an object with redacted values.

  • Fixed. A query credential that an exploded object query parameter would
    swallow fails closed instead of silently changing the request (#68). The
    parameter's style claims every pair a sibling parameter does not, so the
    credential became one of its members: the operation was exercised with a
    value other than the case recorded, and where the object is constrained the
    case this package had called valid came back reported as invalid. OpenAPI
    leaves that ambiguity to the style, so there is nothing to resolve — the
    honest answer is to name both and stop, rather than send a request that means
    something else.

  • Changed. An anonymous security alternative — the empty requirement object
    OpenAPI uses for "authentication is optional" — is a fallback rather than the
    first choice (#69). Taken as soon as it was seen, and it usually is listed
    first, it meant the suite never exercised the authenticated path of such an
    operation at all. An alternative the provider can satisfy is preferred; the
    anonymous one answers only when none can.

  • Internal. The zoo's end-to-end property runs 900 cases rather than 450.
    Its Classify::cover gates are absolute fractions of a mixed population —
    each names a condition only one zoo operation can satisfy — so every
    operation added to the zoo dilutes all of them. At 450 over 16 operations one
    gate landed a tenth of a point under its threshold purely on the draw.

Known limit. An object whose member names run 0, 1, … without a gap cannot
be told from a list in a JSON-compatible PHP value, and a list is exactly what
a negative case sends when it means to violate an object schema. That shape
stays encoded as an array; it is pinned by a test so the limit is a decision
rather than a surprise.