schemathesis 3.34.1__py3-none-any.whl → 3.34.3__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.
- schemathesis/specs/openapi/checks.py +9 -44
- schemathesis/specs/openapi/stateful/__init__.py +23 -6
- schemathesis/transports/__init__.py +1 -0
- {schemathesis-3.34.1.dist-info → schemathesis-3.34.3.dist-info}/METADATA +30 -19
- {schemathesis-3.34.1.dist-info → schemathesis-3.34.3.dist-info}/RECORD +8 -8
- {schemathesis-3.34.1.dist-info → schemathesis-3.34.3.dist-info}/WHEEL +1 -1
- {schemathesis-3.34.1.dist-info → schemathesis-3.34.3.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.34.1.dist-info → schemathesis-3.34.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from http.cookies import SimpleCookie
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Dict, Generator, NoReturn, cast
|
|
6
|
-
from urllib.parse import parse_qs,
|
|
6
|
+
from urllib.parse import parse_qs, urlparse
|
|
7
7
|
|
|
8
8
|
from ... import failures
|
|
9
9
|
from ...exceptions import (
|
|
@@ -334,8 +334,6 @@ def ensure_resource_availability(response: GenericResponse, original: Case) -> b
|
|
|
334
334
|
|
|
335
335
|
def ignored_auth(response: GenericResponse, case: Case) -> bool | None:
|
|
336
336
|
"""Check if an operation declares authentication as a requirement but does not actually enforce it."""
|
|
337
|
-
from requests import Session
|
|
338
|
-
|
|
339
337
|
from .schemas import BaseOpenAPISchema
|
|
340
338
|
|
|
341
339
|
if not isinstance(case.operation.schema, BaseOpenAPISchema):
|
|
@@ -346,14 +344,15 @@ def ignored_auth(response: GenericResponse, case: Case) -> bool | None:
|
|
|
346
344
|
if security_parameters and 200 <= response.status_code < 300:
|
|
347
345
|
if _contains_auth(response.request, security_parameters):
|
|
348
346
|
# If there is auth in the request, then drop it and retry the call
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
new_response
|
|
352
|
-
if new_response.ok:
|
|
347
|
+
_remove_auth_from_case(case, security_parameters)
|
|
348
|
+
new_response = case.operation.schema.transport.send(case)
|
|
349
|
+
if 200 <= new_response.status_code < 300:
|
|
353
350
|
# Mutate the response object in place on the best effort basis
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
351
|
+
if hasattr(response, "__attrs__"):
|
|
352
|
+
for attribute in new_response.__attrs__:
|
|
353
|
+
setattr(response, attribute, getattr(new_response, attribute))
|
|
354
|
+
else:
|
|
355
|
+
response.__dict__.update(new_response.__dict__)
|
|
357
356
|
_raise_auth_error(new_response, case.operation.verbose_name)
|
|
358
357
|
else:
|
|
359
358
|
# Successful response when there is no auth
|
|
@@ -417,40 +416,6 @@ def _contains_auth(request: PreparedRequest, security_parameters: list[SecurityP
|
|
|
417
416
|
return False
|
|
418
417
|
|
|
419
418
|
|
|
420
|
-
def _remove_auth_from_request(
|
|
421
|
-
request: PreparedRequest, security_parameters: list[SecurityParameter]
|
|
422
|
-
) -> PreparedRequest:
|
|
423
|
-
"""Remove security parameters from a request."""
|
|
424
|
-
from requests.cookies import get_cookie_header
|
|
425
|
-
|
|
426
|
-
request = request.copy()
|
|
427
|
-
parsed = urlparse(request.url)
|
|
428
|
-
query = parse_qs(parsed.query) # type: ignore
|
|
429
|
-
should_replace_url = False
|
|
430
|
-
|
|
431
|
-
for parameter in security_parameters:
|
|
432
|
-
name = parameter["name"]
|
|
433
|
-
if parameter["in"] == "header":
|
|
434
|
-
request.headers.pop(name, None)
|
|
435
|
-
if parameter["in"] == "query":
|
|
436
|
-
query.pop(name, None)
|
|
437
|
-
should_replace_url = True
|
|
438
|
-
if parameter["in"] == "cookie":
|
|
439
|
-
del request._cookies[name] # type: ignore
|
|
440
|
-
|
|
441
|
-
if should_replace_url:
|
|
442
|
-
components = [parsed.scheme, parsed.netloc, parsed.path, parsed.params, urlencode(query), parsed.fragment]
|
|
443
|
-
url = cast(str, urlunparse(components)) # type: ignore
|
|
444
|
-
request.url = url
|
|
445
|
-
# Re-generate the `Cookie` header if needed
|
|
446
|
-
raw_cookie = request.headers.pop("Cookie", None)
|
|
447
|
-
if raw_cookie is not None:
|
|
448
|
-
new_cookie_header = get_cookie_header(request._cookies, request) # type: ignore
|
|
449
|
-
if new_cookie_header:
|
|
450
|
-
request.headers["Cookie"] = new_cookie_header
|
|
451
|
-
return request
|
|
452
|
-
|
|
453
|
-
|
|
454
419
|
def _remove_auth_from_case(case: Case, security_parameters: list[SecurityParameter]) -> None:
|
|
455
420
|
"""Remove security parameters from a generated case.
|
|
456
421
|
|
|
@@ -8,6 +8,7 @@ from hypothesis import strategies as st
|
|
|
8
8
|
from hypothesis.stateful import Bundle, Rule, precondition, rule
|
|
9
9
|
|
|
10
10
|
from ....constants import NOT_SET
|
|
11
|
+
from ....generation import DataGenerationMethod
|
|
11
12
|
from ....internal.result import Ok
|
|
12
13
|
from ....stateful.state_machine import APIStateMachine, Direction, StepResult
|
|
13
14
|
from ....types import NotSet
|
|
@@ -43,6 +44,10 @@ class OpenAPIStateMachine(APIStateMachine):
|
|
|
43
44
|
return "\n".join(item.line for item in cls._transition_stats_template.iter_with_format())
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
# The proportion of negative tests generated for "root" transitions
|
|
48
|
+
NEGATIVE_TEST_CASES_THRESHOLD = 20
|
|
49
|
+
|
|
50
|
+
|
|
46
51
|
def create_state_machine(schema: BaseOpenAPISchema) -> type[APIStateMachine]:
|
|
47
52
|
"""Create a state machine class.
|
|
48
53
|
|
|
@@ -111,12 +116,24 @@ def create_state_machine(schema: BaseOpenAPISchema) -> type[APIStateMachine]:
|
|
|
111
116
|
# The source operation has no prerequisite, but we need to allow this rule to be executed
|
|
112
117
|
# in order to reach other transitions
|
|
113
118
|
name = _normalize_name(f"{target.verbose_name} -> X")
|
|
114
|
-
|
|
115
|
-
[
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
if len(schema.data_generation_methods) == 1:
|
|
120
|
+
case_strategy = target.as_strategy(data_generation_method=schema.data_generation_methods[0])
|
|
121
|
+
else:
|
|
122
|
+
strategies = {
|
|
123
|
+
method: target.as_strategy(data_generation_method=method)
|
|
124
|
+
for method in schema.data_generation_methods
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@st.composite # type: ignore[misc]
|
|
128
|
+
def case_strategy_factory(
|
|
129
|
+
draw: st.DrawFn, strategies: dict[DataGenerationMethod, st.SearchStrategy] = strategies
|
|
130
|
+
) -> Case:
|
|
131
|
+
if draw(st.integers(min_value=0, max_value=99)) < NEGATIVE_TEST_CASES_THRESHOLD:
|
|
132
|
+
return draw(strategies[DataGenerationMethod.negative])
|
|
133
|
+
return draw(strategies[DataGenerationMethod.positive])
|
|
134
|
+
|
|
135
|
+
case_strategy = case_strategy_factory()
|
|
136
|
+
|
|
120
137
|
rules[name] = precondition(ensure_links_followed)(
|
|
121
138
|
transition(
|
|
122
139
|
name=name,
|
|
@@ -192,6 +192,7 @@ class RequestsTransport:
|
|
|
192
192
|
context=failures.RequestTimeout(message=message, timeout=timeout),
|
|
193
193
|
) from None
|
|
194
194
|
response.verify = verify # type: ignore[attr-defined]
|
|
195
|
+
response._session = session # type: ignore[attr-defined]
|
|
195
196
|
if close_session:
|
|
196
197
|
session.close()
|
|
197
198
|
return response
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 3.34.
|
|
3
|
+
Version: 3.34.3
|
|
4
4
|
Summary: Property-based testing framework for Open API and GraphQL based apps
|
|
5
5
|
Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
|
|
6
6
|
Project-URL: Changelog, https://schemathesis.readthedocs.io/en/stable/changelog.html
|
|
@@ -56,7 +56,26 @@ Provides-Extra: cov
|
|
|
56
56
|
Requires-Dist: coverage-enable-subprocess; extra == 'cov'
|
|
57
57
|
Requires-Dist: coverage[toml]>=5.3; extra == 'cov'
|
|
58
58
|
Provides-Extra: dev
|
|
59
|
-
Requires-Dist:
|
|
59
|
+
Requires-Dist: aiohttp<4.0,>=3.9.1; extra == 'dev'
|
|
60
|
+
Requires-Dist: coverage-enable-subprocess; extra == 'dev'
|
|
61
|
+
Requires-Dist: coverage>=6; extra == 'dev'
|
|
62
|
+
Requires-Dist: coverage[toml]>=5.3; extra == 'dev'
|
|
63
|
+
Requires-Dist: fastapi>=0.86.0; extra == 'dev'
|
|
64
|
+
Requires-Dist: flask<3.0,>=2.1.1; extra == 'dev'
|
|
65
|
+
Requires-Dist: hypothesis-openapi<1,>=0.2; (python_version >= '3.10') and extra == 'dev'
|
|
66
|
+
Requires-Dist: pydantic>=1.10.2; extra == 'dev'
|
|
67
|
+
Requires-Dist: pytest-asyncio<1.0,>=0.18.0; extra == 'dev'
|
|
68
|
+
Requires-Dist: pytest-codspeed==2.2.1; extra == 'dev'
|
|
69
|
+
Requires-Dist: pytest-httpserver<2.0,>=1.0; extra == 'dev'
|
|
70
|
+
Requires-Dist: pytest-mock<4.0,>=3.7.0; extra == 'dev'
|
|
71
|
+
Requires-Dist: pytest-trio<1.0,>=0.8; extra == 'dev'
|
|
72
|
+
Requires-Dist: pytest-xdist<4.0,>=3; extra == 'dev'
|
|
73
|
+
Requires-Dist: sphinx; extra == 'dev'
|
|
74
|
+
Requires-Dist: sphinx-click; extra == 'dev'
|
|
75
|
+
Requires-Dist: sphinx-rtd-theme; extra == 'dev'
|
|
76
|
+
Requires-Dist: strawberry-graphql[fastapi]>=0.109.0; extra == 'dev'
|
|
77
|
+
Requires-Dist: syrupy<5.0,>=2; extra == 'dev'
|
|
78
|
+
Requires-Dist: trustme<1.0,>=0.9.0; extra == 'dev'
|
|
60
79
|
Provides-Extra: docs
|
|
61
80
|
Requires-Dist: sphinx; extra == 'docs'
|
|
62
81
|
Requires-Dist: sphinx-click; extra == 'docs'
|
|
@@ -66,7 +85,7 @@ Requires-Dist: aiohttp<4.0,>=3.9.1; extra == 'tests'
|
|
|
66
85
|
Requires-Dist: coverage>=6; extra == 'tests'
|
|
67
86
|
Requires-Dist: fastapi>=0.86.0; extra == 'tests'
|
|
68
87
|
Requires-Dist: flask<3.0,>=2.1.1; extra == 'tests'
|
|
69
|
-
Requires-Dist: hypothesis-openapi<1,>=0.2; python_version >= '3.10' and extra == 'tests'
|
|
88
|
+
Requires-Dist: hypothesis-openapi<1,>=0.2; (python_version >= '3.10') and extra == 'tests'
|
|
70
89
|
Requires-Dist: pydantic>=1.10.2; extra == 'tests'
|
|
71
90
|
Requires-Dist: pytest-asyncio<1.0,>=0.18.0; extra == 'tests'
|
|
72
91
|
Requires-Dist: pytest-httpserver<2.0,>=1.0; extra == 'tests'
|
|
@@ -78,10 +97,6 @@ Requires-Dist: syrupy<5.0,>=2; extra == 'tests'
|
|
|
78
97
|
Requires-Dist: trustme<1.0,>=0.9.0; extra == 'tests'
|
|
79
98
|
Description-Content-Type: text/markdown
|
|
80
99
|
|
|
81
|
-
<p align="center">
|
|
82
|
-
<em>Schemathesis: Supercharge your API testing, catch bugs, and ensure compliance</em>
|
|
83
|
-
</p>
|
|
84
|
-
|
|
85
100
|
<p align="center">
|
|
86
101
|
<a href="https://github.com/schemathesis/schemathesis/actions" target="_blank">
|
|
87
102
|
<img src="https://github.com/schemathesis/schemathesis/actions/workflows/build.yml/badge.svg" alt="Build">
|
|
@@ -98,22 +113,17 @@ Description-Content-Type: text/markdown
|
|
|
98
113
|
<a href="https://discord.gg/R9ASRAmHnA" target="_blank">
|
|
99
114
|
<img src="https://img.shields.io/discord/938139740912369755" alt="Discord">
|
|
100
115
|
</a>
|
|
116
|
+
<a href="[https://discord.gg/R9ASRAmHnA](https://schemathesis.readthedocs.io/en/stable/)" target="_blank">
|
|
117
|
+
<img src="https://readthedocs.org/projects/schemathesis/badge/?version=stable" alt="Documentation">
|
|
118
|
+
</a>
|
|
101
119
|
<a href="https://opensource.org/licenses/MIT" target="_blank">
|
|
102
120
|
<img src="https://img.shields.io/pypi/l/schemathesis.svg" alt="License">
|
|
103
121
|
</a>
|
|
104
122
|
</p>
|
|
105
123
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
**Documentation**: <a href="https://schemathesis.readthedocs.io/en/stable/" target="_blank">https://schemathesis.readthedocs.io/en/stable/ </a>
|
|
109
|
-
|
|
110
|
-
**Chat**: <a href="https://discord.gg/R9ASRAmHnA" target="_blank">https://discord.gg/R9ASRAmHnA </a>
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## What is Schemathesis?
|
|
124
|
+
## Schemathesis
|
|
115
125
|
|
|
116
|
-
Schemathesis is a tool that levels
|
|
126
|
+
Schemathesis is a tool that levels up your API testing by automating the process of finding crashes, uncovering bugs, and validating spec compliance. With Schemathesis, you can:
|
|
117
127
|
|
|
118
128
|
🎯 **Catch Hard-to-Find Bugs**
|
|
119
129
|
|
|
@@ -322,11 +332,12 @@ Schemathesis is built on top of <a href="https://hypothesis.works/" target="_bla
|
|
|
322
332
|
|
|
323
333
|
## Who's Using Schemathesis?
|
|
324
334
|
|
|
325
|
-
Schemathesis is used by a number of
|
|
335
|
+
Schemathesis is used by a number of projects and companies, including direct usage or integration into other tools:
|
|
326
336
|
|
|
327
337
|
- Abstract Machines ([Magistrala](https://github.com/absmach/magistrala))
|
|
328
338
|
- Bundesstelle für Open Data ([smard-api](https://github.com/bundesAPI/smard-api))
|
|
329
339
|
- [CheckMK](https://github.com/Checkmk/checkmk)
|
|
340
|
+
- [Chronosphere.io](https://github.com/chronosphereio/calyptia-api)
|
|
330
341
|
- HXSecurity ([DongTai](https://github.com/HXSecurity/DongTai))
|
|
331
342
|
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
|
332
343
|
- [Pixie](https://github.com/pixie-io/pixie)
|
|
@@ -100,7 +100,7 @@ schemathesis/specs/graphql/validation.py,sha256=uINIOt-2E7ZuQV2CxKzwez-7L9tDtqzM
|
|
|
100
100
|
schemathesis/specs/openapi/__init__.py,sha256=HDcx3bqpa6qWPpyMrxAbM3uTo0Lqpg-BUNZhDJSJKnw,279
|
|
101
101
|
schemathesis/specs/openapi/_cache.py,sha256=PAiAu4X_a2PQgD2lG5H3iisXdyg4SaHpU46bRZvfNkM,4320
|
|
102
102
|
schemathesis/specs/openapi/_hypothesis.py,sha256=XtC-rYiH-GHvWykdSSzPFVK7jHNNL7NtibjSEeIZDQw,24122
|
|
103
|
-
schemathesis/specs/openapi/checks.py,sha256=
|
|
103
|
+
schemathesis/specs/openapi/checks.py,sha256=LiwoL5W_qK40j-JFSc9hfM8IGSszMBUWe71YZJ5FBzw,19931
|
|
104
104
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
|
105
105
|
schemathesis/specs/openapi/converter.py,sha256=TaYgc5BBHPdkN-n0lqpbeVgLu3eL3L8Wu3y_Vo3TJaQ,2800
|
|
106
106
|
schemathesis/specs/openapi/definitions.py,sha256=Z186F0gNBSCmPg-Kk7Q-n6XxEZHIOzgUyeqixlC62XE,94058
|
|
@@ -127,7 +127,7 @@ schemathesis/specs/openapi/negative/__init__.py,sha256=gw0w_9tVQf_MY5Df3_xTZFC4r
|
|
|
127
127
|
schemathesis/specs/openapi/negative/mutations.py,sha256=lLEN0GLxvPmZBQ3tHCznDSjmZ4yQiQxspjv1UpO4Kx0,19019
|
|
128
128
|
schemathesis/specs/openapi/negative/types.py,sha256=a7buCcVxNBG6ILBM3A7oNTAX0lyDseEtZndBuej8MbI,174
|
|
129
129
|
schemathesis/specs/openapi/negative/utils.py,sha256=ozcOIuASufLqZSgnKUACjX-EOZrrkuNdXX0SDnLoGYA,168
|
|
130
|
-
schemathesis/specs/openapi/stateful/__init__.py,sha256=
|
|
130
|
+
schemathesis/specs/openapi/stateful/__init__.py,sha256=wI9WCuHW0EBTUeBaClWi48I6J63TiczBX6UiErsmbB4,9470
|
|
131
131
|
schemathesis/specs/openapi/stateful/statistic.py,sha256=EJK4NqeAYRYl1FtU9YEuTLyhGhPmks0bLoxUPuQlOvM,7443
|
|
132
132
|
schemathesis/specs/openapi/stateful/types.py,sha256=UuGcCTFvaHsqeLN9ZeUNcbjsEwmthoT3UcHfDHchOYo,419
|
|
133
133
|
schemathesis/stateful/__init__.py,sha256=gONzl3pgZ8DihjK52Wd3Ye1LeP6gnulmevHI_jEqHWI,5088
|
|
@@ -139,13 +139,13 @@ schemathesis/stateful/sink.py,sha256=xjsqJYH5WETKh5pDGlchYyjT3HcjzHEotUjvo1p0JsE
|
|
|
139
139
|
schemathesis/stateful/state_machine.py,sha256=u7PkwCjxTZZcePC7GPiOUuu4uhqUees3UuorD_0Sx_c,12938
|
|
140
140
|
schemathesis/stateful/statistic.py,sha256=xPLiCw61ofNXQicqcK_sZyLHiqiGcgQARpwd8AiRubM,487
|
|
141
141
|
schemathesis/stateful/validation.py,sha256=JtqnRzl11ZbVR8Lcr0xBJKoOXzx8VUfRz92jOnB2Smg,3605
|
|
142
|
-
schemathesis/transports/__init__.py,sha256=
|
|
142
|
+
schemathesis/transports/__init__.py,sha256=L60_GCzJiX9jFYKVft4aeZb8EmyV6e72k8K73y2zvmE,12898
|
|
143
143
|
schemathesis/transports/auth.py,sha256=yELjkEkfx4g74hNrd0Db9aFf0xDJDRIwhg2vzKOTZGg,1138
|
|
144
144
|
schemathesis/transports/content_types.py,sha256=VrcRQvF5T_TUjrCyrZcYF2LOwKfs3IrLcMtkVSp1ImI,2189
|
|
145
145
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
|
146
146
|
schemathesis/transports/responses.py,sha256=6-gvVcRK0Ho_lSydUysBNFWoJwZEiEgf6Iv-GWkQGd8,1675
|
|
147
|
-
schemathesis-3.34.
|
|
148
|
-
schemathesis-3.34.
|
|
149
|
-
schemathesis-3.34.
|
|
150
|
-
schemathesis-3.34.
|
|
151
|
-
schemathesis-3.34.
|
|
147
|
+
schemathesis-3.34.3.dist-info/METADATA,sha256=PsAgyQEs6iPW3VV5P_9r1zRAcw9WDnCsD4CbSieLWoM,19287
|
|
148
|
+
schemathesis-3.34.3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
149
|
+
schemathesis-3.34.3.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
|
150
|
+
schemathesis-3.34.3.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
|
151
|
+
schemathesis-3.34.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|