Giter VIP home page Giter VIP logo

osteel / openapi-httpfoundation-testing Goto Github PK

View Code? Open in Web Editor NEW
82.0 2.0 13.0 72 KB

Validate your HttpFoundation requests and responses against OpenAPI (3+) definitions

Home Page: https://tech.osteel.me/posts/openapi-backed-api-testing-in-php-projects-a-laravel-example

License: MIT License

PHP 100.00%
openapi httpfoundation symfony laravel http validation testing api php openapi3

openapi-httpfoundation-testing's Introduction

OpenAPI HttpFoundation Testing

Build Status Latest Stable Version License Downloads

Validate HttpFoundation requests and responses against OpenAPI (3+) definitions.

See this post for more details and this repository for an example use in a Laravel project.

๐Ÿ’ก While you can safely use this package for your projects, as long as version 1.0 has not been released "minor" version patches can contain breaking changes. Make sure to check the release section before you upgrade.

Why?

OpenAPI is a specification intended to describe RESTful APIs in a way that can be understood by both humans and machines.

By validating an API's requests and responses against the OpenAPI definition that describes it, we guarantee that the API is used correctly and behaves in accordance with the documentation we provide, thus making the OpenAPI definition the single source of truth.

The HttpFoundation component is developed and maintained as part of the Symfony framework. It is used to handle HTTP requests and responses in projects such as Symfony, Laravel, Drupal, and many others.

How does it work?

This package is built on top of OpenAPI PSR-7 Message Validator, which validates PSR-7 messages against OpenAPI definitions.

It converts HttpFoundation request and response objects to PSR-7 messages using Symfony's PSR-7 Bridge and Tobias Nyholm's PSR-7 implementation, before passing them on to OpenAPI PSR-7 Message Validator.

Installation

๐Ÿ’ก This package is mostly intended to be used as part of an API test suite.

Via Composer:

$ composer require --dev osteel/openapi-httpfoundation-testing

Usage

Import the builder class:

use Osteel\OpenApi\Testing\ValidatorBuilder;

Use the builder to create a \Osteel\OpenApi\Testing\Validator object, using one of the available factory methods for YAML or JSON:

// From a file:

$validator = ValidatorBuilder::fromYamlFile($yamlFile)->getValidator();
$validator = ValidatorBuilder::fromJsonFile($jsonFile)->getValidator();

// From a string:

$validator = ValidatorBuilder::fromYamlString($yamlString)->getValidator();
$validator = ValidatorBuilder::fromJsonString($jsonString)->getValidator();

// Automatic detection (slower):

$validator = ValidatorBuilder::fromYaml($yamlFileOrString)->getValidator();
$validator = ValidatorBuilder::fromJson($jsonFileOrString)->getValidator();

๐Ÿ’ก You can also use a dependency injection container to bind the ValidatorBuilder class to the ValidatorBuilderInterface interface it implements and inject the interface instead, which would also be useful for testing and mocking.

You can now validate \Symfony\Component\HttpFoundation\Request and \Symfony\Component\HttpFoundation\Response objects for a given path and method:

$validator->validate($response, '/users', 'post');

๐Ÿ’ก For convenience, objects implementing \Psr\Http\Message\ServerRequestInterface or \Psr\Http\Message\ResponseInterface are also accepted.

In the example above, we check that the response matches the OpenAPI definition for a POST request on the /users path.

Each of OpenAPI's supported HTTP methods (DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT and TRACE) also has a shortcut method that calls validate under the hood, meaning the line above could also be written this way:

$validator->post($response, '/users');

Validating a request object works exactly the same way:

$validator->post($request, '/users');

In the example above, we check that the request matches the OpenAPI definition for a POST request on the /users path.

The validate method returns true in case of success, and throw a \Osteel\OpenApi\Testing\Exceptions\ValidationException exception in case of error.

Caching

This package supports caching to speed up the parsing of OpenAPI definitions. Simply pass your PSR-6 or PSR-16 cache object to the setCache method of the ValidatorBuilder class.

