Skip to content

Latest commit

 

History

History
64 lines (48 loc) · 3.5 KB

File metadata and controls

64 lines (48 loc) · 3.5 KB

Schema-driven request fuzzing

The ExploresOpenApiEndpoint trait generates N happy-path request inputs for one (method, path) operation directly from the OpenAPI spec — the PHP equivalent of Schemathesis. Pair it with the existing ValidatesOpenApiSchema trait and every fuzzed call automatically asserts response contract conformance and records coverage.

use PHPUnit\Framework\TestCase;
use Studio\OpenApiContractTesting\Laravel\ExploresOpenApiEndpoint;
use Studio\OpenApiContractTesting\Laravel\ValidatesOpenApiSchema;
use Studio\OpenApiContractTesting\Attribute\OpenApiSpec;

#[OpenApiSpec('front')]
class CreatePetTest extends TestCase
{
    use ExploresOpenApiEndpoint;
    use ValidatesOpenApiSchema;

    public function test_create_pet_contract(): void
    {
        $this->exploreEndpoint('POST', '/v1/pets', cases: 50, seed: 1)
            ->each(fn ($input) => $this->postJson('/api/v1/pets', $input->body)
                ->assertSuccessful());
    }
}

What you get per case (Studio\OpenApiContractTesting\Fuzz\ExploredCase):

Property Description
body Generated JSON body (or null when the operation has no application/json requestBody)
query name → value for every in: query parameter
headers name → value for every in: header parameter (excludes the OpenAPI-reserved Accept/Content-Type/Authorization)
pathParams name → value for every {placeholder} segment
method, matchedPath The resolved spec template (/v1/pets/{petId}) and its method

The collection is Countable and IteratorAggregate, so foreach ($cases as $case) works too if you prefer it over the fluent each() helper.

Generation behaviour

  • Supported keywords: type (string/integer/number/boolean/object/array/null), enum, format (email/idn-email/uuid/date/date-time/time/uri/url/iri/hostname/ipv4/ipv6), minLength/maxLength, minimum/maximum, required, properties, items.
  • Optional object properties alternate between included and omitted across cases, so each batch exercises both required-only and required+optional shapes.
  • Required keys are always emitted.
  • Path resolution accepts both the spec template form (/v1/pets/{petId}) and concrete URIs that match it (/api/v1/pets/123 with strip_prefixes=/api). Captured URI values are intentionally discarded — pathParams is always regenerated from the operation spec for consistency.

seed and determinism

When fakerphp/faker is installed (already a transitive dev dependency via orchestra/testbench for most projects), generation uses Faker's locale-aware primitives and is fully deterministic for a given seed:. Without Faker, the trait falls back to deterministic counter-based primitives that still pass schema validation — your CI never depends on a runtime-installed package.

# Optional but recommended for realistic generation
composer require --dev fakerphp/faker

Out of scope (today)

The MVP intentionally targets happy-path generation. Tracked separately:

  • Boundary value injection (min/max-length extremes, Unicode edge cases)
  • Negative-case generation (deliberately invalid inputs to assert 4xx responses)
  • oneOf / anyOf / allOf composition; regex pattern; multipleOf; minItems / maxItems
  • Whole-spec auto-exploration (exploreSpec() to walk every endpoint)