Passed
Pull Request — master (#84)
by Joao
01:45
created

OpenApiValidation::sendRequest()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 31
rs 9.5222
cc 5
nc 8
nop 1
1
<?php
2
3
namespace ByJG\ApiTools;
4
5
use ByJG\ApiTools\Base\Schema;
6
use ByJG\ApiTools\Exception\DefinitionNotFoundException;
7
use ByJG\ApiTools\Exception\GenericApiException;
8
use ByJG\ApiTools\Exception\HttpMethodNotFoundException;
9
use ByJG\ApiTools\Exception\InvalidDefinitionException;
10
use ByJG\ApiTools\Exception\InvalidRequestException;
11
use ByJG\ApiTools\Exception\NotMatchedException;
12
use ByJG\ApiTools\Exception\PathNotFoundException;
13
use ByJG\ApiTools\Exception\RequiredArgumentNotFound;
14
use ByJG\ApiTools\Exception\StatusCodeNotMatchedException;
15
use Psr\Http\Message\ResponseInterface;
16
17
/**
18
 * Trait OpenApiValidation
19
 *
20
 * Provides OpenAPI/Swagger validation functionality that can be used in any test class.
21
 * This trait allows you to validate API requests/responses against OpenAPI specifications
22
 * without being forced to extend ApiTestCase.
23
 *
24
 * Example usage:
25
 * <code>
26
 * class MyTest extends MyCustomBaseTest
27
 * {
28
 *     use OpenApiValidation;
29
 *
30
 *     public function setUp(): void
31
 *     {
32
 *         parent::setUp();
33
 *         $this->setSchema(Schema::fromFile('openapi.json'));
34
 *     }
35
 *
36
 *     public function testApi()
37
 *     {
38
 *         $request = new ApiRequester()
39
 *             ->withMethod('GET')
40
 *             ->withPath('/pet/1');
41
 *         $this->sendRequest($request);
42
 *     }
43
 * }
44
 * </code>
45
 *
46
 * @package ByJG\ApiTools
47
 */
48
trait OpenApiValidation
49
{
50
    /**
51
     * @var Schema|null
52
     */
53
    protected ?Schema $schema = null;
54
55
    /**
56
     * @var AbstractRequester|null
57
     */
58
    protected ?AbstractRequester $requester = null;
59
60
    /**
61
     * Configure the schema to use for requests.
62
     *
63
     * When set, all requests without an own schema use this one instead.
64
     *
65
     * @param Schema|null $schema
66
     */
67
    public function setSchema(?Schema $schema): void
68
    {
69
        $this->schema = $schema;
70
    }
71
72
    /**
73
     * Set a custom requester instance.
74
     *
75
     * @param AbstractRequester $requester
76
     */
77
    public function setRequester(AbstractRequester $requester): void
78
    {
79
        $this->requester = $requester;
80
    }
81
82
    /**
83
     * Get the requester instance, creating a default ApiRequester if needed.
84
     *
85
     * @return AbstractRequester|null
86
     */
87
    protected function getRequester(): AbstractRequester|null
88
    {
89
        if (is_null($this->requester)) {
90
            $this->requester = new ApiRequester();
91
        }
92
        return $this->requester;
93
    }
94
95
    /**
96
     * Send a request and validate it against the OpenAPI/Swagger schema.
97
     *
98
     * This method sends the HTTP request configured in the ApiRequester and validates:
99
     * - Request body matches the schema definition
100
     * - Response status code matches expected value
101
     * - Response body matches the schema definition
102
     *
103
     * Validation happens implicitly - if validation fails, an exception is thrown.
104
     *
105
     * @param AbstractRequester $request Configured request to send and validate
106
     * @return ResponseInterface The PSR-7 response object
107
     * @throws DefinitionNotFoundException
108
     * @throws GenericApiException
109
     * @throws HttpMethodNotFoundException
110
     * @throws InvalidDefinitionException
111
     * @throws InvalidRequestException
112
     * @throws NotMatchedException
113
     * @throws PathNotFoundException
114
     * @throws RequiredArgumentNotFound
115
     * @throws StatusCodeNotMatchedException
116
     */
117
    public function sendRequest(AbstractRequester $request): ResponseInterface
118
    {
119
        // Add own schema if nothing is passed.
120
        if (!$request->hasSchema()) {
121
            $this->checkSchema();
122
            $request = $request->withSchema($this->schema);
0 ignored issues
show
Bug introduced by
It seems like $this->schema can also be of type null; however, parameter $schema of ByJG\ApiTools\AbstractRequester::withSchema() does only seem to accept ByJG\ApiTools\Base\Schema, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
            $request = $request->withSchema(/** @scrutinizer ignore-type */ $this->schema);
Loading history...
123
        }
124
125
        // Send request and validate against the OpenAPI/Swagger specification
126
        // Validation happens inside send() - exceptions are thrown on failure
127
        $response = $request->send();
128
129
        // Add implicit PHPUnit assertion for status code (prevents "risky test" warnings)
130
        // This assertion checks the expected status (default 200) matches actual status
131
        if (method_exists($this, 'assertEquals')) {
132
            $this->assertEquals(
133
                $request->getExpectedStatus(),
134
                $response->getStatusCode(),
135
                "Expected HTTP status code {$request->getExpectedStatus()}"
136
            );
137
        }
138
139
        // Execute any additional PHPUnit assertions from convenience methods
140
        // (e.g., assertStatus(), assertJsonContains(), assertJsonPath())
141
        if (method_exists($this, 'assertEquals')) {
142
            foreach ($request->getPhpunitAssertions() as $assertion) {
143
                $assertion($this, $response);
144
            }
145
        }
146
147
        return $response;
148
    }
149
150
    /**
151
     * Send a request and validate it against the OpenAPI/Swagger schema.
152
     *
153
     * @param AbstractRequester $request
154
     * @return ResponseInterface
155
     * @throws DefinitionNotFoundException
156
     * @throws GenericApiException
157
     * @throws HttpMethodNotFoundException
158
     * @throws InvalidDefinitionException
159
     * @throws InvalidRequestException
160
     * @throws NotMatchedException
161
     * @throws PathNotFoundException
162
     * @throws RequiredArgumentNotFound
163
     * @throws StatusCodeNotMatchedException
164
     * @deprecated Since version 6.0, use sendRequest() instead. Will be removed in version 7.0
165
     */
166
    public function assertRequest(AbstractRequester $request): ResponseInterface
167
    {
168
        return $this->sendRequest($request);
169
    }
170
171
    /**
172
     * Check that a schema has been configured.
173
     *
174
     * @throws GenericApiException
175
     */
176
    protected function checkSchema(): void
177
    {
178
        if (!$this->schema) {
179
            throw new GenericApiException('You have to configure a schema for either the request or the testcase');
180
        }
181
    }
182
}
183