Here is an example using Symfony's Array Cache Adapter:

use Osteel\OpenApi\Testing\ValidatorBuilder;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

$cache = new ArrayAdapter();
$validator = ValidatorBuilder::fromYamlFile($yamlFile)->setCache($cache)->getValidator();

Extending the package

There are two main extension points โ€“ message adapters and cache adapters.

Message adapters

The ValidatorBuilder class uses the HttpFoundationAdapter class as its default HTTP message adapter. This class converts HttpFoundation request and response objects to their PSR-7 counterparts.

If you need to change the adapter's logic, or if you need a new adapter altogether, create a class implementing the MessageAdapterInterface interface and pass it to the setMessageAdapter method of the ValidatorBuilder class:

$validator = ValidatorBuilder::fromYamlFile($yamlFile)
    ->setMessageAdapter($yourAdapter)
    ->getValidator();

Cache adapters

The ValidatorBuilder class uses the Psr16Adapter class as its default cache adapter. This class converts PSR-16 cache objects to their PSR-6 counterparts.

If you need to change the adapter's logic, or if you need a new adapter altogether, create a class implementing the CacheAdapterInterface interface and pass it to the setCacheAdapter method of the ValidatorBuilder class:

$validator = ValidatorBuilder::fromYamlFile($yamlFile)
    ->setCacheAdapter($yourAdapter)
    ->getValidator();

Other interfaces

The ValidatorBuilder and Validator classes are final but they implement the ValidatorBuilderInterface and ValidatorInterface interfaces respectively for which you can provide your own implementations if you need to.

Change log

Please see the Releases section for details.

Contributing

Please see CONTRIBUTING for details.

Credits

People

Special thanks to Pavel Batanov for his advice on structuring the package.

Packages

License

The MIT License (MIT). Please see License File for more information.

openapi-httpfoundation-testing's People

Contributors

osteel avatar paul-m avatar ttomdewit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

openapi-httpfoundation-testing's Issues

laravel 6

Hello, this pkg not working with laravel 6?

Your requirements could not be resolved to an installable set of packages.

Problem 1
- Root composer.json requires osteel/openapi-httpfoundation-testing ^0.6.0 -> satisfiable by osteel/openapi-httpfoundation-testing[v0.6].
- osteel/openapi-httpfoundation-testing v0.6 requires symfony/http-foundation ^5.3 -> found symfony/http-foundation[v5.3.0-BETA1, ..., 5.4.x-dev] but the package is fixed to v4.4.23 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.

Question: How to make the package raise an exception when field is missing in response?

Detailed description

Assume that we have the following openapi scheme:

        IdField:
            type: string
            format: uuid
        TestObject:
            type: object
            properties:
                id:
                    $ref: '#/components/schemas/IdField'
                name:
                    type: string
        TestResponse:
            type: array
            items:
                $ref: '#/components/schemas/TestObject'
    /test:
        get:
            tags:
                - Test
            summary: Some summary here
            responses:
                '200':
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/TestResponse'

And we check it like this

$validator = ValidatorBuilder::fromYamlFile($openapiFilePath)->getValidator();
$validator->validate($this->client->getResponse(),'/test', 'get');

If the response returns array of objects with fields id and name and correct types -> everything is ok
If types of fields are wrong -> we will get an error
If the object will contain fields id and name1 without name -> nothing will happen. No errors.

How to make the package validate required fields in response?

more debug information

Hello

How can I find out what field of response is incorrect ?

Osteel\OpenApi\Testing\Exceptions\ValidationException : Body does not match schema for content-type "application/json" for Response [get /entities/{id} 200]: Value '2022-07-31T21:00:00.000000Z' does not match format date of type string

How to validate securityScheme from openapi

Get this error when securityScheme set with header

Osteel\OpenApi\Testing\Exceptions\ValidationException: None of security schemas did match for Request [get /operation/{operationUuid}]

Defined security scheme as Bearer header

securitySchemes:
  X-Session:
    scheme: bearer
    bearerFormat: ''
    type: http
    description: Bearer Session Token
    x-last-modified: 1668326446280

