robotframework-openapitools 0.2.3__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OpenApiDriver/openapi_executors.py +14 -9
- OpenApiDriver/openapidriver.libspec +4 -4
- OpenApiDriver/openapidriver.py +1 -1
- OpenApiLibCore/dto_base.py +15 -6
- OpenApiLibCore/openapi_libcore.libspec +152 -27
- OpenApiLibCore/openapi_libcore.py +156 -8
- {robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/METADATA +8 -7
- {robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/RECORD +10 -10
- {robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/WHEEL +1 -1
- {robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/LICENSE +0 -0
@@ -20,7 +20,7 @@ run_keyword = BuiltIn().run_keyword
|
|
20
20
|
logger = getLogger(__name__)
|
21
21
|
|
22
22
|
|
23
|
-
@library(scope="
|
23
|
+
@library(scope="SUITE", doc_format="ROBOT")
|
24
24
|
class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-attributes
|
25
25
|
"""Main class providing the keywords and core logic to perform endpoint validations."""
|
26
26
|
|
@@ -52,9 +52,13 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
52
52
|
source=source,
|
53
53
|
origin=origin,
|
54
54
|
base_path=base_path,
|
55
|
+
response_validation=response_validation,
|
56
|
+
disable_server_validation=disable_server_validation,
|
55
57
|
mappings_path=mappings_path,
|
56
58
|
default_id_property_name=default_id_property_name,
|
59
|
+
invalid_property_default_response=invalid_property_default_response,
|
57
60
|
faker_locale=faker_locale,
|
61
|
+
require_body_for_invalid_url=require_body_for_invalid_url,
|
58
62
|
recursion_limit=recursion_limit,
|
59
63
|
recursion_default=recursion_default,
|
60
64
|
username=username,
|
@@ -67,10 +71,6 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
67
71
|
cookies=cookies,
|
68
72
|
proxies=proxies,
|
69
73
|
)
|
70
|
-
self.response_validation = response_validation
|
71
|
-
self.disable_server_validation = disable_server_validation
|
72
|
-
self.require_body_for_invalid_url = require_body_for_invalid_url
|
73
|
-
self.invalid_property_default_response = invalid_property_default_response
|
74
74
|
|
75
75
|
@keyword
|
76
76
|
def test_unauthorized(self, path: str, method: str) -> None:
|
@@ -118,8 +118,9 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
118
118
|
Perform a request for the provided 'path' and 'method' where the url for
|
119
119
|
the `path` is invalidated.
|
120
120
|
|
121
|
-
This keyword will be `SKIPPED` if the path contains no parts
|
122
|
-
can be invalidated
|
121
|
+
This keyword will be `SKIPPED` if the path contains no parts
|
122
|
+
that can be invalidated and there is no mapping for a
|
123
|
+
PathPropertiesConstraint for the `expected_status_code`.
|
123
124
|
|
124
125
|
The optional `expected_status_code` parameter (default: 404) can be set to the
|
125
126
|
expected status code for APIs that do not return a 404 on invalid urls.
|
@@ -131,7 +132,11 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
131
132
|
"""
|
132
133
|
valid_url: str = run_keyword("get_valid_url", path, method)
|
133
134
|
|
134
|
-
if not (
|
135
|
+
if not (
|
136
|
+
url := run_keyword(
|
137
|
+
"get_invalidated_url", valid_url, path, method, expected_status_code
|
138
|
+
)
|
139
|
+
):
|
135
140
|
raise SkipExecution(
|
136
141
|
f"Path {path} does not contain resource references that "
|
137
142
|
f"can be invalidated."
|
@@ -194,7 +199,7 @@ class OpenApiExecutors(OpenApiLibCore): # pylint: disable=too-many-instance-att
|
|
194
199
|
}
|
195
200
|
invalidation_keywords = []
|
196
201
|
|
197
|
-
if request_data.dto.
|
202
|
+
if request_data.dto.get_body_relations_for_error_code(status_code):
|
198
203
|
invalidation_keywords.append("get_invalid_json_data")
|
199
204
|
if request_data.dto.get_parameter_relations_for_error_code(status_code):
|
200
205
|
invalidation_keywords.append("get_invalidated_parameters")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-
|
3
|
-
<version>0.
|
2
|
+
<keywordspec name="OpenApiDriver" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-12-04T12:16:37+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapidriver.py" lineno="352">
|
3
|
+
<version>0.4.0</version>
|
4
4
|
<doc><p>Visit the <a href="https://github.com/MarketSquare/robotframework-openapidriver">library page</a> for an introduction and examples.</p></doc>
|
5
5
|
<tags>
|
6
6
|
</tags>
|
@@ -265,7 +265,7 @@
|
|
265
265
|
</init>
|
266
266
|
</inits>
|
267
267
|
<keywords>
|
268
|
-
<kw name="Test Endpoint" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="
|
268
|
+
<kw name="Test Endpoint" source="/workspaces/robotframework-openapitools/src/OpenApiDriver/openapi_executors.py" lineno="161">
|
269
269
|
<arguments repr="path: str, method: str, status_code: int">
|
270
270
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
|
271
271
|
<name>path</name>
|
@@ -318,7 +318,7 @@
|
|
318
318
|
</arg>
|
319
319
|
</arguments>
|
320
320
|
<doc><p>Perform a request for the provided 'path' and 'method' where the url for the <a href="#type-Path" class="name">path</a> is invalidated.</p>
|
321
|
-
<p>This keyword will be <span class="name">SKIPPED</span> if the path contains no parts that can be invalidated
|
321
|
+
<p>This keyword will be <span class="name">SKIPPED</span> if the path contains no parts that can be invalidated and there is no mapping for a PathPropertiesConstraint for the <span class="name">expected_status_code</span>.</p>
|
322
322
|
<p>The optional <span class="name">expected_status_code</span> parameter (default: 404) can be set to the expected status code for APIs that do not return a 404 on invalid urls.</p>
|
323
323
|
<p>&gt; Note: Depending on API design, the url may be validated before or after validation of headers, query parameters and / or (json) body. By default, no parameters are send with the request. The <span class="name">require_body_for_invalid_url</span> parameter can be set to <span class="name">True</span> if needed.</p></doc>
|
324
324
|
<shortdoc>Perform a request for the provided 'path' and 'method' where the url for the `path` is invalidated.</shortdoc>
|
OpenApiDriver/openapidriver.py
CHANGED
@@ -134,7 +134,7 @@ from OpenApiDriver.openapi_executors import OpenApiExecutors, ValidationLevel
|
|
134
134
|
from OpenApiDriver.openapi_reader import OpenApiReader
|
135
135
|
|
136
136
|
|
137
|
-
@library(scope="
|
137
|
+
@library(scope="SUITE", doc_format="ROBOT")
|
138
138
|
class OpenApiDriver(OpenApiExecutors, DataDriver):
|
139
139
|
"""
|
140
140
|
Visit the [https://github.com/MarketSquare/robotframework-openapidriver | library page]
|
OpenApiLibCore/dto_base.py
CHANGED
@@ -99,6 +99,8 @@ class PathPropertiesConstraint(ResourceRelation):
|
|
99
99
|
|
100
100
|
path: str
|
101
101
|
property_name: str = "id"
|
102
|
+
invalid_value: Any = NOT_SET
|
103
|
+
invalid_value_error_code: int = 422
|
102
104
|
error_code: int = 404
|
103
105
|
|
104
106
|
|
@@ -111,6 +113,7 @@ class PropertyValueConstraint(ResourceRelation):
|
|
111
113
|
invalid_value: Any = NOT_SET
|
112
114
|
invalid_value_error_code: int = 422
|
113
115
|
error_code: int = 422
|
116
|
+
treat_as_mandatory: bool = False
|
114
117
|
|
115
118
|
|
116
119
|
@dataclass
|
@@ -190,6 +193,14 @@ class Dto(ABC):
|
|
190
193
|
]
|
191
194
|
return relations
|
192
195
|
|
196
|
+
def get_body_relations_for_error_code(self, error_code: int) -> List[Relation]:
|
197
|
+
"""
|
198
|
+
Return the list of Relations associated with the given error_code that are
|
199
|
+
applicable to the body / payload of the request.
|
200
|
+
"""
|
201
|
+
all_relations = self.get_relations_for_error_code(error_code=error_code)
|
202
|
+
return [r for r in all_relations if not isinstance(r, PathPropertiesConstraint)]
|
203
|
+
|
193
204
|
def get_invalidated_data(
|
194
205
|
self,
|
195
206
|
schema: Dict[str, Any],
|
@@ -207,18 +218,16 @@ class Dto(ABC):
|
|
207
218
|
r for r in relations if not isinstance(r, PathPropertiesConstraint)
|
208
219
|
]
|
209
220
|
property_names = [r.property_name for r in relations]
|
210
|
-
if status_code == invalid_property_default_code:
|
221
|
+
if status_code == invalid_property_default_code and schema.get("properties"):
|
211
222
|
# add all properties defined in the schema, including optional properties
|
212
223
|
property_names.extend((schema["properties"].keys()))
|
213
|
-
# remove duplicates
|
214
|
-
property_names = list(set(property_names))
|
215
224
|
if not property_names:
|
216
225
|
raise ValueError(
|
217
226
|
f"No property can be invalidated to cause status_code {status_code}"
|
218
227
|
)
|
219
|
-
# shuffle the property_names so different properties on
|
220
|
-
# when rerunning the test
|
221
|
-
shuffle(property_names)
|
228
|
+
# Remove duplicates, then shuffle the property_names so different properties on
|
229
|
+
# the Dto are invalidated when rerunning the test.
|
230
|
+
shuffle(list(set(property_names)))
|
222
231
|
for property_name in property_names:
|
223
232
|
# if possible, invalidate a constraint but send otherwise valid data
|
224
233
|
id_dependencies = [
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<keywordspec name="OpenApiLibCore" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-
|
3
|
-
<version>0.
|
2
|
+
<keywordspec name="OpenApiLibCore" type="LIBRARY" format="HTML" scope="SUITE" generated="2024-12-04T12:16:36+00:00" specversion="6" source="/workspaces/robotframework-openapitools/src/OpenApiLibCore/openapi_libcore.py" lineno="457">
|
3
|
+
<version>0.4.0</version>
|
4
4
|
<doc><p>Main class providing the keywords and core logic to interact with an OpenAPI server.</p>
|
5
5
|
<p>Visit the <a href="https://github.com/MarketSquare/robotframework-openapi-libcore">library page</a> for an introduction.</p></doc>
|
6
6
|
<tags>
|
7
7
|
</tags>
|
8
8
|
<inits>
|
9
|
-
<init name="__init__" lineno="
|
10
|
-
<arguments repr="source: str, origin: str = , base_path: str = , mappings_path: str | Path = , invalid_property_default_response: int = 422, default_id_property_name: str = id, faker_locale: str | List[str] | None = None, recursion_limit: int = 1, recursion_default: Any = {}, username: str = , password: str = , security_token: str = , auth: AuthBase | None = None, cert: str | Tuple[str, str] | None = None, verify_tls: bool | str | None = True, extra_headers: Dict[str, str] | None = None, cookies: Dict[str, str] | RequestsCookieJar | None = None, proxies: Dict[str, str] | None = None">
|
9
|
+
<init name="__init__" lineno="465">
|
10
|
+
<arguments repr="source: str, origin: str = , base_path: str = , response_validation: ValidationLevel = WARN, disable_server_validation: bool = True, mappings_path: str | Path = , invalid_property_default_response: int = 422, default_id_property_name: str = id, faker_locale: str | List[str] | None = None, require_body_for_invalid_url: bool = False, recursion_limit: int = 1, recursion_default: Any = {}, username: str = , password: str = , security_token: str = , auth: AuthBase | None = None, cert: str | Tuple[str, str] | None = None, verify_tls: bool | str | None = True, extra_headers: Dict[str, str] | None = None, cookies: Dict[str, str] | RequestsCookieJar | None = None, proxies: Dict[str, str] | None = None">
|
11
11
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="source: str">
|
12
12
|
<name>source</name>
|
13
13
|
<type name="str" typedoc="string"/>
|
@@ -22,6 +22,16 @@
|
|
22
22
|
<type name="str" typedoc="string"/>
|
23
23
|
<default/>
|
24
24
|
</arg>
|
25
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="response_validation: ValidationLevel = WARN">
|
26
|
+
<name>response_validation</name>
|
27
|
+
<type name="ValidationLevel" typedoc="ValidationLevel"/>
|
28
|
+
<default>WARN</default>
|
29
|
+
</arg>
|
30
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="disable_server_validation: bool = True">
|
31
|
+
<name>disable_server_validation</name>
|
32
|
+
<type name="bool" typedoc="boolean"/>
|
33
|
+
<default>True</default>
|
34
|
+
</arg>
|
25
35
|
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="mappings_path: str | Path = ">
|
26
36
|
<name>mappings_path</name>
|
27
37
|
<type name="Union" union="true">
|
@@ -51,6 +61,11 @@
|
|
51
61
|
</type>
|
52
62
|
<default>None</default>
|
53
63
|
</arg>
|
64
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="require_body_for_invalid_url: bool = False">
|
65
|
+
<name>require_body_for_invalid_url</name>
|
66
|
+
<type name="bool" typedoc="boolean"/>
|
67
|
+
<default>False</default>
|
68
|
+
</arg>
|
54
69
|
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="recursion_limit: int = 1">
|
55
70
|
<name>recursion_limit</name>
|
56
71
|
<type name="int" typedoc="integer"/>
|
@@ -147,6 +162,17 @@
|
|
147
162
|
<p>The server (and port) of the target server. E.g. <code>https://localhost:8000</code></p>
|
148
163
|
<h4>base_path</h4>
|
149
164
|
<p>The routing between <code>origin</code> and the endpoints as found in the <code>paths</code> section in the openapi document. E.g. <code>/petshop/v2</code>.</p>
|
165
|
+
<h3>Test case execution</h3>
|
166
|
+
<h4>response_validation</h4>
|
167
|
+
<p>By default, a <code>WARN</code> is logged when the Response received after a Request does not comply with the schema as defined in the openapi document for the given operation. The following values are supported:</p>
|
168
|
+
<ul>
|
169
|
+
<li><code>DISABLED</code>: All Response validation errors will be ignored</li>
|
170
|
+
<li><code>INFO</code>: Any Response validation erros will be logged at <code>INFO</code> level</li>
|
171
|
+
<li><code>WARN</code>: Any Response validation erros will be logged at <code>WARN</code> level</li>
|
172
|
+
<li><code>STRICT</code>: The Test Case will fail on any Response validation errors</li>
|
173
|
+
</ul>
|
174
|
+
<h4>disable_server_validation</h4>
|
175
|
+
<p>If enabled by setting this parameter to <code>True</code>, the Response validation will also include possible errors for Requests made to a server address that is not defined in the list of servers in the openapi document. This generally means that if there is a mismatch, every Test Case will raise this error. Note that <code>localhost</code> and <code>127.0.0.1</code> are not considered the same by Response validation.</p>
|
150
176
|
<h3>API-specific configurations</h3>
|
151
177
|
<h4>mappings_path</h4>
|
152
178
|
<p>See <a href="https://marketsquare.github.io/robotframework-openapi-libcore/advanced_use.html">this page</a> for an in-depth explanation.</p>
|
@@ -157,6 +183,8 @@
|
|
157
183
|
<p>If different property names are used for the unique identifier for different types of resources, an <code>ID_MAPPING</code> can be implemented using the <code>mappings_path</code>.</p>
|
158
184
|
<h4>faker_locale</h4>
|
159
185
|
<p>A locale string or list of locale strings to pass to the Faker library to be used in generation of string data for supported format types.</p>
|
186
|
+
<h4>require_body_for_invalid_url</h4>
|
187
|
+
<p>When a request is made against an invalid url, this usually is because of a "404" request; a request for a resource that does not exist. Depending on API implementation, when a request with a missing or invalid request body is made on a non-existent resource, either a 404 or a 422 or 400 Response is normally returned. If the API being tested processes the request body before checking if the requested resource exists, set this parameter to True.</p>
|
160
188
|
<h3>Parsing parameters</h3>
|
161
189
|
<h4>recursion_limit</h4>
|
162
190
|
<p>The recursion depth to which to fully parse recursive references before the <span class="name">recursion_default</span> is used to end the recursion.</p>
|
@@ -186,8 +214,8 @@
|
|
186
214
|
</init>
|
187
215
|
</inits>
|
188
216
|
<keywords>
|
189
|
-
<kw name="Authorized Request" lineno="
|
190
|
-
<arguments repr="url: str, method: str, params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None, json_data: Dict[str, Dict[str, JSON] | List[JSON] | str | int | float | bool | None] | List[Dict[str, JSON] | List[JSON] | str | int | float | bool | None] | str | int | float | bool | None = None">
|
217
|
+
<kw name="Authorized Request" lineno="1731">
|
218
|
+
<arguments repr="url: str, method: str, params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None, json_data: Dict[str, Dict[str, JSON] | List[JSON] | str | int | float | bool | None] | List[Dict[str, JSON] | List[JSON] | str | int | float | bool | None] | str | int | float | bool | None = None, data: Any = None, files: Any = None">
|
191
219
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
192
220
|
<name>url</name>
|
193
221
|
<type name="str" typedoc="string"/>
|
@@ -262,13 +290,24 @@
|
|
262
290
|
</type>
|
263
291
|
<default>None</default>
|
264
292
|
</arg>
|
293
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="data: Any = None">
|
294
|
+
<name>data</name>
|
295
|
+
<type name="Any" typedoc="Any"/>
|
296
|
+
<default>None</default>
|
297
|
+
</arg>
|
298
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="files: Any = None">
|
299
|
+
<name>files</name>
|
300
|
+
<type name="Any" typedoc="Any"/>
|
301
|
+
<default>None</default>
|
302
|
+
</arg>
|
265
303
|
</arguments>
|
266
304
|
<returntype name="Response"/>
|
267
305
|
<doc><p>Perform a request using the security token or authentication set in the library.</p>
|
306
|
+
<p><span class="name">json_data</span>, <span class="name">data</span> and <span class="name">files</span> are passed to <span class="name">requests.request</span>s <span class="name">json</span>, <span class="name">data</span> and <span class="name">files</span> parameters unaltered. See the requests documentation for details: <a href="https://requests.readthedocs.io/en/latest/api/#requests.request">https://requests.readthedocs.io/en/latest/api/#requests.request</a></p>
|
268
307
|
<p>&gt; Note: provided username / password or auth objects take precedence over token based security</p></doc>
|
269
308
|
<shortdoc>Perform a request using the security token or authentication set in the library.</shortdoc>
|
270
309
|
</kw>
|
271
|
-
<kw name="Ensure In Use" lineno="
|
310
|
+
<kw name="Ensure In Use" lineno="1635">
|
272
311
|
<arguments repr="url: str, resource_relation: IdReference">
|
273
312
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
274
313
|
<name>url</name>
|
@@ -282,7 +321,7 @@
|
|
282
321
|
<doc><p>Ensure that the (right-most) <span class="name">id</span> of the resource referenced by the <span class="name">url</span> is used by the resource defined by the <span class="name">resource_relation</span>.</p></doc>
|
283
322
|
<shortdoc>Ensure that the (right-most) `id` of the resource referenced by the `url` is used by the resource defined by the `resource_relation`.</shortdoc>
|
284
323
|
</kw>
|
285
|
-
<kw name="Get Ids From Url" lineno="
|
324
|
+
<kw name="Get Ids From Url" lineno="984">
|
286
325
|
<arguments repr="url: str">
|
287
326
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
288
327
|
<name>url</name>
|
@@ -295,7 +334,7 @@
|
|
295
334
|
<doc><p>Perform a GET request on the <span class="name">url</span> and return the list of resource <span class="name">ids</span> from the response.</p></doc>
|
296
335
|
<shortdoc>Perform a GET request on the `url` and return the list of resource `ids` from the response.</shortdoc>
|
297
336
|
</kw>
|
298
|
-
<kw name="Get Invalid Json Data" lineno="
|
337
|
+
<kw name="Get Invalid Json Data" lineno="1409">
|
299
338
|
<arguments repr="url: str, method: str, status_code: int, request_data: RequestData">
|
300
339
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
301
340
|
<name>url</name>
|
@@ -322,7 +361,7 @@
|
|
322
361
|
<p>&gt; Note: applicable UniquePropertyValueConstraint and IdReference Relations are considered before changes to <span class="name">json_data</span> are made.</p></doc>
|
323
362
|
<shortdoc>Return `json_data` based on the `dto` on the `request_data` that will cause the provided `status_code` for the `method` operation on the `url`.</shortdoc>
|
324
363
|
</kw>
|
325
|
-
<kw name="Get Invalidated Parameters" lineno="
|
364
|
+
<kw name="Get Invalidated Parameters" lineno="1460">
|
326
365
|
<arguments repr="status_code: int, request_data: RequestData">
|
327
366
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="status_code: int">
|
328
367
|
<name>status_code</name>
|
@@ -346,22 +385,37 @@
|
|
346
385
|
<doc><p>Returns a version of <span class="name">params, headers</span> as present on <span class="name">request_data</span> that has been modified to cause the provided <span class="name">status_code</span>.</p></doc>
|
347
386
|
<shortdoc>Returns a version of `params, headers` as present on `request_data` that has been modified to cause the provided `status_code`.</shortdoc>
|
348
387
|
</kw>
|
349
|
-
<kw name="Get Invalidated Url" lineno="
|
350
|
-
<arguments repr="valid_url: str">
|
388
|
+
<kw name="Get Invalidated Url" lineno="1356">
|
389
|
+
<arguments repr="valid_url: str, path: str = , method: str = , expected_status_code: int = 404">
|
351
390
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="valid_url: str">
|
352
391
|
<name>valid_url</name>
|
353
392
|
<type name="str" typedoc="string"/>
|
354
393
|
</arg>
|
394
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="path: str = ">
|
395
|
+
<name>path</name>
|
396
|
+
<type name="str" typedoc="string"/>
|
397
|
+
<default/>
|
398
|
+
</arg>
|
399
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="method: str = ">
|
400
|
+
<name>method</name>
|
401
|
+
<type name="str" typedoc="string"/>
|
402
|
+
<default/>
|
403
|
+
</arg>
|
404
|
+
<arg kind="POSITIONAL_OR_NAMED" required="false" repr="expected_status_code: int = 404">
|
405
|
+
<name>expected_status_code</name>
|
406
|
+
<type name="int" typedoc="integer"/>
|
407
|
+
<default>404</default>
|
408
|
+
</arg>
|
355
409
|
</arguments>
|
356
410
|
<returntype name="Union" union="true">
|
357
411
|
<type name="str" typedoc="string"/>
|
358
412
|
<type name="None" typedoc="None"/>
|
359
413
|
</returntype>
|
360
|
-
<doc><p>Return an url with all the path parameters in the <span class="name">valid_url</span> replaced by a random UUID.</p>
|
414
|
+
<doc><p>Return an url with all the path parameters in the <span class="name">valid_url</span> replaced by a random UUID if no PathPropertiesConstraint is mapped for the <a href="#type-Path" class="name">path</a>, <span class="name">method</span> and <span class="name">expected_status_code</span>. If a PathPropertiesConstraint is mapped, the <span class="name">invalid_value</span> is returned.</p>
|
361
415
|
<p>Raises ValueError if the valid_url cannot be invalidated.</p></doc>
|
362
|
-
<shortdoc>Return an url with all the path parameters in the `valid_url` replaced by a random UUID.</shortdoc>
|
416
|
+
<shortdoc>Return an url with all the path parameters in the `valid_url` replaced by a random UUID if no PathPropertiesConstraint is mapped for the `path`, `method` and `expected_status_code`. If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.</shortdoc>
|
363
417
|
</kw>
|
364
|
-
<kw name="Get Json Data For Dto Class" lineno="
|
418
|
+
<kw name="Get Json Data For Dto Class" lineno="1232">
|
365
419
|
<arguments repr="schema: Dict[str, Any], dto_class: Dto | Type[Dto], operation_id: str = ">
|
366
420
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="schema: Dict[str, Any]">
|
367
421
|
<name>schema</name>
|
@@ -395,7 +449,7 @@
|
|
395
449
|
<doc><p>Generate a valid (json-compatible) dict for all the <span class="name">dto_class</span> properties.</p></doc>
|
396
450
|
<shortdoc>Generate a valid (json-compatible) dict for all the `dto_class` properties.</shortdoc>
|
397
451
|
</kw>
|
398
|
-
<kw name="Get Json Data With Conflict" lineno="
|
452
|
+
<kw name="Get Json Data With Conflict" lineno="1679">
|
399
453
|
<arguments repr="url: str, method: str, dto: Dto, conflict_status_code: int">
|
400
454
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
401
455
|
<name>url</name>
|
@@ -421,7 +475,7 @@
|
|
421
475
|
<doc><p>Return <span class="name">json_data</span> based on the <span class="name">UniquePropertyValueConstraint</span> that must be returned by the <span class="name">get_relations</span> implementation on the <span class="name">dto</span> for the given <span class="name">conflict_status_code</span>.</p></doc>
|
422
476
|
<shortdoc>Return `json_data` based on the `UniquePropertyValueConstraint` that must be returned by the `get_relations` implementation on the `dto` for the given `conflict_status_code`.</shortdoc>
|
423
477
|
</kw>
|
424
|
-
<kw name="Get Parameterized Endpoint From Url" lineno="
|
478
|
+
<kw name="Get Parameterized Endpoint From Url" lineno="1397">
|
425
479
|
<arguments repr="url: str">
|
426
480
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="url: str">
|
427
481
|
<name>url</name>
|
@@ -432,7 +486,7 @@
|
|
432
486
|
<doc><p>Return the endpoint as found in the <span class="name">paths</span> section based on the given <span class="name">url</span>.</p></doc>
|
433
487
|
<shortdoc>Return the endpoint as found in the `paths` section based on the given `url`.</shortdoc>
|
434
488
|
</kw>
|
435
|
-
<kw name="Get Request Data" lineno="
|
489
|
+
<kw name="Get Request Data" lineno="1024">
|
436
490
|
<arguments repr="endpoint: str, method: str">
|
437
491
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="endpoint: str">
|
438
492
|
<name>endpoint</name>
|
@@ -447,7 +501,7 @@
|
|
447
501
|
<doc><p>Return an object with valid request data for body, headers and query params.</p></doc>
|
448
502
|
<shortdoc>Return an object with valid request data for body, headers and query params.</shortdoc>
|
449
503
|
</kw>
|
450
|
-
<kw name="Get Valid Id For Endpoint" lineno="
|
504
|
+
<kw name="Get Valid Id For Endpoint" lineno="888">
|
451
505
|
<arguments repr="endpoint: str, method: str">
|
452
506
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="endpoint: str">
|
453
507
|
<name>endpoint</name>
|
@@ -467,7 +521,7 @@
|
|
467
521
|
<p>To prevent resource conflicts with other test cases, a new resource is created (POST) if possible.</p></doc>
|
468
522
|
<shortdoc>Support keyword that returns the `id` for an existing resource at `endpoint`.</shortdoc>
|
469
523
|
</kw>
|
470
|
-
<kw name="Get Valid Url" lineno="
|
524
|
+
<kw name="Get Valid Url" lineno="848">
|
471
525
|
<arguments repr="endpoint: str, method: str">
|
472
526
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="endpoint: str">
|
473
527
|
<name>endpoint</name>
|
@@ -484,7 +538,7 @@
|
|
484
538
|
<p>&gt; Note: if valid ids cannot be retrieved within the scope of the API, the <span class="name">PathPropertiesConstraint</span> Relation can be used. More information can be found <a href="https://marketsquare.github.io/robotframework-openapi-libcore/advanced_use.html">here</a>.</p></doc>
|
485
539
|
<shortdoc>This keyword returns a valid url for the given `endpoint` and `method`.</shortdoc>
|
486
540
|
</kw>
|
487
|
-
<kw name="Perform Validated Request" lineno="
|
541
|
+
<kw name="Perform Validated Request" lineno="1778">
|
488
542
|
<arguments repr="path: str, status_code: int, request_values: RequestValues, original_data: Dict[str, Any] | None = None">
|
489
543
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
|
490
544
|
<name>path</name>
|
@@ -513,19 +567,70 @@
|
|
513
567
|
<doc><p>This keyword first calls the Authorized Request keyword, then the Validate Response keyword and finally validates, for <span class="name">DELETE</span> operations, whether the target resource was indeed deleted (OK response) or not (error responses).</p></doc>
|
514
568
|
<shortdoc>This keyword first calls the Authorized Request keyword, then the Validate Response keyword and finally validates, for `DELETE` operations, whether the target resource was indeed deleted (OK response) or not (error responses).</shortdoc>
|
515
569
|
</kw>
|
516
|
-
<kw name="Set
|
570
|
+
<kw name="Set Auth" lineno="703">
|
571
|
+
<arguments repr="auth: AuthBase">
|
572
|
+
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="auth: AuthBase">
|
573
|
+
<name>auth</name>
|
574
|
+
<type name="AuthBase"/>
|
575
|
+
</arg>
|
576
|
+
</arguments>
|
577
|
+
<doc><p>Set the <span class="name">auth</span> used for authentication after the library is imported.</p>
|
578
|
+
<p>After calling this keyword, subsequent requests will use the provided <span class="name">auth</span> instance.</p></doc>
|
579
|
+
<shortdoc>Set the `auth` used for authentication after the library is imported.</shortdoc>
|
580
|
+
</kw>
|
581
|
+
<kw name="Set Basic Auth" lineno="691">
|
582
|
+
<arguments repr="username: str, password: str">
|
583
|
+
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="username: str">
|
584
|
+
<name>username</name>
|
585
|
+
<type name="str" typedoc="string"/>
|
586
|
+
</arg>
|
587
|
+
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="password: str">
|
588
|
+
<name>password</name>
|
589
|
+
<type name="str" typedoc="string"/>
|
590
|
+
</arg>
|
591
|
+
</arguments>
|
592
|
+
<doc><p>Set the <span class="name">username</span> and <span class="name">password</span> used for basic authentication after the library is imported.</p>
|
593
|
+
<p>After calling this keyword, subsequent requests will use the provided credentials.</p></doc>
|
594
|
+
<shortdoc>Set the `username` and `password` used for basic authentication after the library is imported.</shortdoc>
|
595
|
+
</kw>
|
596
|
+
<kw name="Set Extra Headers" lineno="713">
|
597
|
+
<arguments repr="extra_headers: Dict[str, str]">
|
598
|
+
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="extra_headers: Dict[str, str]">
|
599
|
+
<name>extra_headers</name>
|
600
|
+
<type name="Dict" typedoc="dictionary">
|
601
|
+
<type name="str" typedoc="string"/>
|
602
|
+
<type name="str" typedoc="string"/>
|
603
|
+
</type>
|
604
|
+
</arg>
|
605
|
+
</arguments>
|
606
|
+
<doc><p>Set the <span class="name">extra_headers</span> used in requests after the library is imported.</p>
|
607
|
+
<p>After calling this keyword, subsequent requests will use the provided <span class="name">extra_headers</span>.</p></doc>
|
608
|
+
<shortdoc>Set the `extra_headers` used in requests after the library is imported.</shortdoc>
|
609
|
+
</kw>
|
610
|
+
<kw name="Set Origin" lineno="668">
|
517
611
|
<arguments repr="origin: str">
|
518
612
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="origin: str">
|
519
613
|
<name>origin</name>
|
520
614
|
<type name="str" typedoc="string"/>
|
521
615
|
</arg>
|
522
616
|
</arguments>
|
523
|
-
<doc><p>
|
617
|
+
<doc><p>Set the <span class="name">origin</span> after the library is imported.</p>
|
524
618
|
<p>This can be done during the <span class="name">Suite setup</span> when using DataDriver in situations where the OpenAPI document is available on disk but the target host address is not known before the test starts.</p>
|
525
619
|
<p>In combination with OpenApiLibCore, the <span class="name">origin</span> can be used at any point to target another server that hosts an API that complies to the same OAS.</p></doc>
|
526
|
-
<shortdoc>
|
620
|
+
<shortdoc>Set the `origin` after the library is imported.</shortdoc>
|
527
621
|
</kw>
|
528
|
-
<kw name="
|
622
|
+
<kw name="Set Security Token" lineno="682">
|
623
|
+
<arguments repr="security_token: str">
|
624
|
+
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="security_token: str">
|
625
|
+
<name>security_token</name>
|
626
|
+
<type name="str" typedoc="string"/>
|
627
|
+
</arg>
|
628
|
+
</arguments>
|
629
|
+
<doc><p>Set the <span class="name">security_token</span> after the library is imported.</p>
|
630
|
+
<p>After calling this keyword, subsequent requests will use the provided token.</p></doc>
|
631
|
+
<shortdoc>Set the `security_token` after the library is imported.</shortdoc>
|
632
|
+
</kw>
|
633
|
+
<kw name="Validate Resource Properties" lineno="2019">
|
529
634
|
<arguments repr="resource: Dict[str, Any], schema: Dict[str, Any]">
|
530
635
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="resource: Dict[str, Any]">
|
531
636
|
<name>resource</name>
|
@@ -545,7 +650,7 @@
|
|
545
650
|
<doc><p>Validate that the <span class="name">resource</span> does not contain any properties that are not defined in the <span class="name">schema_properties</span>.</p></doc>
|
546
651
|
<shortdoc>Validate that the `resource` does not contain any properties that are not defined in the `schema_properties`.</shortdoc>
|
547
652
|
</kw>
|
548
|
-
<kw name="Validate Response" lineno="
|
653
|
+
<kw name="Validate Response" lineno="1852">
|
549
654
|
<arguments repr="path: str, response: Response, original_data: Dict[str, Any] | None = None">
|
550
655
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="path: str">
|
551
656
|
<name>path</name>
|
@@ -578,7 +683,7 @@
|
|
578
683
|
</ul></doc>
|
579
684
|
<shortdoc>Validate the `response` by performing the following validations: - validate the `response` against the openapi schema for the `endpoint` - validate that the response does not contain extra properties - validate that a href, if present, refers to the correct resource - validate that the value for a property that is in the response is equal to the property value that was send - validate that no `original_data` is preserved when performing a PUT operation - validate that a PATCH operation only updates the provided properties</shortdoc>
|
580
685
|
</kw>
|
581
|
-
<kw name="Validate Send Response" lineno="
|
686
|
+
<kw name="Validate Send Response" lineno="2137">
|
582
687
|
<arguments repr="response: Response, original_data: Dict[str, Any] | None = None">
|
583
688
|
<arg kind="POSITIONAL_OR_NAMED" required="true" repr="response: Response">
|
584
689
|
<name>response</name>
|
@@ -649,6 +754,7 @@
|
|
649
754
|
<usage>Get Json Data For Dto Class</usage>
|
650
755
|
<usage>Get Json Data With Conflict</usage>
|
651
756
|
<usage>Perform Validated Request</usage>
|
757
|
+
<usage>Set Extra Headers</usage>
|
652
758
|
<usage>Validate Resource Properties</usage>
|
653
759
|
<usage>Validate Response</usage>
|
654
760
|
<usage>Validate Send Response</usage>
|
@@ -681,6 +787,7 @@
|
|
681
787
|
<usage>Authorized Request</usage>
|
682
788
|
<usage>Get Invalid Json Data</usage>
|
683
789
|
<usage>Get Invalidated Parameters</usage>
|
790
|
+
<usage>Get Invalidated Url</usage>
|
684
791
|
<usage>Get Json Data With Conflict</usage>
|
685
792
|
<usage>Get Valid Id For Endpoint</usage>
|
686
793
|
<usage>Perform Validated Request</usage>
|
@@ -746,7 +853,10 @@
|
|
746
853
|
<usage>Get Valid Id For Endpoint</usage>
|
747
854
|
<usage>Get Valid Url</usage>
|
748
855
|
<usage>Perform Validated Request</usage>
|
856
|
+
<usage>Set Basic Auth</usage>
|
857
|
+
<usage>Set Extra Headers</usage>
|
749
858
|
<usage>Set Origin</usage>
|
859
|
+
<usage>Set Security Token</usage>
|
750
860
|
<usage>Validate Resource Properties</usage>
|
751
861
|
<usage>Validate Response</usage>
|
752
862
|
<usage>Validate Send Response</usage>
|
@@ -765,5 +875,20 @@
|
|
765
875
|
<usage>Get Invalidated Parameters</usage>
|
766
876
|
</usages>
|
767
877
|
</type>
|
878
|
+
<type name="ValidationLevel" type="Enum">
|
879
|
+
<doc><p>The available levels for the response_validation parameter.</p></doc>
|
880
|
+
<accepts>
|
881
|
+
<type>string</type>
|
882
|
+
</accepts>
|
883
|
+
<usages>
|
884
|
+
<usage>__init__</usage>
|
885
|
+
</usages>
|
886
|
+
<members>
|
887
|
+
<member name="DISABLED" value="DISABLED"/>
|
888
|
+
<member name="INFO" value="INFO"/>
|
889
|
+
<member name="WARN" value="WARN"/>
|
890
|
+
<member name="STRICT" value="STRICT"/>
|
891
|
+
</members>
|
892
|
+
</type>
|
768
893
|
</typedocs>
|
769
894
|
</keywordspec>
|
@@ -149,6 +149,7 @@ from openapi_core.contrib.requests import (
|
|
149
149
|
RequestsOpenAPIResponse,
|
150
150
|
)
|
151
151
|
from openapi_core.exceptions import OpenAPIError
|
152
|
+
from openapi_core.templating.paths.exceptions import ServerNotFound
|
152
153
|
from openapi_core.validation.exceptions import ValidationError
|
153
154
|
from openapi_core.validation.response.exceptions import ResponseValidationError
|
154
155
|
from openapi_core.validation.schemas.exceptions import InvalidSchemaValue
|
@@ -157,8 +158,8 @@ from prance.util.url import ResolutionError
|
|
157
158
|
from requests import Response, Session
|
158
159
|
from requests.auth import AuthBase, HTTPBasicAuth
|
159
160
|
from requests.cookies import RequestsCookieJar as CookieJar
|
160
|
-
from robot.api import Failure
|
161
161
|
from robot.api.deco import keyword, library
|
162
|
+
from robot.api.exceptions import Failure
|
162
163
|
from robot.libraries.BuiltIn import BuiltIn
|
163
164
|
|
164
165
|
from OpenApiLibCore import value_utils
|
@@ -377,7 +378,15 @@ class RequestData:
|
|
377
378
|
|
378
379
|
def get_required_properties_dict(self) -> Dict[str, Any]:
|
379
380
|
"""Get the json-compatible dto data containing only the required properties."""
|
380
|
-
|
381
|
+
relations = self.dto.get_relations()
|
382
|
+
mandatory_properties = [
|
383
|
+
relation.property_name
|
384
|
+
for relation in relations
|
385
|
+
if getattr(relation, "treat_as_mandatory", False)
|
386
|
+
]
|
387
|
+
required_properties: List[str] = self.dto_schema.get("required", [])
|
388
|
+
required_properties.extend(mandatory_properties)
|
389
|
+
|
381
390
|
required_properties_dict: Dict[str, Any] = {}
|
382
391
|
for key, value in (self.dto.as_dict()).items():
|
383
392
|
if key in required_properties:
|
@@ -413,20 +422,38 @@ class RequestData:
|
|
413
422
|
|
414
423
|
def get_required_params(self) -> Dict[str, str]:
|
415
424
|
"""Get the params dict containing only the required query parameters."""
|
425
|
+
relations = self.dto.get_parameter_relations()
|
426
|
+
mandatory_properties = [
|
427
|
+
relation.property_name
|
428
|
+
for relation in relations
|
429
|
+
if getattr(relation, "treat_as_mandatory", False)
|
430
|
+
]
|
431
|
+
mandatory_parameters = [p for p in mandatory_properties if p in self.parameters]
|
432
|
+
|
416
433
|
required_parameters = [
|
417
434
|
p.get("name") for p in self.parameters if p.get("required")
|
418
435
|
]
|
436
|
+
required_parameters.extend(mandatory_parameters)
|
419
437
|
return {k: v for k, v in self.params.items() if k in required_parameters}
|
420
438
|
|
421
439
|
def get_required_headers(self) -> Dict[str, str]:
|
422
440
|
"""Get the headers dict containing only the required headers."""
|
441
|
+
relations = self.dto.get_parameter_relations()
|
442
|
+
mandatory_properties = [
|
443
|
+
relation.property_name
|
444
|
+
for relation in relations
|
445
|
+
if getattr(relation, "treat_as_mandatory", False)
|
446
|
+
]
|
447
|
+
mandatory_parameters = [p for p in mandatory_properties if p in self.parameters]
|
448
|
+
|
423
449
|
required_parameters = [
|
424
450
|
p.get("name") for p in self.parameters if p.get("required")
|
425
451
|
]
|
452
|
+
required_parameters.extend(mandatory_parameters)
|
426
453
|
return {k: v for k, v in self.headers.items() if k in required_parameters}
|
427
454
|
|
428
455
|
|
429
|
-
@library(scope="
|
456
|
+
@library(scope="SUITE", doc_format="ROBOT")
|
430
457
|
class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
431
458
|
"""
|
432
459
|
Main class providing the keywords and core logic to interact with an OpenAPI server.
|
@@ -440,10 +467,13 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
440
467
|
source: str,
|
441
468
|
origin: str = "",
|
442
469
|
base_path: str = "",
|
470
|
+
response_validation: ValidationLevel = ValidationLevel.WARN,
|
471
|
+
disable_server_validation: bool = True,
|
443
472
|
mappings_path: Union[str, Path] = "",
|
444
473
|
invalid_property_default_response: int = 422,
|
445
474
|
default_id_property_name: str = "id",
|
446
475
|
faker_locale: Optional[Union[str, List[str]]] = None,
|
476
|
+
require_body_for_invalid_url: bool = False,
|
447
477
|
recursion_limit: int = 1,
|
448
478
|
recursion_default: Any = {},
|
449
479
|
username: str = "",
|
@@ -470,6 +500,25 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
470
500
|
section in the openapi document.
|
471
501
|
E.g. ``/petshop/v2``.
|
472
502
|
|
503
|
+
== Test case execution ==
|
504
|
+
|
505
|
+
=== response_validation ===
|
506
|
+
By default, a ``WARN`` is logged when the Response received after a Request does not
|
507
|
+
comply with the schema as defined in the openapi document for the given operation. The
|
508
|
+
following values are supported:
|
509
|
+
|
510
|
+
- ``DISABLED``: All Response validation errors will be ignored
|
511
|
+
- ``INFO``: Any Response validation erros will be logged at ``INFO`` level
|
512
|
+
- ``WARN``: Any Response validation erros will be logged at ``WARN`` level
|
513
|
+
- ``STRICT``: The Test Case will fail on any Response validation errors
|
514
|
+
|
515
|
+
=== disable_server_validation ===
|
516
|
+
If enabled by setting this parameter to ``True``, the Response validation will also
|
517
|
+
include possible errors for Requests made to a server address that is not defined in
|
518
|
+
the list of servers in the openapi document. This generally means that if there is a
|
519
|
+
mismatch, every Test Case will raise this error. Note that ``localhost`` and
|
520
|
+
``127.0.0.1`` are not considered the same by Response validation.
|
521
|
+
|
473
522
|
== API-specific configurations ==
|
474
523
|
|
475
524
|
=== mappings_path ===
|
@@ -496,6 +545,14 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
496
545
|
A locale string or list of locale strings to pass to the Faker library to be
|
497
546
|
used in generation of string data for supported format types.
|
498
547
|
|
548
|
+
=== require_body_for_invalid_url ===
|
549
|
+
When a request is made against an invalid url, this usually is because of a "404" request;
|
550
|
+
a request for a resource that does not exist. Depending on API implementation, when a
|
551
|
+
request with a missing or invalid request body is made on a non-existent resource,
|
552
|
+
either a 404 or a 422 or 400 Response is normally returned. If the API being tested
|
553
|
+
processes the request body before checking if the requested resource exists, set
|
554
|
+
this parameter to True.
|
555
|
+
|
499
556
|
== Parsing parameters ==
|
500
557
|
|
501
558
|
=== recursion_limit ===
|
@@ -552,6 +609,8 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
552
609
|
self._source = source
|
553
610
|
self._origin = origin
|
554
611
|
self._base_path = base_path
|
612
|
+
self.response_validation = response_validation
|
613
|
+
self.disable_server_validation = disable_server_validation
|
555
614
|
self._recursion_limit = recursion_limit
|
556
615
|
self._recursion_default = recursion_default
|
557
616
|
self.session = Session()
|
@@ -559,7 +618,7 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
559
618
|
# if multiple are provided, username and password take precedence
|
560
619
|
self.security_token = security_token
|
561
620
|
self.auth = auth
|
562
|
-
if username
|
621
|
+
if username:
|
563
622
|
self.auth = HTTPBasicAuth(username, password)
|
564
623
|
# Robot Framework does not allow users to create tuples and requests
|
565
624
|
# does not accept lists, so perform the conversion here
|
@@ -596,8 +655,10 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
596
655
|
)
|
597
656
|
if faker_locale:
|
598
657
|
FAKE.set_locale(locale=faker_locale)
|
658
|
+
self.require_body_for_invalid_url = require_body_for_invalid_url
|
599
659
|
# update the globally available DEFAULT_ID_PROPERTY_NAME to the provided value
|
600
660
|
DEFAULT_ID_PROPERTY_NAME.id_property_name = default_id_property_name
|
661
|
+
self._server_validation_warning_logged = False
|
601
662
|
|
602
663
|
@property
|
603
664
|
def origin(self) -> str:
|
@@ -606,7 +667,7 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
606
667
|
@keyword
|
607
668
|
def set_origin(self, origin: str) -> None:
|
608
669
|
"""
|
609
|
-
|
670
|
+
Set the `origin` after the library is imported.
|
610
671
|
|
611
672
|
This can be done during the `Suite setup` when using DataDriver in situations
|
612
673
|
where the OpenAPI document is available on disk but the target host address is
|
@@ -617,6 +678,47 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
617
678
|
"""
|
618
679
|
self._origin = origin
|
619
680
|
|
681
|
+
@keyword
|
682
|
+
def set_security_token(self, security_token: str) -> None:
|
683
|
+
"""
|
684
|
+
Set the `security_token` after the library is imported.
|
685
|
+
|
686
|
+
After calling this keyword, subsequent requests will use the provided token.
|
687
|
+
"""
|
688
|
+
self.security_token = security_token
|
689
|
+
|
690
|
+
@keyword
|
691
|
+
def set_basic_auth(self, username: str, password: str) -> None:
|
692
|
+
"""
|
693
|
+
Set the `username` and `password` used for basic
|
694
|
+
authentication after the library is imported.
|
695
|
+
|
696
|
+
After calling this keyword, subsequent requests
|
697
|
+
will use the provided credentials.
|
698
|
+
"""
|
699
|
+
if username:
|
700
|
+
self.auth = HTTPBasicAuth(username, password)
|
701
|
+
|
702
|
+
@keyword
|
703
|
+
def set_auth(self, auth: AuthBase) -> None:
|
704
|
+
"""
|
705
|
+
Set the `auth` used for authentication after the library is imported.
|
706
|
+
|
707
|
+
After calling this keyword, subsequent requests
|
708
|
+
will use the provided `auth` instance.
|
709
|
+
"""
|
710
|
+
self.auth = auth
|
711
|
+
|
712
|
+
@keyword
|
713
|
+
def set_extra_headers(self, extra_headers: Dict[str, str]) -> None:
|
714
|
+
"""
|
715
|
+
Set the `extra_headers` used in requests after the library is imported.
|
716
|
+
|
717
|
+
After calling this keyword, subsequent requests
|
718
|
+
will use the provided `extra_headers`.
|
719
|
+
"""
|
720
|
+
self.extra_headers = extra_headers
|
721
|
+
|
620
722
|
@property
|
621
723
|
def base_url(self) -> str:
|
622
724
|
return f"{self.origin}{self._base_path}"
|
@@ -1251,13 +1353,32 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1251
1353
|
return json_data
|
1252
1354
|
|
1253
1355
|
@keyword
|
1254
|
-
def get_invalidated_url(
|
1356
|
+
def get_invalidated_url(
|
1357
|
+
self,
|
1358
|
+
valid_url: str,
|
1359
|
+
path: str = "",
|
1360
|
+
method: str = "",
|
1361
|
+
expected_status_code: int = 404,
|
1362
|
+
) -> Optional[str]:
|
1255
1363
|
"""
|
1256
1364
|
Return an url with all the path parameters in the `valid_url` replaced by a
|
1257
|
-
random UUID
|
1365
|
+
random UUID if no PathPropertiesConstraint is mapped for the `path`, `method`
|
1366
|
+
and `expected_status_code`.
|
1367
|
+
If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.
|
1258
1368
|
|
1259
1369
|
Raises ValueError if the valid_url cannot be invalidated.
|
1260
1370
|
"""
|
1371
|
+
dto_class = self.get_dto_class(endpoint=path, method=method)
|
1372
|
+
relations = dto_class.get_relations()
|
1373
|
+
paths = [
|
1374
|
+
p.invalid_value
|
1375
|
+
for p in relations
|
1376
|
+
if isinstance(p, PathPropertiesConstraint)
|
1377
|
+
and p.invalid_value_error_code == expected_status_code
|
1378
|
+
]
|
1379
|
+
if paths:
|
1380
|
+
url = f"{self.base_url}{choice(paths)}"
|
1381
|
+
return url
|
1261
1382
|
parameterized_endpoint = self.get_parameterized_endpoint_from_url(valid_url)
|
1262
1383
|
parameterized_url = self.base_url + parameterized_endpoint
|
1263
1384
|
valid_url_parts = list(reversed(valid_url.split("/")))
|
@@ -1301,6 +1422,9 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1301
1422
|
"""
|
1302
1423
|
method = method.lower()
|
1303
1424
|
data_relations = request_data.dto.get_relations_for_error_code(status_code)
|
1425
|
+
data_relations = [
|
1426
|
+
r for r in data_relations if not isinstance(r, PathPropertiesConstraint)
|
1427
|
+
]
|
1304
1428
|
if not data_relations:
|
1305
1429
|
if not request_data.dto_schema:
|
1306
1430
|
raise ValueError(
|
@@ -1611,10 +1735,17 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1611
1735
|
params: Optional[Dict[str, Any]] = None,
|
1612
1736
|
headers: Optional[Dict[str, str]] = None,
|
1613
1737
|
json_data: Optional[JSON] = None,
|
1738
|
+
data: Any = None,
|
1739
|
+
files: Any = None,
|
1614
1740
|
) -> Response:
|
1615
1741
|
"""
|
1616
1742
|
Perform a request using the security token or authentication set in the library.
|
1617
1743
|
|
1744
|
+
`json_data`, `data` and `files` are passed to `requests.request`s `json`,
|
1745
|
+
`data` and `files` parameters unaltered.
|
1746
|
+
See the requests documentation for details:
|
1747
|
+
https://requests.readthedocs.io/en/latest/api/#requests.request
|
1748
|
+
|
1618
1749
|
> Note: provided username / password or auth objects take precedence over token
|
1619
1750
|
based security
|
1620
1751
|
"""
|
@@ -1632,6 +1763,8 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1632
1763
|
params=params,
|
1633
1764
|
headers=headers,
|
1634
1765
|
json=json_data,
|
1766
|
+
data=data,
|
1767
|
+
files=files,
|
1635
1768
|
cookies=self.cookies,
|
1636
1769
|
auth=self.auth,
|
1637
1770
|
proxies=self.proxies,
|
@@ -1788,6 +1921,11 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1788
1921
|
response_spec["content"][content_type]["schema"]
|
1789
1922
|
)
|
1790
1923
|
|
1924
|
+
response_types = response_schema.get("types")
|
1925
|
+
if response_types:
|
1926
|
+
# In case of oneOf / anyOf there can be multiple possible response types
|
1927
|
+
# which makes generic validation too complex
|
1928
|
+
return None
|
1791
1929
|
response_type = response_schema.get("type", "undefined")
|
1792
1930
|
if response_type not in ["object", "array"]:
|
1793
1931
|
self._validate_value_type(value=json_response, expected_type=response_type)
|
@@ -1841,7 +1979,7 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1841
1979
|
request=RequestsOpenAPIRequest(response.request),
|
1842
1980
|
response=RequestsOpenAPIResponse(response),
|
1843
1981
|
)
|
1844
|
-
except ResponseValidationError as exception:
|
1982
|
+
except (ResponseValidationError, ServerNotFound) as exception:
|
1845
1983
|
errors: List[InvalidSchemaValue] = exception.__cause__
|
1846
1984
|
validation_errors: Optional[List[ValidationError]] = getattr(
|
1847
1985
|
errors, "schema_errors", None
|
@@ -1856,6 +1994,16 @@ class OpenApiLibCore: # pylint: disable=too-many-instance-attributes
|
|
1856
1994
|
else:
|
1857
1995
|
error_message = str(exception)
|
1858
1996
|
|
1997
|
+
if isinstance(exception, ServerNotFound):
|
1998
|
+
if not self._server_validation_warning_logged:
|
1999
|
+
logger.warning(
|
2000
|
+
f"ServerNotFound was raised during response validation. "
|
2001
|
+
f"Due to this, no full response validation will be performed."
|
2002
|
+
f"\nThe original error was: {error_message}"
|
2003
|
+
)
|
2004
|
+
self._server_validation_warning_logged = True
|
2005
|
+
if self.disable_server_validation:
|
2006
|
+
return
|
1859
2007
|
if response.status_code == self.invalid_property_default_response:
|
1860
2008
|
logger.debug(error_message)
|
1861
2009
|
return
|
{robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: robotframework-openapitools
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A set of Robot Framework libraries to test APIs for which the OAS is available.
|
5
5
|
Home-page: https://github.com/MarketSquare/robotframework-openapitools
|
6
6
|
License: Apache-2.0
|
@@ -18,18 +18,19 @@ Classifier: Programming Language :: Python :: 3.9
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.10
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
21
22
|
Classifier: Topic :: Software Development :: Testing
|
22
23
|
Classifier: Topic :: Software Development :: Testing :: Acceptance
|
23
24
|
Requires-Dist: Faker (>=23.1.0)
|
24
|
-
Requires-Dist: Jinja2 (>=3.1.2
|
25
|
+
Requires-Dist: Jinja2 (>=3.1.2)
|
25
26
|
Requires-Dist: black (>=24.1.0)
|
26
|
-
Requires-Dist: openapi-core (>=0.19.0
|
27
|
-
Requires-Dist: prance[cli] (>=23
|
28
|
-
Requires-Dist: requests (>=2.31.0
|
29
|
-
Requires-Dist: rich_click (>=1.7.0
|
27
|
+
Requires-Dist: openapi-core (>=0.19.0)
|
28
|
+
Requires-Dist: prance[cli] (>=23)
|
29
|
+
Requires-Dist: requests (>=2.31.0)
|
30
|
+
Requires-Dist: rich_click (>=1.7.0)
|
30
31
|
Requires-Dist: robotframework (>=6.0.0,!=7.0.0)
|
31
32
|
Requires-Dist: robotframework-datadriver (>=1.10.0)
|
32
|
-
Requires-Dist: rstr (>=3.2.0
|
33
|
+
Requires-Dist: rstr (>=3.2.0)
|
33
34
|
Description-Content-Type: text/markdown
|
34
35
|
|
35
36
|
# OpenApiTools for Robot Framework
|
{robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/RECORD
RENAMED
@@ -1,15 +1,15 @@
|
|
1
1
|
OpenApiDriver/__init__.py,sha256=34h5RkB8nBNRKPId4r_B_xzcgXJYD3m2QYwIaPfpv80,1331
|
2
|
-
OpenApiDriver/openapi_executors.py,sha256=
|
2
|
+
OpenApiDriver/openapi_executors.py,sha256=L_oixhT1Ux6ZOTz0-iQEBos_Qz1kkRc3W_YjI-A-VuM,12434
|
3
3
|
OpenApiDriver/openapi_reader.py,sha256=4kSM-hFd54ws-jq88inbienkaoC5sSDwfRuxLrHP7aA,4652
|
4
|
-
OpenApiDriver/openapidriver.libspec,sha256=
|
5
|
-
OpenApiDriver/openapidriver.py,sha256=
|
4
|
+
OpenApiDriver/openapidriver.libspec,sha256=5Wleyi6EM42z4_Wzg38L5YGUPNRBjGw-XU0ZaYut4ow,27181
|
5
|
+
OpenApiDriver/openapidriver.py,sha256=HD2t32pU0RokKbf2VopjxlArIVoh_-fLcwiJmfQ2MJM,15209
|
6
6
|
OpenApiDriver/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
OpenApiLibCore/__init__.py,sha256=CGZRj3Vh4TZ76LOLWJOrBFr27QYWVXsO1imLqH-aP9E,1604
|
8
|
-
OpenApiLibCore/dto_base.py,sha256=
|
8
|
+
OpenApiLibCore/dto_base.py,sha256=GE_vXY0bJsDfOyQFahRy9jVgFR6BbWDmyrAegl1NQS4,12299
|
9
9
|
OpenApiLibCore/dto_utils.py,sha256=maYX9QqPaJtiEo8vIFQLsT2FOT-LUGVnNcVaca4zCDk,2903
|
10
10
|
OpenApiLibCore/oas_cache.py,sha256=Qg_Is5pAJZjZu5VmwEEarQs8teKrh3Y2psCcpDLU5Y4,379
|
11
|
-
OpenApiLibCore/openapi_libcore.libspec,sha256=
|
12
|
-
OpenApiLibCore/openapi_libcore.py,sha256=
|
11
|
+
OpenApiLibCore/openapi_libcore.libspec,sha256=o3_sXNjA3QSr-sPH6Ai5mGPg0MFToLJ5GnTWqZWajfc,47001
|
12
|
+
OpenApiLibCore/openapi_libcore.py,sha256=zRk8QMc22wFLqpLQQOac1aZgoF_IDQdZdRkrBtfqCF0,93211
|
13
13
|
OpenApiLibCore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
OpenApiLibCore/value_utils.py,sha256=wO5ssYTuk4YWWMJb0kl5n20X2r0NG55FXvgLqQd8qvw,18145
|
15
15
|
roboswag/__init__.py,sha256=-8ql4wuY_ftOZRdCxSmBCMW3WpDk7Ir__st9xcb9qCI,223
|
@@ -35,7 +35,7 @@ roboswag/validate/__init__.py,sha256=stpgQmvZvqlqPBjZ3Vxhd3wbX_Nb85jyIbj44_EhK_w
|
|
35
35
|
roboswag/validate/core.py,sha256=CfUEhkXPFAzIppRSiGyh62j4BYW4vkjIXWEzRcJFD6o,84
|
36
36
|
roboswag/validate/schema.py,sha256=jyD44GcYU_JQLw5hb1wK-DwxOsbJ-FstoNHwIVVMqoo,711
|
37
37
|
roboswag/validate/text_response.py,sha256=P7WEC6ot1OG3YDEXRtmOwIFwki8jgq8fMb-L77X4vIo,527
|
38
|
-
robotframework_openapitools-0.
|
39
|
-
robotframework_openapitools-0.
|
40
|
-
robotframework_openapitools-0.
|
41
|
-
robotframework_openapitools-0.
|
38
|
+
robotframework_openapitools-0.4.0.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
39
|
+
robotframework_openapitools-0.4.0.dist-info/METADATA,sha256=gebYOaq4qzFePksuD1iKi6kdc7nYGv9nFuwwWc7IAwc,1621
|
40
|
+
robotframework_openapitools-0.4.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
41
|
+
robotframework_openapitools-0.4.0.dist-info/RECORD,,
|
{robotframework_openapitools-0.2.3.dist-info → robotframework_openapitools-0.4.0.dist-info}/LICENSE
RENAMED
File without changes
|