schemathesis 4.3.15__py3-none-any.whl → 4.3.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.
Potentially problematic release.
This version of schemathesis might be problematic. Click here for more details.
- schemathesis/checks.py +1 -1
- schemathesis/cli/commands/run/handlers/cassettes.py +1 -2
- schemathesis/config/_error.py +1 -1
- schemathesis/core/errors.py +9 -0
- schemathesis/specs/openapi/converter.py +1 -14
- schemathesis/specs/openapi/examples.py +4 -4
- schemathesis/specs/openapi/references.py +31 -1
- {schemathesis-4.3.15.dist-info → schemathesis-4.3.16.dist-info}/METADATA +1 -1
- {schemathesis-4.3.15.dist-info → schemathesis-4.3.16.dist-info}/RECORD +12 -12
- {schemathesis-4.3.15.dist-info → schemathesis-4.3.16.dist-info}/WHEEL +0 -0
- {schemathesis-4.3.15.dist-info → schemathesis-4.3.16.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.3.15.dist-info → schemathesis-4.3.16.dist-info}/licenses/LICENSE +0 -0
schemathesis/checks.py
CHANGED
|
@@ -93,7 +93,7 @@ CHECKS = Registry[CheckFunction]()
|
|
|
93
93
|
|
|
94
94
|
def load_all_checks() -> None:
|
|
95
95
|
# NOTE: Trigger registering all Open API checks
|
|
96
|
-
from schemathesis.specs.openapi.checks import status_code_conformance # noqa: F401
|
|
96
|
+
from schemathesis.specs.openapi.checks import status_code_conformance # noqa: F401
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
def check(func: CheckFunction) -> CheckFunction:
|
|
@@ -123,8 +123,7 @@ def vcr_writer(output: TextOutput, config: ProjectConfig, queue: Queue) -> None:
|
|
|
123
123
|
current_id = 1
|
|
124
124
|
|
|
125
125
|
def write_header_values(stream: IO, values: list[str]) -> None:
|
|
126
|
-
for v in values
|
|
127
|
-
stream.write(f" - {json.dumps(v)}\n")
|
|
126
|
+
stream.writelines(f" - {json.dumps(v)}\n" for v in values)
|
|
128
127
|
|
|
129
128
|
if config.output.sanitization.enabled:
|
|
130
129
|
sanitization_keys = config.output.sanitization.keys_to_sanitize
|
schemathesis/config/_error.py
CHANGED
|
@@ -142,7 +142,7 @@ def _format_anyof_error(error: ValidationError) -> str:
|
|
|
142
142
|
)
|
|
143
143
|
elif list(error.schema_path) == ["properties", "workers", "anyOf"]:
|
|
144
144
|
return (
|
|
145
|
-
f"Invalid value for 'workers': {
|
|
145
|
+
f"Invalid value for 'workers': {error.instance!r}\n\n"
|
|
146
146
|
f"Expected either:\n"
|
|
147
147
|
f" - A positive integer (e.g., workers = 4)\n"
|
|
148
148
|
f' - The string "auto" for automatic detection (workers = "auto")'
|
schemathesis/core/errors.py
CHANGED
|
@@ -161,6 +161,8 @@ class InvalidSchema(SchemathesisError):
|
|
|
161
161
|
message += "\n File reference could not be resolved. Check that the file exists."
|
|
162
162
|
elif reference.startswith(("#/components", "#/definitions")):
|
|
163
163
|
message += "\n Component does not exist in the schema."
|
|
164
|
+
elif isinstance(error.__cause__, RemoteDocumentError):
|
|
165
|
+
message += f"\n {error.__cause__}"
|
|
164
166
|
return cls(message, path=path, method=method)
|
|
165
167
|
|
|
166
168
|
def as_failing_test_function(self) -> Callable:
|
|
@@ -176,6 +178,13 @@ class InvalidSchema(SchemathesisError):
|
|
|
176
178
|
return actual_test
|
|
177
179
|
|
|
178
180
|
|
|
181
|
+
class RemoteDocumentError(SchemathesisError):
|
|
182
|
+
"""Remote reference resolution failed.
|
|
183
|
+
|
|
184
|
+
This exception carries more context than the default one in `jsonschema`.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
|
|
179
188
|
class HookError(SchemathesisError):
|
|
180
189
|
"""Happens during hooks loading."""
|
|
181
190
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from itertools import chain
|
|
4
3
|
from typing import Any, Callable, overload
|
|
5
4
|
|
|
6
5
|
from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY
|
|
@@ -188,29 +187,17 @@ def update_pattern_in_schema(schema: dict[str, Any]) -> None:
|
|
|
188
187
|
|
|
189
188
|
def rewrite_properties(schema: dict[str, Any], predicate: Callable[[dict[str, Any]], bool]) -> None:
|
|
190
189
|
required = schema.get("required", [])
|
|
191
|
-
forbidden = []
|
|
192
190
|
for name, subschema in list(schema.get("properties", {}).items()):
|
|
193
191
|
if predicate(subschema):
|
|
194
192
|
if name in required:
|
|
195
193
|
required.remove(name)
|
|
196
|
-
|
|
197
|
-
forbidden.append(name)
|
|
198
|
-
if forbidden:
|
|
199
|
-
forbid_properties(schema, forbidden)
|
|
194
|
+
schema["properties"][name] = {"not": {}}
|
|
200
195
|
if not schema.get("required"):
|
|
201
196
|
schema.pop("required", None)
|
|
202
197
|
if not schema.get("properties"):
|
|
203
198
|
schema.pop("properties", None)
|
|
204
199
|
|
|
205
200
|
|
|
206
|
-
def forbid_properties(schema: dict[str, Any], forbidden: list[str]) -> None:
|
|
207
|
-
"""Explicitly forbid properties via the `not` keyword."""
|
|
208
|
-
not_schema = schema.setdefault("not", {})
|
|
209
|
-
already_forbidden = not_schema.setdefault("required", [])
|
|
210
|
-
already_forbidden.extend(forbidden)
|
|
211
|
-
not_schema["required"] = list(set(chain(already_forbidden, forbidden)))
|
|
212
|
-
|
|
213
|
-
|
|
214
201
|
def is_write_only(schema: dict[str, Any] | bool) -> bool:
|
|
215
202
|
if isinstance(schema, bool):
|
|
216
203
|
return False
|
|
@@ -242,7 +242,7 @@ def _expand_subschemas(
|
|
|
242
242
|
except InfiniteRecursiveReference:
|
|
243
243
|
return
|
|
244
244
|
|
|
245
|
-
yield
|
|
245
|
+
yield schema, current_path
|
|
246
246
|
|
|
247
247
|
if isinstance(schema, dict):
|
|
248
248
|
# For anyOf/oneOf, yield each alternative with the same path
|
|
@@ -250,10 +250,10 @@ def _expand_subschemas(
|
|
|
250
250
|
if key in schema:
|
|
251
251
|
for subschema in schema[key]:
|
|
252
252
|
# Each alternative starts with the current path
|
|
253
|
-
yield
|
|
253
|
+
yield subschema, current_path
|
|
254
254
|
|
|
255
255
|
# For allOf, merge all alternatives
|
|
256
|
-
if
|
|
256
|
+
if schema.get("allOf"):
|
|
257
257
|
subschema = deepclone(schema["allOf"][0])
|
|
258
258
|
try:
|
|
259
259
|
subschema, expanded_path = _resolve_bundled(subschema, resolver, current_path)
|
|
@@ -278,7 +278,7 @@ def _expand_subschemas(
|
|
|
278
278
|
else:
|
|
279
279
|
subschema[key] = value
|
|
280
280
|
|
|
281
|
-
yield
|
|
281
|
+
yield subschema, expanded_path
|
|
282
282
|
|
|
283
283
|
|
|
284
284
|
def extract_inner_examples(examples: dict[str, Any] | list, schema: BaseOpenAPISchema) -> Generator[Any, None, None]:
|
|
@@ -9,6 +9,7 @@ import requests
|
|
|
9
9
|
|
|
10
10
|
from schemathesis.core.compat import RefResolutionError, RefResolver
|
|
11
11
|
from schemathesis.core.deserialization import deserialize_yaml
|
|
12
|
+
from schemathesis.core.errors import RemoteDocumentError
|
|
12
13
|
from schemathesis.core.transport import DEFAULT_RESPONSE_TIMEOUT
|
|
13
14
|
|
|
14
15
|
|
|
@@ -30,10 +31,39 @@ def load_file_uri(location: str) -> dict[str, Any]:
|
|
|
30
31
|
return load_file_impl(location, urlopen)
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
_HTML_MARKERS = (b"<!doctype", b"<html", b"<head", b"<body")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _looks_like_html(content_type: str | None, body: bytes) -> bool:
|
|
38
|
+
if content_type and "html" in content_type.lower():
|
|
39
|
+
return True
|
|
40
|
+
head = body.lstrip()[:64].lower()
|
|
41
|
+
return any(head.startswith(m) for m in _HTML_MARKERS)
|
|
42
|
+
|
|
43
|
+
|
|
33
44
|
def load_remote_uri(uri: str) -> Any:
|
|
34
45
|
"""Load the resource and parse it as YAML / JSON."""
|
|
35
46
|
response = requests.get(uri, timeout=DEFAULT_RESPONSE_TIMEOUT)
|
|
36
|
-
|
|
47
|
+
content_type = response.headers.get("Content-Type", "")
|
|
48
|
+
body = response.content or b""
|
|
49
|
+
|
|
50
|
+
def _suffix() -> str:
|
|
51
|
+
return f"(HTTP {response.status_code}, Content-Type={content_type}, size={len(body)})"
|
|
52
|
+
|
|
53
|
+
if not (200 <= response.status_code < 300):
|
|
54
|
+
raise RemoteDocumentError(f"Failed to fetch {_suffix()}")
|
|
55
|
+
|
|
56
|
+
if _looks_like_html(content_type, body):
|
|
57
|
+
raise RemoteDocumentError(f"Expected YAML/JSON, got HTML {_suffix()}")
|
|
58
|
+
|
|
59
|
+
document = deserialize_yaml(response.content)
|
|
60
|
+
|
|
61
|
+
if not isinstance(document, (dict, list)):
|
|
62
|
+
raise RemoteDocumentError(
|
|
63
|
+
f"Remote document is parsed as {type(document).__name__}, but an object/array is expected {_suffix()}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return document
|
|
37
67
|
|
|
38
68
|
|
|
39
69
|
JSONType = Union[None, bool, float, str, list, Dict[str, Any]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.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://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
schemathesis/__init__.py,sha256=QqVUCBQr-RDEstgCZLsxzIa9HJslVSeijrm9gES4b_0,1423
|
|
2
2
|
schemathesis/auths.py,sha256=JdEwPRS9WKmPcxzGXYYz9pjlIUMQYCfif7ZJU0Kde-I,16400
|
|
3
|
-
schemathesis/checks.py,sha256=
|
|
3
|
+
schemathesis/checks.py,sha256=F_lsC5JTUKm_ByvilBN_9IpbL4mJiidfLgS8ir2ZDoQ,6873
|
|
4
4
|
schemathesis/errors.py,sha256=K3irHIZkrBH2-9LIjlgXlm8RNC41Nffd39ncfwagUvw,1053
|
|
5
5
|
schemathesis/filters.py,sha256=IevPA5A04GfRLLjmkFLZ0CLhjNO3RmpZq_yw6MqjLIA,13515
|
|
6
6
|
schemathesis/hooks.py,sha256=q2wqYNgpMCO8ImSBkbrWDSwN0BSELelqJMgAAgGvv2M,14836
|
|
@@ -21,7 +21,7 @@ schemathesis/cli/commands/run/loaders.py,sha256=eRgP1ZPfhOfxR7iXQ_CfV9r_8jP1DN4t
|
|
|
21
21
|
schemathesis/cli/commands/run/validation.py,sha256=DQaMiBLN2tYT9hONvv8xnyPvNXZH768UlOdUxTd5kZs,9193
|
|
22
22
|
schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31MAXXn1qI7uU4FtiDwroXZI,1915
|
|
23
23
|
schemathesis/cli/commands/run/handlers/base.py,sha256=qUtDvtr3F6were_BznfnaPpMibGJMnQ5CA9aEzcIUBc,1306
|
|
24
|
-
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=
|
|
24
|
+
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=2sXW9jykEFw4HCv25ycRfRd8uw5VV1e26Cp14O7PVhs,19245
|
|
25
25
|
schemathesis/cli/commands/run/handlers/junitxml.py,sha256=qiFvM4-SlM67sep003SkLqPslzaEb4nOm3bkzw-DO-Q,2602
|
|
26
26
|
schemathesis/cli/commands/run/handlers/output.py,sha256=pPp5-lJP3Zir1sTA7fmlhc-u1Jn17enXZNUerQMr56M,64166
|
|
27
27
|
schemathesis/cli/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -33,7 +33,7 @@ schemathesis/config/_auth.py,sha256=83RLVPm97W2thbn-yi01Rt94YwOxLG_a5VoxhEfjUjs,
|
|
|
33
33
|
schemathesis/config/_checks.py,sha256=F0r16eSSiICvoiTUkNNOE2PH73EGd8bikoeZdME_3Yw,10763
|
|
34
34
|
schemathesis/config/_diff_base.py,sha256=U7wuE4480EjP3K16mfC528TP5q7Q5IwAZwZLqRIrS1E,4300
|
|
35
35
|
schemathesis/config/_env.py,sha256=8XfIyrnGNQuCDnfG0lwmKRFbasRUjgeQGBAMupsmtOU,613
|
|
36
|
-
schemathesis/config/_error.py,sha256=
|
|
36
|
+
schemathesis/config/_error.py,sha256=0QnXO7Zagr69aYsX46GHm5xcTDd_18m8pFQBb0SsYvY,5777
|
|
37
37
|
schemathesis/config/_generation.py,sha256=giWs4z17z9nRe_9Z3mAZ3LEoyh4hkcJnlAA6LSy6iEo,5210
|
|
38
38
|
schemathesis/config/_health_check.py,sha256=zC9inla5ibMBlEy5WyM4_TME7ju_KH3Bwfo21RI3Gks,561
|
|
39
39
|
schemathesis/config/_operations.py,sha256=JvfMkieYBkbEmZRb4cTvQLfvHQLhmsxa3GXzgjOtmFc,12383
|
|
@@ -52,7 +52,7 @@ schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1
|
|
|
52
52
|
schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
|
|
53
53
|
schemathesis/core/curl.py,sha256=jrPL9KpNHteyJ6A1oxJRSkL5bfuBeuPs3xh9Z_ml2cE,1892
|
|
54
54
|
schemathesis/core/deserialization.py,sha256=qjXUPaz_mc1OSgXzTUSkC8tuVR8wgVQtb9g3CcAF6D0,2951
|
|
55
|
-
schemathesis/core/errors.py,sha256=
|
|
55
|
+
schemathesis/core/errors.py,sha256=NxuhMozUnC57BSsTKC95zJgSflZ1d01D3j6p8VyXhcw,20209
|
|
56
56
|
schemathesis/core/failures.py,sha256=yFpAxWdEnm0Ri8z8RqRI9H7vcLH5ztOeSIi4m4SGx5g,8996
|
|
57
57
|
schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
|
|
58
58
|
schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
|
|
@@ -130,13 +130,13 @@ schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzV
|
|
|
130
130
|
schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
|
|
131
131
|
schemathesis/specs/openapi/_hypothesis.py,sha256=O8vN-koBjzBVZfpD3pmgIt6ecU4ddAPHOxTAORd23Lo,22642
|
|
132
132
|
schemathesis/specs/openapi/checks.py,sha256=tb2s8azZYcO__Jf13ONUstO1inXoZBlo3dD2uuABB24,31712
|
|
133
|
-
schemathesis/specs/openapi/converter.py,sha256=
|
|
133
|
+
schemathesis/specs/openapi/converter.py,sha256=OlvGCCDAiIZS_osM9OQBvDGzwEToUZmVWwzQa4wQNws,6463
|
|
134
134
|
schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
|
|
135
|
-
schemathesis/specs/openapi/examples.py,sha256=
|
|
135
|
+
schemathesis/specs/openapi/examples.py,sha256=bNQVYJAb2LGtl_6josaNj5O4B0b_WaqtXVg1ZTvDDv8,24451
|
|
136
136
|
schemathesis/specs/openapi/formats.py,sha256=4tYRdckauHxkJCmOhmdwDq_eOpHPaKloi89lzMPbPzw,3975
|
|
137
137
|
schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
|
|
138
138
|
schemathesis/specs/openapi/patterns.py,sha256=GqPZEXMRdWENQxanWjBOalIZ2MQUjuxk21kmdiI703E,18027
|
|
139
|
-
schemathesis/specs/openapi/references.py,sha256=
|
|
139
|
+
schemathesis/specs/openapi/references.py,sha256=Jez2KpfsDIwXSvxZCmRQtlHk2X9UZ-JZkKAJaCPSLk8,2981
|
|
140
140
|
schemathesis/specs/openapi/schemas.py,sha256=ONFB8kMBrryZL_tKHWvxnBjyUHoHh_MAUqxjuVDc78c,34034
|
|
141
141
|
schemathesis/specs/openapi/serialization.py,sha256=RPNdadne5wdhsGmjSvgKLRF58wpzpRx3wura8PsHM3o,12152
|
|
142
142
|
schemathesis/specs/openapi/utils.py,sha256=XkOJT8qD-6uhq-Tmwxk_xYku1Gy5F9pKL3ldNg_DRZw,522
|
|
@@ -178,8 +178,8 @@ schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEz
|
|
|
178
178
|
schemathesis/transport/requests.py,sha256=jJAKqmsnlErU066WlUTIZHa3rx5lFtHWT43nm8CewUc,10717
|
|
179
179
|
schemathesis/transport/serialization.py,sha256=GwO6OAVTmL1JyKw7HiZ256tjV4CbrRbhQN0ep1uaZwI,11157
|
|
180
180
|
schemathesis/transport/wsgi.py,sha256=kQtasFre6pjdJWRKwLA_Qb-RyQHCFNpaey9ubzlFWKI,5907
|
|
181
|
-
schemathesis-4.3.
|
|
182
|
-
schemathesis-4.3.
|
|
183
|
-
schemathesis-4.3.
|
|
184
|
-
schemathesis-4.3.
|
|
185
|
-
schemathesis-4.3.
|
|
181
|
+
schemathesis-4.3.16.dist-info/METADATA,sha256=C--ciooZRdh7oAzk5hwFfccPqx1-G8QPHTTxbh_lZGw,8566
|
|
182
|
+
schemathesis-4.3.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
183
|
+
schemathesis-4.3.16.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
|
|
184
|
+
schemathesis-4.3.16.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
|
|
185
|
+
schemathesis-4.3.16.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|