and when validating request with below code
$validator->validate($request, '/permissions/{permissionUuid}', 'GET');

It gives this error

Osteel\OpenApi\Testing\Exceptions\ValidationException: None of security schemas did match for Request [get /operation/{operationUuid}]

/web/vendor/osteel/openapi-httpfoundation-testing/src/Exceptions/ValidationException.php:32
/web/vendor/osteel/openapi-httpfoundation-testing/src/Validator.php:70
/web/tests/TestCase/Action/Operation/OperationTest.php:83

Caused by
League\OpenAPIValidation\PSR7\Exception\Validation\InvalidSecurity: None of security schemas did match for Request [get /operation/{operationUuid}]

/web/vendor/league/openapi-psr7-validator/src/PSR7/Exception/Validation/AddressValidationFailed.php:39
/web/vendor/league/openapi-psr7-validator/src/PSR7/Exception/Validation/InvalidSecurity.php:24
/web/vendor/league/openapi-psr7-validator/src/PSR7/Validators/SecurityValidator.php:80
/web/vendor/league/openapi-psr7-validator/src/PSR7/Validators/SecurityValidator.php:53
/web/vendor/league/openapi-psr7-validator/src/PSR7/Validators/ValidatorChain.php:25
/web/vendor/league/openapi-psr7-validator/src/PSR7/RoutedServerRequestValidator.php:49
/web/vendor/osteel/openapi-httpfoundation-testing/src/Validator.php:68
/web/tests/TestCase/Permission/OperationTest.php:83

Is there support to add headers to validator request? If yes than how to add headers and if there is no support than how to validate request with security scheme.

Your environment

php 8.0
openapi 3.0

Return as exception all mandatory fields

Detailed description

Right now I have the id and name fields mandatory and description optional.
If I don't send id and name the exception return only id as missing. Can I show all missing fields that are mandatory?

Thanks

issue: Header values must be RFC 7230 compatible strings

Detailed description

I keep getting this error when trying to test my swagger docs using an endpoint
InvalidArgumentException: Header values must be RFC 7230 compatible strings.

Here's the trace:

/var/www/vendor/nyholm/psr7/src/MessageTrait.php:201
/var/www/vendor/nyholm/psr7/src/MessageTrait.php:80
/var/www/vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php:163
/var/www/vendor/osteel/openapi-httpfoundation-testing/src/HttpFoundation/HttpFoundationResponseAdapter.php:33
/var/www/vendor/osteel/openapi-httpfoundation-testing/src/ResponseValidator.php:57
/var/www/tests/Feature/SensorAPI/GatewayControllerTest.php:113

This is an example of the test I am trying to run:

        $response = $this->get(
            'api/v1/sensor/gateways/gateway_name',
            [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer token'
            ]
        );

        $validator = ResponseValidatorBuilder::fromJson(storage_path('api-docs/api-docs.json'))->getValidator();

        $result = $validator->validate('/v1/sensor/gateways/gateway_name', 'get', $response->baseResponse);

        $this->assertTrue($result);

I'm not sure if it's a bug but all my other tests work when testing requests.

Your environment

PHP 7.3.20
Laravel 8

inverse valiation as a possiblity.

Detailed description

This package works great to validate that all properties that are in the schema are also in the response, but I would like to also validate that every single property on my response is part of the schema.

Context

We're using https://orval.dev/ to generate typescript types based on the schema and sometimes our frontend people yell at me to add typing/schema, I would like our pipelines to yell at me instead.

How can it benefit other users?

^ See above.

Not obligatory, but suggest an idea for implementing addition or change.

I think it should be probably an optional setting, because I'm not sure everyone will want to be this strict about their responses matching schemas.

Testing against OpenAPI Specification doesnt seem to work as intended.

Hi there,

first of all this is not an issue.

This is a package that I alway wanted to have to validate my API endpoints against the OpenAPI documentation.

It seems that I am missing something and would love to get some help to get this to work.

