Skip to content

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 05 Sep 07:54
· 32 commits to master since this release
0ef11c9
  • Added. NegativeRequestCaseArbitrary::partContentTypeMismatchForOperation()
    (#80): the valid multipart body with one required part sent under a media
    type its encoding.contentType does not allow. It is the only category that
    can see a validator reading that keyword and ignoring it — neglecting it is
    fail-open, so every valid case passes either way, and the recorded corpus in
    openapi-contract replayed green against a validator that ignored it. A part
    without a declared contentType, an optional part, and a part declaring a
    wildcard are skipped for the next declared part, and an operation left with
    no candidate fails closed — as does a body that can travel as more than one
    media type, since which one a valid case carries is a generation choice — so
    the case never claims a contradiction it does not carry.

  • Changed. A header parameter is written as it will be read: verbatim, not
    percent-encoded, on both the request and the response side
    (openapi-contract#66). Encoding one put a string on the wire that no client
    sends and that the validator now reads as a different value. Generated
    header strings stay inside what an HTTP field value may carry — printable
    characters, no whitespace at either end, and no comma when the style needs
    it to separate members — because reading a header as sent leaves nothing to
    escape a delimiter with. A hand-written case carrying a CR or an LF is
    refused as UnsupportedGeneration naming the header, rather than
    percent-encoded into something harmless or handed to a PSR-7 implementation
    to reject in its own words.

  • Added. headers.get in the zoo: a scalar, a list and an exploded object
    header, so the round trip a validator and a server have to agree on is
    proven rather than assumed. It could not be proven before — the whole zoo
    had no header parameter, and both sides percent-encoded, so they agreed with
    each other and with nobody else. The differential fixture gets its header
    parameter back for the same reason: the disagreement it was excluded for is
    settled, not pinned.

  • Fixed. A generated multipart text part no longer carries leading or
    trailing whitespace. RFC 2046 puts the part body between the blank line and
    the next delimiter and says nothing about trimming it; the parsers in the
    wild trim anyway, so a part whose value is only a carriage return came back
    as the empty string and any other value quietly lost its padding — the
    handler receiving a value the client did not send, which is the same failure
    as the query +. Found by the league differential on a seed the local runs
    had not drawn, four times in six thousand; the shape itself is pinned as its
    own case rather than removed from sight. Which way league falls on it is a
    property of its parser's minor version — riverline 2.0.3 reads the bytes,
    later ones trim — so the pin asserts our reading and the agreement on the
    shape the generator does emit, not a verdict that is true only of today's
    lock file.

  • Requires rasuvaeff/openapi-contract ^0.7.

  • Added. Three zoo operations carrying the document forms last month's
    findings lived in, and a document accessor beside each contract so those
    forms can be recorded into a portable corpus: a 3.0 body with a boolean
    additionalProperties, a JSON body whose property names are decimal integers,
    and a multipart body with encoding.contentType. Verified against the
    pre-fix validator — replaying the recorded corpus on openapi-contract
    0.5.1 rejects a bounded.create document outright and fails a valid
    numeric.create body, which is the point of recording it.

  • Added. A differential against league/openapi-psr7-validator fed by the
    generator. openapi-contract has compared verdicts with league from the
    start, but from a hand-written corpus, so it could only disagree about
    requests someone thought to write down; this package produces requests nobody
    thought of. Both readers are handed the same document and the same bytes: a
    generated valid case both must accept, and a case built to violate exactly
    one thing that neither may let through. The document is deliberately narrower
    than the zoo — a differential is informative only on the intersection of what
    two implementations claim to support, and outside it degenerates into a list
    of the other library's limitations. What is left out is recorded with its
    reason, including the one live disagreement it found: a header parameter is
    RFC 6570 simple expansion to us and opaque bytes to league, so %C4%8B is one
    character against maxLength on one side and six on the other. That case is
    pinned rather than generated. Verified by reverting the query decoder in the
    installed contract: the property turns red and names the parameter.

  • Added. A test that asks the one question neither package can answer
    alone: does the application behind the validator receive the value the
    generator recorded? A case is generated, materialized, validated, and then
    sent through Psr15Transport, which reads it the way parse_str() and the
    SAPI do — and all three readings have to agree. A validator whose reading of
    the wire is not the server's reading passes requests the application will
    mishandle, and that is precisely how a + in the query stood as a literal
    plus to the validator and a space to every SAPI for the life of the package.
    The half of the question a generator cannot ask — wire forms a client sends
    and a generator never emits, always percent-encoded as it is — is covered by
    a raw-wire data provider beside it. Verified by reverting the fix in the
    installed contract: the corresponding case turns red.

  • Fixed. RedactionPolicy::cookies does something (#73). The Cookie
    header was redacted wholesale by the default header set, so a policy naming a
    cookie and one that did not produced byte-identical output — the option was
    inert. It was also defending the wrong thing: the reproducer never applies
    credentials, by construction, so what it hid was the case's own generated
    data. cookie is out of the default set; a named cookie is redacted and the
    rest of the header stays readable. authorization, proxy-authorization and
    set-cookie stay wholesale, because a header arrives as one opaque string
    and its name is all there is to judge it by.

  • Changed. minMsi is 92 rather than 93, against a measured 93.05% (#75).
    A gate set at the current score has no headroom: adding source code adds
    mutants, some equivalent by construction, and the score falls before any test
    can be written to stop it. Two changes in one wave failed the gate on nothing
    but equivalent mutants they had introduced. The alarm the gate exists for — a
    real drop of ~36 killed mutants — still fires; the noise does not.