schemathesis 3.27.1__py3-none-any.whl → 3.28.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.
- schemathesis/cli/context.py +2 -0
- schemathesis/cli/junitxml.py +8 -8
- schemathesis/cli/output/default.py +34 -8
- schemathesis/cli/reporting.py +1 -3
- schemathesis/exceptions.py +8 -0
- schemathesis/models.py +18 -2
- schemathesis/runner/impl/core.py +17 -13
- schemathesis/runner/serialization.py +16 -1
- schemathesis/serializers.py +1 -1
- schemathesis/specs/openapi/references.py +1 -1
- schemathesis/specs/openapi/schemas.py +1 -1
- schemathesis/transports/__init__.py +6 -0
- {schemathesis-3.27.1.dist-info → schemathesis-3.28.0.dist-info}/METADATA +37 -14
- {schemathesis-3.27.1.dist-info → schemathesis-3.28.0.dist-info}/RECORD +17 -17
- {schemathesis-3.27.1.dist-info → schemathesis-3.28.0.dist-info}/WHEEL +0 -0
- {schemathesis-3.27.1.dist-info → schemathesis-3.28.0.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.27.1.dist-info → schemathesis-3.28.0.dist-info}/licenses/LICENSE +0 -0
schemathesis/cli/context.py
CHANGED
|
@@ -55,6 +55,8 @@ class ExecutionContext:
|
|
|
55
55
|
report: ServiceReportContext | FileReportContext | None = None
|
|
56
56
|
probes: list[ProbeRun] | None = None
|
|
57
57
|
analysis: Result[AnalysisResult, Exception] | None = None
|
|
58
|
+
# Special flag to display a warning about Windows-specific encoding issue
|
|
59
|
+
encountered_windows_encoding_issue: bool = False
|
|
58
60
|
|
|
59
61
|
@deprecated_property(removed_in="4.0", replacement="show_trace")
|
|
60
62
|
def show_errors_tracebacks(self) -> bool:
|
schemathesis/cli/junitxml.py
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import base64
|
|
4
3
|
import platform
|
|
5
4
|
import textwrap
|
|
6
5
|
from dataclasses import dataclass, field
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING, cast
|
|
8
7
|
|
|
9
8
|
from junit_xml import TestCase, TestSuite, to_xml_report_file
|
|
10
9
|
|
|
10
|
+
from ..exceptions import RuntimeErrorType, prepare_response_payload
|
|
11
11
|
from ..models import Status
|
|
12
12
|
from ..runner import events
|
|
13
13
|
from ..runner.serialization import SerializedCheck, SerializedError
|
|
14
|
-
from ..exceptions import prepare_response_payload, RuntimeErrorType
|
|
15
14
|
from .handlers import EventHandler
|
|
16
|
-
from .reporting import
|
|
15
|
+
from .reporting import TEST_CASE_ID_TITLE, get_runtime_error_suggestion, group_by_case, split_traceback
|
|
17
16
|
|
|
18
17
|
if TYPE_CHECKING:
|
|
19
18
|
from click.utils import LazyFile
|
|
@@ -65,14 +64,15 @@ def build_failure_message(idx: int, code_sample: str, checks: list[SerializedChe
|
|
|
65
64
|
status_code = check.response.status_code
|
|
66
65
|
reason = get_reason(status_code)
|
|
67
66
|
message += f"\n[{check.response.status_code}] {reason}:\n"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if not response_body:
|
|
67
|
+
if check.response.body is not None:
|
|
68
|
+
if not check.response.body:
|
|
71
69
|
message += "\n <EMPTY>\n"
|
|
72
70
|
else:
|
|
73
71
|
encoding = check.response.encoding or "utf8"
|
|
74
72
|
try:
|
|
75
|
-
|
|
73
|
+
# Checked that is not None
|
|
74
|
+
body = cast(bytes, check.response.deserialize_body())
|
|
75
|
+
payload = body.decode(encoding)
|
|
76
76
|
payload = prepare_response_payload(payload)
|
|
77
77
|
payload = textwrap.indent(f"\n`{payload}`\n", prefix=" ")
|
|
78
78
|
message += payload
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import base64
|
|
4
3
|
import os
|
|
5
4
|
import platform
|
|
6
5
|
import shutil
|
|
@@ -200,6 +199,7 @@ def display_full_traceback_message(error: SerializedError) -> bool:
|
|
|
200
199
|
"hypothesis.errors.FailedHealthCheck",
|
|
201
200
|
"hypothesis.errors.InvalidArgument: Scalar ",
|
|
202
201
|
"hypothesis.errors.InvalidArgument: min_size=",
|
|
202
|
+
"hypothesis.errors.Unsatisfiable",
|
|
203
203
|
)
|
|
204
204
|
)
|
|
205
205
|
and "can never generate an example, because min_size is larger than Hypothesis supports." not in error.exception
|
|
@@ -303,23 +303,38 @@ def display_failures_for_single_test(context: ExecutionContext, result: Serializ
|
|
|
303
303
|
reason = get_reason(status_code)
|
|
304
304
|
response = bold(f"[{check.response.status_code}] {reason}")
|
|
305
305
|
click.echo(f"\n{response}:")
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if not response_body:
|
|
306
|
+
if check.response.body is not None:
|
|
307
|
+
if not check.response.body:
|
|
309
308
|
click.echo("\n <EMPTY>")
|
|
310
309
|
else:
|
|
311
310
|
encoding = check.response.encoding or "utf8"
|
|
312
311
|
try:
|
|
313
|
-
|
|
312
|
+
# Checked that is not None
|
|
313
|
+
body = cast(bytes, check.response.deserialize_body())
|
|
314
|
+
payload = body.decode(encoding)
|
|
314
315
|
payload = prepare_response_payload(payload)
|
|
315
316
|
payload = textwrap.indent(f"\n`{payload}`", prefix=" ")
|
|
316
317
|
click.echo(payload)
|
|
317
318
|
except UnicodeDecodeError:
|
|
318
319
|
click.echo("\n <BINARY>")
|
|
319
320
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
try:
|
|
322
|
+
click.echo(
|
|
323
|
+
f"\n{bold('Reproduce with')}: \n\n {code_sample}\n",
|
|
324
|
+
)
|
|
325
|
+
except UnicodeEncodeError:
|
|
326
|
+
# On Windows it may fail when redirecting the output to a file
|
|
327
|
+
# because it uses a different encoding than the console encoding and the default
|
|
328
|
+
# is cp1252, which doesn't support some Unicode characters.
|
|
329
|
+
# In this case, display a stub message and set a flag to display a warning at the end
|
|
330
|
+
if platform.system() != "Windows":
|
|
331
|
+
raise
|
|
332
|
+
click.echo(
|
|
333
|
+
f"\n{bold('Reproduce with')}: \n\n"
|
|
334
|
+
" CAN NOT DISPLAY THIS CODE SAMPLE DUE TO TERMINAL LIMITATIONS.\n"
|
|
335
|
+
" SEE DETAILS AT THE END OF THE OUTPUT\n",
|
|
336
|
+
)
|
|
337
|
+
context.encountered_windows_encoding_issue = True
|
|
323
338
|
|
|
324
339
|
|
|
325
340
|
def display_application_logs(context: ExecutionContext, event: events.Finished) -> None:
|
|
@@ -465,6 +480,17 @@ def display_statistic(context: ExecutionContext, event: events.Finished) -> None
|
|
|
465
480
|
seed_option = f"`--hypothesis-seed={context.seed}`"
|
|
466
481
|
click.secho(f"\n{bold('Note')}: To replicate these test failures, rerun with {bold(seed_option)}")
|
|
467
482
|
|
|
483
|
+
if context.encountered_windows_encoding_issue:
|
|
484
|
+
click.echo()
|
|
485
|
+
title = click.style("WARNING:", bold=True, fg="yellow")
|
|
486
|
+
name = click.style("PYTHONIOENCODING", bold=True)
|
|
487
|
+
value = click.style("utf8", bold=True)
|
|
488
|
+
warning = (
|
|
489
|
+
"Some code samples could not be displayed due to terminal limitations on Windows.\n"
|
|
490
|
+
f"To resolve this set the '{name}' environment variable to '{value}' and rerun your command."
|
|
491
|
+
)
|
|
492
|
+
click.secho(f"{title} {warning}")
|
|
493
|
+
|
|
468
494
|
if context.report is not None and not context.is_interrupted:
|
|
469
495
|
if isinstance(context.report, FileReportContext):
|
|
470
496
|
click.echo()
|
schemathesis/cli/reporting.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import base64
|
|
4
3
|
from itertools import groupby
|
|
5
4
|
from typing import Callable, Generator
|
|
6
5
|
|
|
@@ -22,11 +21,10 @@ def group_by_case(
|
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
def _by_unique_code_sample(check: SerializedCheck, code_sample_style: CodeSampleStyle) -> str:
|
|
25
|
-
request_body = base64.b64decode(check.example.body).decode() if check.example.body is not None else None
|
|
26
24
|
return code_sample_style.generate(
|
|
27
25
|
method=check.example.method,
|
|
28
26
|
url=check.example.url,
|
|
29
|
-
body=
|
|
27
|
+
body=check.example.deserialize_body(),
|
|
30
28
|
headers=check.example.headers,
|
|
31
29
|
verify=check.example.verify,
|
|
32
30
|
extra_headers=check.example.extra_headers,
|
schemathesis/exceptions.py
CHANGED
|
@@ -336,6 +336,12 @@ class DeadlineExceeded(Exception):
|
|
|
336
336
|
)
|
|
337
337
|
|
|
338
338
|
|
|
339
|
+
class RecursiveReferenceError(Exception):
|
|
340
|
+
"""Recursive reference is impossible to resolve due to current limitations."""
|
|
341
|
+
|
|
342
|
+
__module__ = "builtins"
|
|
343
|
+
|
|
344
|
+
|
|
339
345
|
@enum.unique
|
|
340
346
|
class RuntimeErrorType(str, enum.Enum):
|
|
341
347
|
# Connection related issues
|
|
@@ -354,6 +360,7 @@ class RuntimeErrorType(str, enum.Enum):
|
|
|
354
360
|
|
|
355
361
|
SCHEMA_BODY_IN_GET_REQUEST = "schema_body_in_get_request"
|
|
356
362
|
SCHEMA_INVALID_REGULAR_EXPRESSION = "schema_invalid_regular_expression"
|
|
363
|
+
SCHEMA_UNSUPPORTED = "schema_unsupported"
|
|
357
364
|
SCHEMA_GENERIC = "schema_generic"
|
|
358
365
|
|
|
359
366
|
SERIALIZATION_NOT_POSSIBLE = "serialization_not_possible"
|
|
@@ -367,6 +374,7 @@ class RuntimeErrorType(str, enum.Enum):
|
|
|
367
374
|
return self not in (
|
|
368
375
|
RuntimeErrorType.SCHEMA_BODY_IN_GET_REQUEST,
|
|
369
376
|
RuntimeErrorType.SCHEMA_INVALID_REGULAR_EXPRESSION,
|
|
377
|
+
RuntimeErrorType.SCHEMA_UNSUPPORTED,
|
|
370
378
|
RuntimeErrorType.SCHEMA_GENERIC,
|
|
371
379
|
RuntimeErrorType.SERIALIZATION_NOT_POSSIBLE,
|
|
372
380
|
)
|
schemathesis/models.py
CHANGED
|
@@ -50,11 +50,11 @@ from .exceptions import (
|
|
|
50
50
|
from .generation import DataGenerationMethod, GenerationConfig, generate_random_case_id
|
|
51
51
|
from .hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, dispatch
|
|
52
52
|
from .internal.copy import fast_deepcopy
|
|
53
|
-
from .internal.deprecation import
|
|
53
|
+
from .internal.deprecation import deprecated_function, deprecated_property
|
|
54
54
|
from .parameters import Parameter, ParameterSet, PayloadAlternatives
|
|
55
55
|
from .sanitization import sanitize_request, sanitize_response
|
|
56
56
|
from .serializers import Serializer
|
|
57
|
-
from .transports import ASGITransport, RequestsTransport, WSGITransport, serialize_payload
|
|
57
|
+
from .transports import ASGITransport, RequestsTransport, WSGITransport, serialize_payload, deserialize_payload
|
|
58
58
|
from .types import Body, Cookies, FormData, Headers, NotSet, PathParameters, Query
|
|
59
59
|
|
|
60
60
|
if TYPE_CHECKING:
|
|
@@ -854,6 +854,14 @@ class Request:
|
|
|
854
854
|
body=serialize_payload(body) if body is not None else body,
|
|
855
855
|
)
|
|
856
856
|
|
|
857
|
+
def deserialize_body(self) -> bytes | None:
|
|
858
|
+
"""Deserialize the request body.
|
|
859
|
+
|
|
860
|
+
`Request` should be serializable to JSON, therefore body is encoded as base64 string
|
|
861
|
+
to support arbitrary binary data.
|
|
862
|
+
"""
|
|
863
|
+
return deserialize_payload(self.body)
|
|
864
|
+
|
|
857
865
|
|
|
858
866
|
@dataclass(repr=False)
|
|
859
867
|
class Response:
|
|
@@ -923,6 +931,14 @@ class Response:
|
|
|
923
931
|
verify=True,
|
|
924
932
|
)
|
|
925
933
|
|
|
934
|
+
def deserialize_body(self) -> bytes | None:
|
|
935
|
+
"""Deserialize the response body.
|
|
936
|
+
|
|
937
|
+
`Response` should be serializable to JSON, therefore body is encoded as base64 string
|
|
938
|
+
to support arbitrary binary data.
|
|
939
|
+
"""
|
|
940
|
+
return deserialize_payload(self.body)
|
|
941
|
+
|
|
926
942
|
|
|
927
943
|
@dataclass
|
|
928
944
|
class Interaction:
|
schemathesis/runner/impl/core.py
CHANGED
|
@@ -42,10 +42,12 @@ from ...constants import (
|
|
|
42
42
|
from ...exceptions import (
|
|
43
43
|
CheckFailed,
|
|
44
44
|
DeadlineExceeded,
|
|
45
|
+
InternalError,
|
|
45
46
|
InvalidHeadersExample,
|
|
46
47
|
InvalidRegularExpression,
|
|
47
48
|
NonCheckError,
|
|
48
49
|
OperationSchemaError,
|
|
50
|
+
RecursiveReferenceError,
|
|
49
51
|
SerializationNotPossible,
|
|
50
52
|
SkipTest,
|
|
51
53
|
format_exception,
|
|
@@ -474,13 +476,25 @@ def run_test(
|
|
|
474
476
|
except SkipTest as exc:
|
|
475
477
|
status = Status.skip
|
|
476
478
|
result.mark_skipped(exc)
|
|
477
|
-
except AssertionError: #
|
|
478
|
-
error = reraise(operation)
|
|
479
|
+
except AssertionError as exc: # May come from `hypothesis-jsonschema` or `hypothesis`
|
|
479
480
|
status = Status.error
|
|
481
|
+
try:
|
|
482
|
+
operation.schema.validate()
|
|
483
|
+
try:
|
|
484
|
+
raise InternalError(f"Unexpected error during testing of this API operation: {exc}") from exc
|
|
485
|
+
except InternalError as exc:
|
|
486
|
+
error = exc
|
|
487
|
+
except ValidationError as exc:
|
|
488
|
+
error = OperationSchemaError.from_jsonschema_error(
|
|
489
|
+
exc,
|
|
490
|
+
path=operation.path,
|
|
491
|
+
method=operation.method,
|
|
492
|
+
full_path=operation.schema.get_full_path(operation.path),
|
|
493
|
+
)
|
|
480
494
|
result.add_error(error)
|
|
481
495
|
except HypothesisRefResolutionError:
|
|
482
496
|
status = Status.error
|
|
483
|
-
result.add_error(
|
|
497
|
+
result.add_error(RecursiveReferenceError(RECURSIVE_REFERENCE_ERROR_MESSAGE))
|
|
484
498
|
except InvalidArgument as error:
|
|
485
499
|
status = Status.error
|
|
486
500
|
message = get_invalid_regular_expression_message(warnings)
|
|
@@ -614,16 +628,6 @@ def get_invalid_regular_expression_message(warnings: list[WarningMessage]) -> st
|
|
|
614
628
|
return None
|
|
615
629
|
|
|
616
630
|
|
|
617
|
-
def reraise(operation: APIOperation) -> OperationSchemaError:
|
|
618
|
-
try:
|
|
619
|
-
operation.schema.validate()
|
|
620
|
-
except ValidationError as exc:
|
|
621
|
-
return OperationSchemaError.from_jsonschema_error(
|
|
622
|
-
exc, path=operation.path, method=operation.method, full_path=operation.schema.get_full_path(operation.path)
|
|
623
|
-
)
|
|
624
|
-
return OperationSchemaError("Unknown schema error")
|
|
625
|
-
|
|
626
|
-
|
|
627
631
|
MEMORY_ADDRESS_RE = re.compile("0x[0-9a-fA-F]+")
|
|
628
632
|
URL_IN_ERROR_MESSAGE_RE = re.compile(r"Max retries exceeded with url: .*? \(Caused by")
|
|
629
633
|
|
|
@@ -19,6 +19,7 @@ from ..exceptions import (
|
|
|
19
19
|
InternalError,
|
|
20
20
|
InvalidRegularExpression,
|
|
21
21
|
OperationSchemaError,
|
|
22
|
+
RecursiveReferenceError,
|
|
22
23
|
RuntimeErrorType,
|
|
23
24
|
SerializationError,
|
|
24
25
|
UnboundPrefixError,
|
|
@@ -27,7 +28,7 @@ from ..exceptions import (
|
|
|
27
28
|
make_unique_by_key,
|
|
28
29
|
)
|
|
29
30
|
from ..models import Case, Check, Interaction, Request, Response, Status, TestResult
|
|
30
|
-
from ..transports import serialize_payload
|
|
31
|
+
from ..transports import serialize_payload, deserialize_payload
|
|
31
32
|
|
|
32
33
|
if TYPE_CHECKING:
|
|
33
34
|
import hypothesis.errors
|
|
@@ -81,6 +82,14 @@ class SerializedCase:
|
|
|
81
82
|
extra_headers=request_data.headers,
|
|
82
83
|
)
|
|
83
84
|
|
|
85
|
+
def deserialize_body(self) -> bytes | None:
|
|
86
|
+
"""Deserialize the test case body.
|
|
87
|
+
|
|
88
|
+
`SerializedCase` should be serializable to JSON, therefore body is encoded as base64 string
|
|
89
|
+
to support arbitrary binary data.
|
|
90
|
+
"""
|
|
91
|
+
return deserialize_payload(self.body)
|
|
92
|
+
|
|
84
93
|
|
|
85
94
|
def _serialize_body(body: str | bytes | None) -> str | None:
|
|
86
95
|
if body is None:
|
|
@@ -244,6 +253,11 @@ class SerializedError:
|
|
|
244
253
|
type_ = RuntimeErrorType.HYPOTHESIS_DEADLINE_EXCEEDED
|
|
245
254
|
message = str(exception).strip()
|
|
246
255
|
extras = []
|
|
256
|
+
elif isinstance(exception, RecursiveReferenceError):
|
|
257
|
+
type_ = RuntimeErrorType.SCHEMA_UNSUPPORTED
|
|
258
|
+
message = str(exception).strip()
|
|
259
|
+
extras = []
|
|
260
|
+
title = "Unsupported Schema"
|
|
247
261
|
elif isinstance(exception, hypothesis.errors.InvalidArgument) and str(exception).startswith("Scalar "):
|
|
248
262
|
# Comes from `hypothesis-graphql`
|
|
249
263
|
scalar_name = _scalar_name_from_error(exception)
|
|
@@ -264,6 +278,7 @@ class SerializedError:
|
|
|
264
278
|
message = f"{exception}. Possible reasons:"
|
|
265
279
|
extras = [
|
|
266
280
|
"- Contradictory schema constraints, such as a minimum value exceeding the maximum.",
|
|
281
|
+
"- Invalid schema definitions for headers or cookies, for example allowing for non-ASCII characters.",
|
|
267
282
|
"- Excessive schema complexity, which hinders parameter generation.",
|
|
268
283
|
]
|
|
269
284
|
title = "Schema Error"
|
schemathesis/serializers.py
CHANGED
|
@@ -241,7 +241,7 @@ def _encode_multipart(value: Any, boundary: str) -> bytes:
|
|
|
241
241
|
return body.getvalue()
|
|
242
242
|
|
|
243
243
|
|
|
244
|
-
@register("multipart/form-data")
|
|
244
|
+
@register("multipart/form-data", aliases=("multipart/mixed",))
|
|
245
245
|
class MultipartSerializer:
|
|
246
246
|
def as_requests(self, context: SerializerContext, value: Any) -> dict[str, Any]:
|
|
247
247
|
if isinstance(value, bytes):
|
|
@@ -186,7 +186,7 @@ def remove_optional_references(schema: dict[str, Any]) -> None:
|
|
|
186
186
|
v = s.get(keyword)
|
|
187
187
|
if v is not None:
|
|
188
188
|
elided = [sub for sub in v if not can_elide(sub)]
|
|
189
|
-
if len(elided) == 1 and
|
|
189
|
+
if len(elided) == 1 and contains_ref(elided[0]):
|
|
190
190
|
found.append(keyword)
|
|
191
191
|
return found
|
|
192
192
|
|
|
@@ -1065,7 +1065,7 @@ class OpenApi30(SwaggerV20):
|
|
|
1065
1065
|
# the "multipart/form-data" media type, or any other more general media type that matches it (like `*/*`)
|
|
1066
1066
|
for media_type, entry in content.items():
|
|
1067
1067
|
main, sub = parse_content_type(media_type)
|
|
1068
|
-
if main in ("*", "multipart") and sub in ("*", "form-data"):
|
|
1068
|
+
if main in ("*", "multipart") and sub in ("*", "form-data", "mixed"):
|
|
1069
1069
|
schema = entry["schema"]
|
|
1070
1070
|
break
|
|
1071
1071
|
else:
|
|
@@ -30,6 +30,12 @@ def serialize_payload(payload: bytes) -> str:
|
|
|
30
30
|
return base64.b64encode(payload).decode()
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def deserialize_payload(data: str | None) -> bytes | None:
|
|
34
|
+
if data is None:
|
|
35
|
+
return None
|
|
36
|
+
return base64.b64decode(data)
|
|
37
|
+
|
|
38
|
+
|
|
33
39
|
def get(app: Any) -> Transport:
|
|
34
40
|
"""Get transport to send the data to the application."""
|
|
35
41
|
if app is None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.28.0
|
|
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
|
|
@@ -77,7 +77,7 @@ Requires-Dist: trustme<1.0,>=0.9.0; extra == 'tests'
|
|
|
77
77
|
Description-Content-Type: text/markdown
|
|
78
78
|
|
|
79
79
|
<p align="center">
|
|
80
|
-
<em>Schemathesis:
|
|
80
|
+
<em>Schemathesis: Supercharge your API testing, catch bugs, and ensure compliance</em>
|
|
81
81
|
</p>
|
|
82
82
|
|
|
83
83
|
<p align="center">
|
|
@@ -109,29 +109,34 @@ Description-Content-Type: text/markdown
|
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
112
|
-
##
|
|
112
|
+
## What is Schemathesis?
|
|
113
113
|
|
|
114
|
-
Schemathesis is a tool that
|
|
114
|
+
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:
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
🎯 **Catch Hard-to-Find Bugs**
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
- Uncover hidden crashes and edge cases that manual testing might miss
|
|
119
|
+
- Identify spec violations and ensure your API adheres to its defined contract
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
⚡ **Accelerate Testing Cycles**
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
- Automatically generate a wide range of test cases based on your API schema
|
|
124
|
+
- Save time and effort by eliminating the need for manual test case creation
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
🧩 **Integrate Seamlessly**
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
- Works with popular API formats such as OpenAPI, GraphQL.
|
|
129
|
+
- Easily integrate into your existing testing pipeline and CI/CD workflows
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
🔧 **Customize and Extend**
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
- Tune the testing process to your specific requirements using Python extensions
|
|
134
|
+
- Modify and enhance various aspects of the testing flow to suit your needs with rich configuration options
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
📊 **Gain Valuable Insights**
|
|
133
137
|
|
|
134
|
-
|
|
138
|
+
- Get detailed reports and actionable insights to help you identify and fix issues quickly
|
|
139
|
+
- Reproduce failing test cases effortlessly with generated code samples and cURL commands
|
|
135
140
|
|
|
136
141
|
## Quick Demo
|
|
137
142
|
|
|
@@ -309,6 +314,24 @@ This includes:
|
|
|
309
314
|
|
|
310
315
|
To discuss a custom support arrangement that best suits your organization, please contact our support team at <a href="mailto:support@schemathesis.io">support@schemathesis.io</a>.
|
|
311
316
|
|
|
317
|
+
## Acknowledgements
|
|
318
|
+
|
|
319
|
+
Schemathesis is built on top of <a href="https://hypothesis.works/" target="_blank">Hypothesis</a>, a powerful property-based testing library for Python.
|
|
320
|
+
|
|
321
|
+
## Who's Using Schemathesis?
|
|
322
|
+
|
|
323
|
+
Schemathesis is used by a number of project and companies, including direct usage or integration into other tools:
|
|
324
|
+
|
|
325
|
+
- Abstract Machines ([Magistrala](https://github.com/absmach/magistrala))
|
|
326
|
+
- Bundesstelle für Open Data ([smard-api](https://github.com/bundesAPI/smard-api))
|
|
327
|
+
- [CheckMK](https://github.com/Checkmk/checkmk)
|
|
328
|
+
- HXSecurity ([DongTai](https://github.com/HXSecurity/DongTai))
|
|
329
|
+
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
|
330
|
+
- [Pixie](https://github.com/pixie-io/pixie)
|
|
331
|
+
- [Qdrant](https://github.com/qdrant/qdrant)
|
|
332
|
+
- Spotify ([Backstage](https://github.com/backstage/backstage))
|
|
333
|
+
- WordPress ([OpenVerse](https://github.com/WordPress/openverse))
|
|
334
|
+
|
|
312
335
|
## Additional content
|
|
313
336
|
|
|
314
337
|
### Papers
|
|
@@ -10,19 +10,19 @@ schemathesis/auths.py,sha256=tUuaHvXO96HJr22Gu9OmlSpL2wbAqKZr3TRbb9dVyA4,14732
|
|
|
10
10
|
schemathesis/checks.py,sha256=SYts1Teecg-5kSHBo32Pzhh7YQ4a1Y7DIfldd-0VTj8,2155
|
|
11
11
|
schemathesis/code_samples.py,sha256=QFwyA7dmc_ij2FRwfbe0uMRm6j2yeofQ8zXynZV3SZI,4122
|
|
12
12
|
schemathesis/constants.py,sha256=gptQZnXJwEJjuLgw62n7LL_kbkE7dhxUgyyJw_dEEAc,2672
|
|
13
|
-
schemathesis/exceptions.py,sha256
|
|
13
|
+
schemathesis/exceptions.py,sha256=-u7pnntcv2OBuY0aO4SGRRtGk2Bd8RqdZuUTSnBzZjk,19882
|
|
14
14
|
schemathesis/failures.py,sha256=oKbIDD075ymLT4h7SQK-herHDT608d-StPX9pXmOSVU,5996
|
|
15
15
|
schemathesis/filters.py,sha256=9SJyZ8mcuDyKG2G1hp2Nfz-TvyJWP_NcntsgwFWYY_Q,10250
|
|
16
16
|
schemathesis/graphql.py,sha256=YkoKWY5K8lxp7H3ikAs-IsoDbiPwJvChG7O8p3DgwtI,229
|
|
17
17
|
schemathesis/hooks.py,sha256=cNJgCh7SyLWT1sYDKF5ncDC80ld08CinvKo2IqLMV4g,12396
|
|
18
18
|
schemathesis/lazy.py,sha256=CivWpvesh4iYLSkatXbQPTEOruWmXvuZQ29gng5p9wM,14846
|
|
19
19
|
schemathesis/loaders.py,sha256=RJnrbf-3vZ7KXmRBkmr3uqWyg0eHzOnONABuudWcTIg,4586
|
|
20
|
-
schemathesis/models.py,sha256=
|
|
20
|
+
schemathesis/models.py,sha256=K5Ct8NhpN0UmjNRVLnb5W_d5tMCu0IYZxR3Q8oE6L4Y,43565
|
|
21
21
|
schemathesis/parameters.py,sha256=VheEffVzoSfYaSEcG7KhPlA4ypifosG8biiHifzwL8g,2257
|
|
22
22
|
schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
schemathesis/sanitization.py,sha256=WSV_MB5YRrYkp1pQRPHrzsidqsKcqYZiq64N9grKobo,8956
|
|
24
24
|
schemathesis/schemas.py,sha256=RzxAoBoJJSLsixrkTZPe2N_oWbWv8n5RL4XaYRyoSIo,18531
|
|
25
|
-
schemathesis/serializers.py,sha256=
|
|
25
|
+
schemathesis/serializers.py,sha256=kxXZ-UGa1v_vOm0sC4QYcrNv4rfvI7tHGT2elRVbCbc,11649
|
|
26
26
|
schemathesis/targets.py,sha256=tzp7VZ2-7g2nZHCooRgFzTMtOVcbl0rvtNR421hQthA,1162
|
|
27
27
|
schemathesis/throttling.py,sha256=uwhL4XWPWAU8HECg0NhibfCMn5dT7NElTx3fdL3Mmcc,1065
|
|
28
28
|
schemathesis/types.py,sha256=AglR5M0bce-YXeDRkweToXTP0GjNOWVjS_mIsxLobwc,919
|
|
@@ -32,15 +32,15 @@ schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,
|
|
|
32
32
|
schemathesis/cli/callbacks.py,sha256=HmD0WmSYf5x8v4xeZdOKzy_8CEk23NitUlL8JQ7LHdQ,14950
|
|
33
33
|
schemathesis/cli/cassettes.py,sha256=1ucgYyOZiNu44uOv044FdfKTzQolUyhAM2onk4m3MD0,12988
|
|
34
34
|
schemathesis/cli/constants.py,sha256=XoezT0_fHuiOY2e--cmBUhNieJsepcUoW8e48QuLSDI,1544
|
|
35
|
-
schemathesis/cli/context.py,sha256=
|
|
35
|
+
schemathesis/cli/context.py,sha256=Hsbt7YS1Gr01QJ825mECpEduEbRIaRRMUz7k6j1SpVI,2005
|
|
36
36
|
schemathesis/cli/debug.py,sha256=PDEa-oHyz5bZ8aYjRYARwQaCv_AC6HM_L43LJfe4vUM,657
|
|
37
37
|
schemathesis/cli/handlers.py,sha256=62GPWPmgeGUz3pkDd01H4UCXcIy5a9yRtU7qaAeeR-A,389
|
|
38
|
-
schemathesis/cli/junitxml.py,sha256=
|
|
38
|
+
schemathesis/cli/junitxml.py,sha256=WdxtBKBi6V5Ow840Nde21MvUlDJQyJu2M6DXx1VDuyo,4648
|
|
39
39
|
schemathesis/cli/options.py,sha256=7_dXcrPT0kWqAkm60cAT1J0IsTOcvNFxT1pcHYttBuI,2558
|
|
40
|
-
schemathesis/cli/reporting.py,sha256=
|
|
40
|
+
schemathesis/cli/reporting.py,sha256=_Jh8sJMfOf_CUHdbTpG5VRsfW8INtXQjtk1njmGWEnY,3298
|
|
41
41
|
schemathesis/cli/sanitization.py,sha256=v-9rsMCBpWhom0Bfzj_8c6InqxkVjSWK6kRoURa52Nk,727
|
|
42
42
|
schemathesis/cli/output/__init__.py,sha256=AXaUzQ1nhQ-vXhW4-X-91vE2VQtEcCOrGtQXXNN55iQ,29
|
|
43
|
-
schemathesis/cli/output/default.py,sha256=
|
|
43
|
+
schemathesis/cli/output/default.py,sha256=130qttjuzUgYcCD-LnTaMTumoOMWLzu3q-aPOyJ0ogs,38176
|
|
44
44
|
schemathesis/cli/output/short.py,sha256=MmFMOKBRqqvgq7Kmx6XJc7Vqkr9g5BrJBxRbyZ0Rh-k,2068
|
|
45
45
|
schemathesis/contrib/__init__.py,sha256=FH8NL8NXgSKBFOF8Jy_EB6T4CJEaiM-tmDhz16B2o4k,187
|
|
46
46
|
schemathesis/contrib/unique_data.py,sha256=zxrmAlQH7Bcil9YSfy2EazwLj2rxLzOvAE3O6QRRkFY,1317
|
|
@@ -69,9 +69,9 @@ schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QA
|
|
|
69
69
|
schemathesis/runner/__init__.py,sha256=kkqjC_5G2Mrq00syLNUDHP3sXqwiId8_cusKtIlOyXM,21419
|
|
70
70
|
schemathesis/runner/events.py,sha256=eZc4TT8C8mWCMr9h4JvlijvQQ-x8wFry8LntdsYIL-A,10200
|
|
71
71
|
schemathesis/runner/probes.py,sha256=t_B38-ljy-p3Odw-dqcZUVIjYTPwBvac8KE5GaLnz4M,5546
|
|
72
|
-
schemathesis/runner/serialization.py,sha256=
|
|
72
|
+
schemathesis/runner/serialization.py,sha256=NkeXNQUvzpnTiT4TbojpUC0QXl2Gjv7quPx5HUxpJXI,17356
|
|
73
73
|
schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
|
|
74
|
-
schemathesis/runner/impl/core.py,sha256=
|
|
74
|
+
schemathesis/runner/impl/core.py,sha256=E38yRz4c5hc_QjZnMSvZwbRYjt6VpoS2KxH2V7l3RIk,39802
|
|
75
75
|
schemathesis/runner/impl/solo.py,sha256=Y_tNhVBVxcuxv65hN0FjxLlVSC41ebHMOM1xSzVrNk8,3358
|
|
76
76
|
schemathesis/runner/impl/threadpool.py,sha256=2-2Wvw7msezZtenZY5vU_x3FGLLVlH-ywvhU9hTwAAo,15073
|
|
77
77
|
schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
|
|
@@ -107,8 +107,8 @@ schemathesis/specs/openapi/links.py,sha256=FUjqEf6Sv6PeS0UX7CLL5p2VHa9vA1MGDPhx2
|
|
|
107
107
|
schemathesis/specs/openapi/loaders.py,sha256=QoEFJ7fxJz5XcGTEWV_ZGXARiektmyyT_xmVtcHco6Q,24131
|
|
108
108
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
|
109
109
|
schemathesis/specs/openapi/parameters.py,sha256=5jMZQZFsZNBFjG22Bqot-Rc65emiSA4E95rIzwImThk,13610
|
|
110
|
-
schemathesis/specs/openapi/references.py,sha256=
|
|
111
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
|
110
|
+
schemathesis/specs/openapi/references.py,sha256=zEDzoJbUAwVYTy9CtJ9w1E1Xzp60CVsjebDkFjMXnno,8982
|
|
111
|
+
schemathesis/specs/openapi/schemas.py,sha256=oFRfFucQ3Sf5iZvnVCGJDW0MH7lyA1pSAn7S7bnaSmQ,50466
|
|
112
112
|
schemathesis/specs/openapi/security.py,sha256=nCUIaZTzI6t26HAfd8YTHW6mFxXAPN9Ds-P9UnXxmNA,6628
|
|
113
113
|
schemathesis/specs/openapi/serialization.py,sha256=jajqowTIiyEVWEx-Gy4ZinXZewNg0n_ipsGzz7JXP7c,11383
|
|
114
114
|
schemathesis/specs/openapi/utils.py,sha256=gmW4v6o6sZQifajfPquhFeWmZWGQM89mOOBYZvjnE7g,741
|
|
@@ -127,13 +127,13 @@ schemathesis/specs/openapi/stateful/__init__.py,sha256=R8RhPJD3rDzqL4eA9jSnUwh9Q
|
|
|
127
127
|
schemathesis/specs/openapi/stateful/links.py,sha256=cSIwho2Hroty6au-gyCD-OwqnuCcIpnIIHU6FvF0SwA,3533
|
|
128
128
|
schemathesis/stateful/__init__.py,sha256=T7rvhzftfl3wumEsir33DBBzCTK2PtRp9CxBxMLdMSE,4693
|
|
129
129
|
schemathesis/stateful/state_machine.py,sha256=ZcKpbvEl1QGhVOYnA7Ow6zkuFHtEPDAyCjroPrj-FgU,11343
|
|
130
|
-
schemathesis/transports/__init__.py,sha256=
|
|
130
|
+
schemathesis/transports/__init__.py,sha256=vQ5pyMefhVpbl0FFXtivGsFHWX-7V1PXNwvqCKMJHoI,11423
|
|
131
131
|
schemathesis/transports/auth.py,sha256=ZKFku9gjhIG6445qNC2p_64Yt9Iz_4azbvja8AMptBk,404
|
|
132
132
|
schemathesis/transports/content_types.py,sha256=xU8RZWxz-CyWRqQTI2fGYQacB7KasoY7LL_bxPQdyPY,2144
|
|
133
133
|
schemathesis/transports/headers.py,sha256=EDxpm8su0AuhyqZUkMex-OFZMAJN_5NHah7fDT2HDZE,989
|
|
134
134
|
schemathesis/transports/responses.py,sha256=j_-udvWbmi6XtEYmpdX8WoFnlrQ6-i3PSBizfozRjQI,1673
|
|
135
|
-
schemathesis-3.
|
|
136
|
-
schemathesis-3.
|
|
137
|
-
schemathesis-3.
|
|
138
|
-
schemathesis-3.
|
|
139
|
-
schemathesis-3.
|
|
135
|
+
schemathesis-3.28.0.dist-info/METADATA,sha256=42KfcWW6Hsnb65TECi8kVUb_lTES715x93ei2OjJNgs,17376
|
|
136
|
+
schemathesis-3.28.0.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
|
|
137
|
+
schemathesis-3.28.0.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
|
138
|
+
schemathesis-3.28.0.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
|
139
|
+
schemathesis-3.28.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|