I am using Laravel with Docker (Laravel Sail) and I use Stoplight Studio to get some help and to write my OpenAPI definition.

I used exactly the same Example I saw on twitter, but it seems I am getting some problems getting it to work the way I want it to have.

For a better example I recorded a video that shows where my problem is.

https://youtu.be/Gzadpf3MyCI

Thank you in advance.

PS: If you have some questions let me know.

Symfony 6 support

Would be nice to have Symfony 6 support. AS for me it should be enough to change supported versions in composer

Is there any way to validate response objects recursively?

It looks like the validator fails to check objects inside a request

Detailed description

I've modified responce in example project so it looks like this:

Route::post('/test', function (Request $request) {
    return response()->json([
        'status' => 200,
        'success' => true,
        'data' => [
            'here' => 'here',
        ],
    ]);
});

Also I've modified openapi defenition:

  /test:
    post:
      tags:
        - "Authorization"
      summary: "login into user account"
      description: "Stores new access tokens in DB. returns stored access tokens and user data"
      requestBody:
        $ref: '#/components/requestBodies/LoginRequest'
      responses:
        "200":
          $ref: "#/components/responses/LoginResponse"

  responses:
    LoginResponse:
      description: "successful operation"
      content:
        application/json:
          schema:
            type: object
            required:
              - status
              - success
              - data
            properties:
              status:
                type: integer
                example: 200
              success:
                type: boolean
                example: true
              data:
                type: object
                properties:
                  accessToken:
                    type: integer
                  refreshToken:
                    type: integer

  requestBodies:
    LoginRequest:
      content:
        application/json:
          schema:
            type: object
            properties:
              email:
                type: string
                example: "[email protected]"
              password:
                description: "min 6 symbols"
                type: string
                example: "password123"
            required:
              - email
              - password

as you can see actual response and openapi response body object doesn't match. a real one returns only 'here' => 'here' in data while open api expects 'refreshToken' => 'string' and 'accessToken' => 'string'

But tests are passing anyway which was not expected in this case

Context

I was expecting it is possible to validate response body entirely

Possible implementation

Unfortunately I'm not experienced enough to offer any concrete solutions

Your environment

openapi-httpfoundation-testing example laravel project launched via sail

php8.1, Ubuntu 22.04

Support for Symfony 5.4 is broken

Package version
0.10

Describe the bug
I cannot update to 0.10 because of a new requirement on symfony/cache ^6.0

To Reproduce
composer.json

{
  "require": {
    "php": ">=8.2",
    "symfony/symfony": "5.4.*",
  },
  "require-dev": {
    "osteel/openapi-httpfoundation-testing": "^0.9.0",
  },
  "extra": {
    "symfony": {
        "require": "5.4.*"
    },
}
$ composer require --dev osteel/openapi-httpfoundation-testing:^0.10.0 -W
./composer.json has been updated
Running composer update osteel/openapi-httpfoundation-testing --with-all-dependencies
Loading composer repositories with package information                                                                                                                       Info from https://repo.packagist.org: #StandWithUkraine
Restricting packages listed in "symfony/symfony" to "5.4.*"
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires osteel/openapi-httpfoundation-testing ^0.10 -> satisfiable by osteel/openapi-httpfoundation-testing[v0.10].
    - osteel/openapi-httpfoundation-testing v0.10 requires symfony/cache ^6.0 -> found symfony/cache[v6.0.0, ..., v6.3.0] but these were not loaded, likely because it conflicts with another require.

Expected behaviour
I expected the library to be compatible with maintained Symfony branches, which are 5.4, 6.2, and 6.3: https://symfony.com/releases

Path parameter validation is broken

Path parameter validation is broken with the version 0.6.0

Detailed description

openapi-psr7-validator cannot parse path parameters when using the 0.6.0 version of the package. The following LogicException is thrown when trying to validate an endpoint with a path parameter in it:

Required Parameter Missing should not be thrown in Path Validator, because presence of all parameters have to be checked before

Your environment

  • PHP 8.0
  • Laravel 8.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.