schemathesis 3.39.14__py3-none-any.whl → 3.39.16__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/__init__.py +9 -1
- schemathesis/_hypothesis.py +6 -1
- schemathesis/generation/coverage.py +37 -2
- schemathesis/specs/openapi/parameters.py +3 -0
- schemathesis/specs/openapi/references.py +4 -1
- schemathesis/specs/openapi/schemas.py +36 -21
- {schemathesis-3.39.14.dist-info → schemathesis-3.39.16.dist-info}/METADATA +1 -1
- {schemathesis-3.39.14.dist-info → schemathesis-3.39.16.dist-info}/RECORD +11 -11
- {schemathesis-3.39.14.dist-info → schemathesis-3.39.16.dist-info}/WHEEL +0 -0
- {schemathesis-3.39.14.dist-info → schemathesis-3.39.16.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.39.14.dist-info → schemathesis-3.39.16.dist-info}/licenses/LICENSE +0 -0
schemathesis/__init__.py
CHANGED
@@ -6,6 +6,7 @@ from . import auths, checks, contrib, experimental, fixups, graphql, hooks, runn
|
|
6
6
|
from ._lazy_import import lazy_import
|
7
7
|
from .constants import SCHEMATHESIS_VERSION
|
8
8
|
from .generation import DataGenerationMethod, GenerationConfig, HeaderConfig
|
9
|
+
from .hooks import HookContext
|
9
10
|
from .models import Case
|
10
11
|
from .specs import openapi
|
11
12
|
|
@@ -65,6 +66,7 @@ __all__ = [
|
|
65
66
|
"register_check",
|
66
67
|
"register_target",
|
67
68
|
"register_string_format",
|
69
|
+
"HookContext",
|
68
70
|
]
|
69
71
|
|
70
72
|
|
@@ -74,7 +76,13 @@ def _load_generic_response() -> Any:
|
|
74
76
|
return GenericResponse
|
75
77
|
|
76
78
|
|
77
|
-
|
79
|
+
def _load_base_schema() -> Any:
|
80
|
+
from .schemas import BaseSchema
|
81
|
+
|
82
|
+
return BaseSchema
|
83
|
+
|
84
|
+
|
85
|
+
_imports = {"GenericResponse": _load_generic_response, "BaseSchema": _load_base_schema}
|
78
86
|
|
79
87
|
|
80
88
|
def __getattr__(name: str) -> Any:
|
schemathesis/_hypothesis.py
CHANGED
@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Any, Callable, Generator, Mapping
|
|
11
11
|
|
12
12
|
import hypothesis
|
13
13
|
from hypothesis import Phase
|
14
|
+
from hypothesis._settings import all_settings
|
14
15
|
from hypothesis.errors import HypothesisWarning, Unsatisfiable
|
15
16
|
from hypothesis.internal.entropy import deterministic_PRNG
|
16
17
|
from jsonschema.exceptions import SchemaError
|
@@ -101,7 +102,11 @@ def create_test(
|
|
101
102
|
default = hypothesis.settings.default
|
102
103
|
wrapped_test._hypothesis_internal_use_settings = hypothesis.settings(
|
103
104
|
wrapped_test._hypothesis_internal_use_settings,
|
104
|
-
**{
|
105
|
+
**{
|
106
|
+
item: getattr(settings, item)
|
107
|
+
for item in all_settings
|
108
|
+
if getattr(settings, item) != getattr(default, item)
|
109
|
+
},
|
105
110
|
)
|
106
111
|
else:
|
107
112
|
wrapped_test = settings(wrapped_test)
|
@@ -150,7 +150,7 @@ class CoverageContext:
|
|
150
150
|
|
151
151
|
def is_valid_for_location(self, value: Any) -> bool:
|
152
152
|
if self.location in ("header", "cookie") and isinstance(value, str):
|
153
|
-
return is_latin_1_encodable(value) and not has_invalid_characters("", value)
|
153
|
+
return not value or (is_latin_1_encodable(value) and not has_invalid_characters("", value))
|
154
154
|
return True
|
155
155
|
|
156
156
|
def generate_from(self, strategy: st.SearchStrategy) -> Any:
|
@@ -437,6 +437,36 @@ def cover_schema_iter(
|
|
437
437
|
elif key == "required":
|
438
438
|
template = template or ctx.generate_from_schema(_get_template_schema(schema, "object"))
|
439
439
|
yield from _negative_required(ctx, template, value)
|
440
|
+
elif key == "maxItems" and isinstance(value, int) and value < BUFFER_SIZE:
|
441
|
+
try:
|
442
|
+
# Force the array to have one more item than allowed
|
443
|
+
new_schema = {**schema, "minItems": value + 1, "maxItems": value + 1, "type": "array"}
|
444
|
+
array_value = ctx.generate_from_schema(new_schema)
|
445
|
+
k = _to_hashable_key(array_value)
|
446
|
+
if k not in seen:
|
447
|
+
yield NegativeValue(
|
448
|
+
array_value,
|
449
|
+
description="Array with more items than allowed by maxItems",
|
450
|
+
location=ctx.current_path,
|
451
|
+
)
|
452
|
+
seen.add(k)
|
453
|
+
except (InvalidArgument, Unsatisfiable):
|
454
|
+
pass
|
455
|
+
elif key == "minItems" and isinstance(value, int) and value > 0:
|
456
|
+
try:
|
457
|
+
# Force the array to have one less item than the minimum
|
458
|
+
new_schema = {**schema, "minItems": value - 1, "maxItems": value - 1, "type": "array"}
|
459
|
+
array_value = ctx.generate_from_schema(new_schema)
|
460
|
+
k = _to_hashable_key(array_value)
|
461
|
+
if k not in seen:
|
462
|
+
yield NegativeValue(
|
463
|
+
array_value,
|
464
|
+
description="Array with fewer items than allowed by minItems",
|
465
|
+
location=ctx.current_path,
|
466
|
+
)
|
467
|
+
seen.add(k)
|
468
|
+
except (InvalidArgument, Unsatisfiable):
|
469
|
+
pass
|
440
470
|
elif (
|
441
471
|
key == "additionalProperties"
|
442
472
|
and not value
|
@@ -770,7 +800,12 @@ def _negative_enum(
|
|
770
800
|
_hashed = _to_hashable_key(x)
|
771
801
|
return _hashed not in seen
|
772
802
|
|
773
|
-
strategy = (
|
803
|
+
strategy = (
|
804
|
+
st.text(alphabet=st.characters(min_codepoint=65, max_codepoint=122, categories=["L"]), min_size=3)
|
805
|
+
| st.none()
|
806
|
+
| st.booleans()
|
807
|
+
| NUMERIC_STRATEGY
|
808
|
+
).filter(is_not_in_value)
|
774
809
|
value = ctx.generate_from(strategy)
|
775
810
|
yield NegativeValue(value, description="Invalid enum value", location=ctx.current_path)
|
776
811
|
hashed = _to_hashable_key(value)
|
@@ -132,6 +132,7 @@ class OpenAPI20Parameter(OpenAPIParameter):
|
|
132
132
|
"multipleOf",
|
133
133
|
"example",
|
134
134
|
"examples",
|
135
|
+
"default",
|
135
136
|
)
|
136
137
|
|
137
138
|
|
@@ -176,6 +177,7 @@ class OpenAPI30Parameter(OpenAPIParameter):
|
|
176
177
|
"format",
|
177
178
|
"example",
|
178
179
|
"examples",
|
180
|
+
"default",
|
179
181
|
)
|
180
182
|
|
181
183
|
def from_open_api_to_json_schema(self, operation: APIOperation, open_api_schema: dict[str, Any]) -> dict[str, Any]:
|
@@ -228,6 +230,7 @@ class OpenAPI20Body(OpenAPIBody, OpenAPI20Parameter):
|
|
228
230
|
"additionalProperties",
|
229
231
|
"example",
|
230
232
|
"examples",
|
233
|
+
"default",
|
231
234
|
)
|
232
235
|
# NOTE. For Open API 2.0 bodies, we still give `x-example` precedence over the schema-level `example` field to keep
|
233
236
|
# the precedence rules consistent.
|
@@ -141,7 +141,10 @@ class ConvertingResolver(InliningResolver):
|
|
141
141
|
def resolve(self, ref: str) -> tuple[str, Any]:
|
142
142
|
url, document = super().resolve(ref)
|
143
143
|
document = to_json_schema_recursive(
|
144
|
-
document,
|
144
|
+
document,
|
145
|
+
nullable_name=self.nullable_name,
|
146
|
+
is_response_schema=self.is_response_schema,
|
147
|
+
update_quantifiers=False,
|
145
148
|
)
|
146
149
|
return url, document
|
147
150
|
|
@@ -1013,24 +1013,30 @@ class SwaggerV20(BaseOpenAPISchema):
|
|
1013
1013
|
content_types = self.get_request_payload_content_types(operation)
|
1014
1014
|
is_multipart = "multipart/form-data" in content_types
|
1015
1015
|
|
1016
|
-
|
1017
|
-
if isinstance(file_value, list):
|
1018
|
-
for item in file_value:
|
1019
|
-
files.append((name, (None, item)))
|
1020
|
-
else:
|
1021
|
-
files.append((name, file_value))
|
1016
|
+
known_fields: dict[str, dict] = {}
|
1022
1017
|
|
1023
1018
|
for parameter in operation.body:
|
1024
1019
|
if isinstance(parameter, OpenAPI20CompositeBody):
|
1025
1020
|
for form_parameter in parameter.definition:
|
1026
|
-
name = form_parameter.
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1021
|
+
known_fields[form_parameter.name] = form_parameter.definition
|
1022
|
+
|
1023
|
+
def add_file(name: str, value: Any) -> None:
|
1024
|
+
if isinstance(value, list):
|
1025
|
+
for item in value:
|
1026
|
+
files.append((name, (None, item)))
|
1027
|
+
else:
|
1028
|
+
files.append((name, value))
|
1029
|
+
|
1030
|
+
for name, value in form_data.items():
|
1031
|
+
param_def = known_fields.get(name)
|
1032
|
+
if param_def:
|
1033
|
+
if param_def.get("type") == "file" or is_multipart:
|
1034
|
+
add_file(name, value)
|
1035
|
+
else:
|
1036
|
+
data[name] = value
|
1037
|
+
else:
|
1038
|
+
# Unknown field — treat it as a file (safe default under multipart/form-data)
|
1039
|
+
add_file(name, value)
|
1034
1040
|
# `None` is the default value for `files` and `data` arguments in `requests.request`
|
1035
1041
|
return files or None, data or None
|
1036
1042
|
|
@@ -1175,7 +1181,11 @@ class OpenApi30(SwaggerV20):
|
|
1175
1181
|
files = []
|
1176
1182
|
definition = operation.definition.raw
|
1177
1183
|
if "$ref" in definition["requestBody"]:
|
1178
|
-
|
1184
|
+
self.resolver.push_scope(operation.definition.scope)
|
1185
|
+
try:
|
1186
|
+
body = self.resolver.resolve_all(definition["requestBody"], RECURSION_DEPTH_LIMIT)
|
1187
|
+
finally:
|
1188
|
+
self.resolver.pop_scope()
|
1179
1189
|
else:
|
1180
1190
|
body = definition["requestBody"]
|
1181
1191
|
content = body["content"]
|
@@ -1188,14 +1198,19 @@ class OpenApi30(SwaggerV20):
|
|
1188
1198
|
break
|
1189
1199
|
else:
|
1190
1200
|
raise InternalError("No 'multipart/form-data' media type found in the schema")
|
1191
|
-
for name,
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1201
|
+
for name, value in form_data.items():
|
1202
|
+
property_schema = (schema or {}).get("properties", {}).get(name)
|
1203
|
+
if property_schema:
|
1204
|
+
if isinstance(value, list):
|
1205
|
+
files.extend([(name, item) for item in value])
|
1195
1206
|
elif property_schema.get("format") in ("binary", "base64"):
|
1196
|
-
files.append((name,
|
1207
|
+
files.append((name, value))
|
1197
1208
|
else:
|
1198
|
-
files.append((name, (None,
|
1209
|
+
files.append((name, (None, value)))
|
1210
|
+
elif isinstance(value, list):
|
1211
|
+
files.extend([(name, item) for item in value])
|
1212
|
+
else:
|
1213
|
+
files.append((name, (None, value)))
|
1199
1214
|
# `None` is the default value for `files` and `data` arguments in `requests.request`
|
1200
1215
|
return files or None, None
|
1201
1216
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemathesis
|
3
|
-
Version: 3.39.
|
3
|
+
Version: 3.39.16
|
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
|
@@ -1,7 +1,7 @@
|
|
1
|
-
schemathesis/__init__.py,sha256=
|
1
|
+
schemathesis/__init__.py,sha256=AGpMI329waZiHwU05CW7cB1cxqQsXvRPB5vs4N7ktB4,2090
|
2
2
|
schemathesis/_compat.py,sha256=y4RZd59i2NCnZ91VQhnKeMn_8t3SgvLOk2Xm8nymUHY,1837
|
3
3
|
schemathesis/_dependency_versions.py,sha256=pjEkkGAfOQJYNb-9UOo84V8nj_lKHr_TGDVdFwY2UU0,816
|
4
|
-
schemathesis/_hypothesis.py,sha256=
|
4
|
+
schemathesis/_hypothesis.py,sha256=L0cCrVk4NBI4Tn9pRIQe_URPXsJCn5Ooay0VHbdj5z8,30899
|
5
5
|
schemathesis/_lazy_import.py,sha256=aMhWYgbU2JOltyWBb32vnWBb6kykOghucEzI_F70yVE,470
|
6
6
|
schemathesis/_override.py,sha256=TAjYB3eJQmlw9K_xiR9ptt9Wj7if4U7UFlUhGjpBAoM,1625
|
7
7
|
schemathesis/_patches.py,sha256=Hsbpn4UVeXUQD2Kllrbq01CSWsTYENWa0VJTyhX5C2k,895
|
@@ -61,7 +61,7 @@ schemathesis/fixups/utf8_bom.py,sha256=lWT9RNmJG8i-l5AXIpaCT3qCPUwRgzXPW3eoOjmZE
|
|
61
61
|
schemathesis/generation/__init__.py,sha256=PClFLK3bu-8Gsy71rgdD0ULMqySrzX-Um8Tan77x_5A,1628
|
62
62
|
schemathesis/generation/_hypothesis.py,sha256=74fzLPHugZgMQXerWYFAMqCAjtAXz5E4gek7Gnkhli4,1756
|
63
63
|
schemathesis/generation/_methods.py,sha256=r8oVlJ71_gXcnEhU-byw2E0R2RswQQFm8U7yGErSqbw,1204
|
64
|
-
schemathesis/generation/coverage.py,sha256=
|
64
|
+
schemathesis/generation/coverage.py,sha256=bE93UnTpp8FsILzbupgqcc-1pQL5Y2DykuIlwWUweM4,41279
|
65
65
|
schemathesis/internal/__init__.py,sha256=93HcdG3LF0BbQKbCteOsFMa1w6nXl8yTmx87QLNJOik,161
|
66
66
|
schemathesis/internal/checks.py,sha256=ZPvsPJ7gWwK0IpzBFgOMaq4L2e0yfeC8qxPrnpauVFA,2741
|
67
67
|
schemathesis/internal/copy.py,sha256=DcL56z-d69kKR_5u8mlHvjSL1UTyUKNMAwexrwHFY1s,1031
|
@@ -116,10 +116,10 @@ schemathesis/specs/openapi/formats.py,sha256=3KtEC-8nQRwMErS-WpMadXsr8R0O-NzYwFi
|
|
116
116
|
schemathesis/specs/openapi/links.py,sha256=C4Uir2P_EcpqME8ee_a1vdUM8Tm3ZcKNn2YsGjZiMUQ,17935
|
117
117
|
schemathesis/specs/openapi/loaders.py,sha256=jlTYLoG5sVRh8xycIF2M2VDCZ44M80Sct07a_ycg1Po,25698
|
118
118
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
119
|
-
schemathesis/specs/openapi/parameters.py,sha256=
|
119
|
+
schemathesis/specs/openapi/parameters.py,sha256=fP4BupY_1wFbjL9n0lTtpQZY0YBt2mCjrG598LF8ZOI,14712
|
120
120
|
schemathesis/specs/openapi/patterns.py,sha256=L99UtslPvwObCVf5ndq3vL2YjQ7H1nMb-ZNMcyz_Qvk,12677
|
121
|
-
schemathesis/specs/openapi/references.py,sha256=
|
122
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
121
|
+
schemathesis/specs/openapi/references.py,sha256=0-gqbAxvBfrvFXA7YqmcNh7zRY7V_pKEnak0ncwQljI,9894
|
122
|
+
schemathesis/specs/openapi/schemas.py,sha256=Vrk6ORM1POvMe5XkbYTT9pnLFsDTFvHXrCplyKnBeDU,54173
|
123
123
|
schemathesis/specs/openapi/security.py,sha256=Z-6pk2Ga1PTUtBe298KunjVHsNh5A-teegeso7zcPIE,7138
|
124
124
|
schemathesis/specs/openapi/serialization.py,sha256=rcZfqQbWer_RELedu4Sh5h_RhKYPWTfUjnmLwpP2R_A,11842
|
125
125
|
schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
|
@@ -153,8 +153,8 @@ schemathesis/transports/auth.py,sha256=urSTO9zgFO1qU69xvnKHPFQV0SlJL3d7_Ojl0tLnZ
|
|
153
153
|
schemathesis/transports/content_types.py,sha256=MiKOm-Hy5i75hrROPdpiBZPOTDzOwlCdnthJD12AJzI,2187
|
154
154
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
155
155
|
schemathesis/transports/responses.py,sha256=OFD4ZLqwEFpo7F9vaP_SVgjhxAqatxIj38FS4XVq8Qs,1680
|
156
|
-
schemathesis-3.39.
|
157
|
-
schemathesis-3.39.
|
158
|
-
schemathesis-3.39.
|
159
|
-
schemathesis-3.39.
|
160
|
-
schemathesis-3.39.
|
156
|
+
schemathesis-3.39.16.dist-info/METADATA,sha256=3G135B_eBxr3JzsGS2GBSk9HrsnaD0CSfo-fUIVpKRM,11901
|
157
|
+
schemathesis-3.39.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
158
|
+
schemathesis-3.39.16.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
159
|
+
schemathesis-3.39.16.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
160
|
+
schemathesis-3.39.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|