schemathesis 3.27.0__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/__init__.py +1 -1
- schemathesis/cli/__main__.py +4 -0
- 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/generation/__init__.py +14 -2
- schemathesis/models.py +18 -2
- schemathesis/runner/impl/core.py +18 -17
- schemathesis/runner/serialization.py +16 -1
- schemathesis/serializers.py +1 -1
- schemathesis/specs/openapi/_hypothesis.py +16 -5
- schemathesis/specs/openapi/references.py +1 -1
- schemathesis/specs/openapi/schemas.py +1 -1
- schemathesis/transports/__init__.py +10 -3
- {schemathesis-3.27.0.dist-info → schemathesis-3.28.0.dist-info}/METADATA +38 -16
- {schemathesis-3.27.0.dist-info → schemathesis-3.28.0.dist-info}/RECORD +21 -20
- {schemathesis-3.27.0.dist-info → schemathesis-3.28.0.dist-info}/WHEEL +0 -0
- {schemathesis-3.27.0.dist-info → schemathesis-3.28.0.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.27.0.dist-info → schemathesis-3.28.0.dist-info}/licenses/LICENSE +0 -0
schemathesis/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any
|
|
|
3
3
|
|
|
4
4
|
from . import auths, checks, experimental, contrib, fixups, graphql, hooks, runner, serializers, targets # noqa: E402
|
|
5
5
|
from ._lazy_import import lazy_import
|
|
6
|
-
from .generation import DataGenerationMethod, GenerationConfig # noqa: E402
|
|
6
|
+
from .generation import DataGenerationMethod, GenerationConfig, HeaderConfig # noqa: E402
|
|
7
7
|
from .constants import SCHEMATHESIS_VERSION # noqa: E402
|
|
8
8
|
from .models import Case # noqa: E402
|
|
9
9
|
from .specs import openapi # noqa: E402
|
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
|
)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import random
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Union, Iterable
|
|
5
|
+
from typing import Union, Iterable, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from hypothesis.strategies import SearchStrategy
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
class DataGenerationMethod(str, Enum):
|
|
@@ -58,6 +61,13 @@ def generate_random_case_id(length: int = 6) -> str:
|
|
|
58
61
|
return output
|
|
59
62
|
|
|
60
63
|
|
|
64
|
+
@dataclass
|
|
65
|
+
class HeaderConfig:
|
|
66
|
+
"""Configuration for generating headers."""
|
|
67
|
+
|
|
68
|
+
strategy: SearchStrategy[str] | None = None
|
|
69
|
+
|
|
70
|
+
|
|
61
71
|
@dataclass
|
|
62
72
|
class GenerationConfig:
|
|
63
73
|
"""Holds various configuration options relevant for data generation."""
|
|
@@ -66,3 +76,5 @@ class GenerationConfig:
|
|
|
66
76
|
allow_x00: bool = True
|
|
67
77
|
# Generate strings using the given codec
|
|
68
78
|
codec: str | None = "utf-8"
|
|
79
|
+
# Header generation configuration
|
|
80
|
+
headers: HeaderConfig = field(default_factory=HeaderConfig)
|
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,
|
|
@@ -299,10 +301,7 @@ def run_probes(schema: BaseSchema, config: probes.ProbeConfig) -> list[probes.Pr
|
|
|
299
301
|
if isinstance(result.probe, probes.NullByteInHeader) and result.is_failure:
|
|
300
302
|
from ...specs.openapi._hypothesis import HEADER_FORMAT, header_values
|
|
301
303
|
|
|
302
|
-
formats.register(
|
|
303
|
-
HEADER_FORMAT,
|
|
304
|
-
header_values(blacklist_characters="\n\r\x00").map(str.lstrip),
|
|
305
|
-
)
|
|
304
|
+
formats.register(HEADER_FORMAT, header_values(blacklist_characters="\n\r\x00"))
|
|
306
305
|
return results
|
|
307
306
|
|
|
308
307
|
|
|
@@ -477,13 +476,25 @@ def run_test(
|
|
|
477
476
|
except SkipTest as exc:
|
|
478
477
|
status = Status.skip
|
|
479
478
|
result.mark_skipped(exc)
|
|
480
|
-
except AssertionError: #
|
|
481
|
-
error = reraise(operation)
|
|
479
|
+
except AssertionError as exc: # May come from `hypothesis-jsonschema` or `hypothesis`
|
|
482
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
|
+
)
|
|
483
494
|
result.add_error(error)
|
|
484
495
|
except HypothesisRefResolutionError:
|
|
485
496
|
status = Status.error
|
|
486
|
-
result.add_error(
|
|
497
|
+
result.add_error(RecursiveReferenceError(RECURSIVE_REFERENCE_ERROR_MESSAGE))
|
|
487
498
|
except InvalidArgument as error:
|
|
488
499
|
status = Status.error
|
|
489
500
|
message = get_invalid_regular_expression_message(warnings)
|
|
@@ -617,16 +628,6 @@ def get_invalid_regular_expression_message(warnings: list[WarningMessage]) -> st
|
|
|
617
628
|
return None
|
|
618
629
|
|
|
619
630
|
|
|
620
|
-
def reraise(operation: APIOperation) -> OperationSchemaError:
|
|
621
|
-
try:
|
|
622
|
-
operation.schema.validate()
|
|
623
|
-
except ValidationError as exc:
|
|
624
|
-
return OperationSchemaError.from_jsonschema_error(
|
|
625
|
-
exc, path=operation.path, method=operation.method, full_path=operation.schema.get_full_path(operation.path)
|
|
626
|
-
)
|
|
627
|
-
return OperationSchemaError("Unknown schema error")
|
|
628
|
-
|
|
629
|
-
|
|
630
631
|
MEMORY_ADDRESS_RE = re.compile("0x[0-9a-fA-F]+")
|
|
631
632
|
URL_IN_ERROR_MESSAGE_RE = re.compile(r"Max retries exceeded with url: .*? \(Caused by")
|
|
632
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):
|
|
@@ -45,7 +45,8 @@ StrategyFactory = Callable[[Dict[str, Any], str, str, Optional[str], GenerationC
|
|
|
45
45
|
def header_values(blacklist_characters: str = "\n\r") -> st.SearchStrategy[str]:
|
|
46
46
|
return st.text(
|
|
47
47
|
alphabet=st.characters(min_codepoint=0, max_codepoint=255, blacklist_characters=blacklist_characters)
|
|
48
|
-
|
|
48
|
+
# Header values with leading non-visible chars can't be sent with `requests`
|
|
49
|
+
).map(str.lstrip)
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
@lru_cache
|
|
@@ -67,8 +68,7 @@ def get_default_format_strategies() -> dict[str, st.SearchStrategy]:
|
|
|
67
68
|
"_header_name": st.text(
|
|
68
69
|
min_size=1, alphabet=st.sampled_from("!#$%&'*+-.^_`|~" + string.digits + string.ascii_letters)
|
|
69
70
|
),
|
|
70
|
-
|
|
71
|
-
HEADER_FORMAT: header_value.map(str.lstrip),
|
|
71
|
+
HEADER_FORMAT: header_value,
|
|
72
72
|
"_basic_auth": st.tuples(latin1_text, latin1_text).map(make_basic_auth_str),
|
|
73
73
|
"_bearer_auth": header_value.map("Bearer {}".format),
|
|
74
74
|
}
|
|
@@ -425,6 +425,15 @@ def jsonify_python_specific_types(value: dict[str, Any]) -> dict[str, Any]:
|
|
|
425
425
|
return value
|
|
426
426
|
|
|
427
427
|
|
|
428
|
+
def _build_custom_formats(
|
|
429
|
+
custom_formats: dict[str, st.SearchStrategy] | None, generation_config: GenerationConfig
|
|
430
|
+
) -> dict[str, st.SearchStrategy]:
|
|
431
|
+
custom_formats = {**get_default_format_strategies(), **STRING_FORMATS, **(custom_formats or {})}
|
|
432
|
+
if generation_config.headers.strategy is not None:
|
|
433
|
+
custom_formats[HEADER_FORMAT] = generation_config.headers.strategy
|
|
434
|
+
return custom_formats
|
|
435
|
+
|
|
436
|
+
|
|
428
437
|
def make_positive_strategy(
|
|
429
438
|
schema: dict[str, Any],
|
|
430
439
|
operation_name: str,
|
|
@@ -441,9 +450,10 @@ def make_positive_strategy(
|
|
|
441
450
|
for sub_schema in schema.get("properties", {}).values():
|
|
442
451
|
if list(sub_schema) == ["type"] and sub_schema["type"] == "string":
|
|
443
452
|
sub_schema.setdefault("format", HEADER_FORMAT)
|
|
453
|
+
custom_formats = _build_custom_formats(custom_formats, generation_config)
|
|
444
454
|
return from_schema(
|
|
445
455
|
schema,
|
|
446
|
-
custom_formats=
|
|
456
|
+
custom_formats=custom_formats,
|
|
447
457
|
allow_x00=generation_config.allow_x00,
|
|
448
458
|
codec=generation_config.codec,
|
|
449
459
|
)
|
|
@@ -462,12 +472,13 @@ def make_negative_strategy(
|
|
|
462
472
|
generation_config: GenerationConfig,
|
|
463
473
|
custom_formats: dict[str, st.SearchStrategy] | None = None,
|
|
464
474
|
) -> st.SearchStrategy:
|
|
475
|
+
custom_formats = _build_custom_formats(custom_formats, generation_config)
|
|
465
476
|
return negative_schema(
|
|
466
477
|
schema,
|
|
467
478
|
operation_name=operation_name,
|
|
468
479
|
location=location,
|
|
469
480
|
media_type=media_type,
|
|
470
|
-
custom_formats=
|
|
481
|
+
custom_formats=custom_formats,
|
|
471
482
|
generation_config=generation_config,
|
|
472
483
|
)
|
|
473
484
|
|
|
@@ -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:
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
4
|
import time
|
|
5
|
+
from inspect import iscoroutinefunction
|
|
5
6
|
from contextlib import contextmanager
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from datetime import timedelta
|
|
@@ -29,13 +30,19 @@ def serialize_payload(payload: bytes) -> str:
|
|
|
29
30
|
return base64.b64encode(payload).decode()
|
|
30
31
|
|
|
31
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
|
+
|
|
32
39
|
def get(app: Any) -> Transport:
|
|
33
40
|
"""Get transport to send the data to the application."""
|
|
34
|
-
from starlette.applications import Starlette
|
|
35
|
-
|
|
36
41
|
if app is None:
|
|
37
42
|
return RequestsTransport()
|
|
38
|
-
if
|
|
43
|
+
if iscoroutinefunction(app) or (
|
|
44
|
+
hasattr(app, "__call__") and iscoroutinefunction(app.__call__) # noqa: B004
|
|
45
|
+
):
|
|
39
46
|
return ASGITransport(app=app)
|
|
40
47
|
return WSGITransport(app=app)
|
|
41
48
|
|
|
@@ -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
|
|
@@ -28,7 +28,6 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
28
28
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
29
29
|
Classifier: Topic :: Software Development :: Testing
|
|
30
30
|
Requires-Python: >=3.8
|
|
31
|
-
Requires-Dist: anyio<4
|
|
32
31
|
Requires-Dist: backoff<3.0,>=2.1.2
|
|
33
32
|
Requires-Dist: click<9.0,>=7.0
|
|
34
33
|
Requires-Dist: colorama<1.0,>=0.4
|
|
@@ -44,7 +43,7 @@ Requires-Dist: pytest-subtests<0.8.0,>=0.2.1
|
|
|
44
43
|
Requires-Dist: pytest<9,>=4.6.4
|
|
45
44
|
Requires-Dist: pyyaml<7.0,>=5.1
|
|
46
45
|
Requires-Dist: requests<3,>=2.22
|
|
47
|
-
Requires-Dist: starlette-testclient<1
|
|
46
|
+
Requires-Dist: starlette-testclient<1,>=0.4.1
|
|
48
47
|
Requires-Dist: starlette<1,>=0.13
|
|
49
48
|
Requires-Dist: tomli-w<2.0,>=1.0.0
|
|
50
49
|
Requires-Dist: tomli<3.0,>=2.0.1
|
|
@@ -78,7 +77,7 @@ Requires-Dist: trustme<1.0,>=0.9.0; extra == 'tests'
|
|
|
78
77
|
Description-Content-Type: text/markdown
|
|
79
78
|
|
|
80
79
|
<p align="center">
|
|
81
|
-
<em>Schemathesis:
|
|
80
|
+
<em>Schemathesis: Supercharge your API testing, catch bugs, and ensure compliance</em>
|
|
82
81
|
</p>
|
|
83
82
|
|
|
84
83
|
<p align="center">
|
|
@@ -110,29 +109,34 @@ Description-Content-Type: text/markdown
|
|
|
110
109
|
|
|
111
110
|
---
|
|
112
111
|
|
|
113
|
-
##
|
|
112
|
+
## What is Schemathesis?
|
|
114
113
|
|
|
115
|
-
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:
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
🎯 **Catch Hard-to-Find Bugs**
|
|
118
117
|
|
|
119
|
-
|
|
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
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
⚡ **Accelerate Testing Cycles**
|
|
122
122
|
|
|
123
|
-
|
|
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
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
🧩 **Integrate Seamlessly**
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
- Works with popular API formats such as OpenAPI, GraphQL.
|
|
129
|
+
- Easily integrate into your existing testing pipeline and CI/CD workflows
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
🔧 **Customize and Extend**
|
|
130
132
|
|
|
131
|
-
|
|
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
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
📊 **Gain Valuable Insights**
|
|
134
137
|
|
|
135
|
-
|
|
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
|
|
136
140
|
|
|
137
141
|
## Quick Demo
|
|
138
142
|
|
|
@@ -310,6 +314,24 @@ This includes:
|
|
|
310
314
|
|
|
311
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>.
|
|
312
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
|
+
|
|
313
335
|
## Additional content
|
|
314
336
|
|
|
315
337
|
### Papers
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
schemathesis/__init__.py,sha256=
|
|
1
|
+
schemathesis/__init__.py,sha256=zAa981OnVdIdjiETPP5a5vZL8QXAh2HbTWj8iEoxKC4,1984
|
|
2
2
|
schemathesis/_compat.py,sha256=VizWR0QAVj4l7WZautNe_zmo3AJ7WBl2zrJQOfJAQtk,1837
|
|
3
3
|
schemathesis/_dependency_versions.py,sha256=InIv6MZmuRVHc_9FxAiRg7_dY-vuF0jT69FBxrBLK6U,879
|
|
4
4
|
schemathesis/_hypothesis.py,sha256=O3rfMbT0rChFONMUsYmMEGV9nPG5cHd_6v9NhWDI_IQ,10763
|
|
@@ -10,36 +10,37 @@ 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
|
|
29
29
|
schemathesis/utils.py,sha256=4HXvHysnHp-Uz2HfNgLfW5F5VjL-mtixrjjzRCEJhYo,5233
|
|
30
30
|
schemathesis/cli/__init__.py,sha256=oTBqweV5CP681LqRFdRXtkd0VyYoG_xUKshkKEPnqIQ,64871
|
|
31
|
+
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
|
31
32
|
schemathesis/cli/callbacks.py,sha256=HmD0WmSYf5x8v4xeZdOKzy_8CEk23NitUlL8JQ7LHdQ,14950
|
|
32
33
|
schemathesis/cli/cassettes.py,sha256=1ucgYyOZiNu44uOv044FdfKTzQolUyhAM2onk4m3MD0,12988
|
|
33
34
|
schemathesis/cli/constants.py,sha256=XoezT0_fHuiOY2e--cmBUhNieJsepcUoW8e48QuLSDI,1544
|
|
34
|
-
schemathesis/cli/context.py,sha256=
|
|
35
|
+
schemathesis/cli/context.py,sha256=Hsbt7YS1Gr01QJ825mECpEduEbRIaRRMUz7k6j1SpVI,2005
|
|
35
36
|
schemathesis/cli/debug.py,sha256=PDEa-oHyz5bZ8aYjRYARwQaCv_AC6HM_L43LJfe4vUM,657
|
|
36
37
|
schemathesis/cli/handlers.py,sha256=62GPWPmgeGUz3pkDd01H4UCXcIy5a9yRtU7qaAeeR-A,389
|
|
37
|
-
schemathesis/cli/junitxml.py,sha256=
|
|
38
|
+
schemathesis/cli/junitxml.py,sha256=WdxtBKBi6V5Ow840Nde21MvUlDJQyJu2M6DXx1VDuyo,4648
|
|
38
39
|
schemathesis/cli/options.py,sha256=7_dXcrPT0kWqAkm60cAT1J0IsTOcvNFxT1pcHYttBuI,2558
|
|
39
|
-
schemathesis/cli/reporting.py,sha256=
|
|
40
|
+
schemathesis/cli/reporting.py,sha256=_Jh8sJMfOf_CUHdbTpG5VRsfW8INtXQjtk1njmGWEnY,3298
|
|
40
41
|
schemathesis/cli/sanitization.py,sha256=v-9rsMCBpWhom0Bfzj_8c6InqxkVjSWK6kRoURa52Nk,727
|
|
41
42
|
schemathesis/cli/output/__init__.py,sha256=AXaUzQ1nhQ-vXhW4-X-91vE2VQtEcCOrGtQXXNN55iQ,29
|
|
42
|
-
schemathesis/cli/output/default.py,sha256=
|
|
43
|
+
schemathesis/cli/output/default.py,sha256=130qttjuzUgYcCD-LnTaMTumoOMWLzu3q-aPOyJ0ogs,38176
|
|
43
44
|
schemathesis/cli/output/short.py,sha256=MmFMOKBRqqvgq7Kmx6XJc7Vqkr9g5BrJBxRbyZ0Rh-k,2068
|
|
44
45
|
schemathesis/contrib/__init__.py,sha256=FH8NL8NXgSKBFOF8Jy_EB6T4CJEaiM-tmDhz16B2o4k,187
|
|
45
46
|
schemathesis/contrib/unique_data.py,sha256=zxrmAlQH7Bcil9YSfy2EazwLj2rxLzOvAE3O6QRRkFY,1317
|
|
@@ -56,7 +57,7 @@ schemathesis/extra/pytest_plugin.py,sha256=44RRkjkN1JupWu0muq9B5b3K9goucYndB394Q
|
|
|
56
57
|
schemathesis/fixups/__init__.py,sha256=iTpOm-cwyZQRglbWRJHxj3k7WO9a4a-MCq-UDQa6U0Y,966
|
|
57
58
|
schemathesis/fixups/fast_api.py,sha256=-gjYNSUtiKl7cKgyQ_rON0Oo_sGm6r7rOQDH0ahG32A,1322
|
|
58
59
|
schemathesis/fixups/utf8_bom.py,sha256=ttzPOaJb6K7QtNAbIZ_TWrrm0N9FylS8QzO6zI0jXgY,764
|
|
59
|
-
schemathesis/generation/__init__.py,sha256=
|
|
60
|
+
schemathesis/generation/__init__.py,sha256=UXRrDTaUEX29VEK9bLqVi_xVzyNNqul3WhpLh1za90M,2277
|
|
60
61
|
schemathesis/internal/__init__.py,sha256=93HcdG3LF0BbQKbCteOsFMa1w6nXl8yTmx87QLNJOik,161
|
|
61
62
|
schemathesis/internal/copy.py,sha256=xsC7RyrFa9KadMsj9ai_aAy0XZg0oplz3cFZCka_2fg,459
|
|
62
63
|
schemathesis/internal/datetime.py,sha256=zPLBL0XXLNfP-KYel3H2m8pnsxjsA_4d-zTOhJg2EPQ,136
|
|
@@ -68,9 +69,9 @@ schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QA
|
|
|
68
69
|
schemathesis/runner/__init__.py,sha256=kkqjC_5G2Mrq00syLNUDHP3sXqwiId8_cusKtIlOyXM,21419
|
|
69
70
|
schemathesis/runner/events.py,sha256=eZc4TT8C8mWCMr9h4JvlijvQQ-x8wFry8LntdsYIL-A,10200
|
|
70
71
|
schemathesis/runner/probes.py,sha256=t_B38-ljy-p3Odw-dqcZUVIjYTPwBvac8KE5GaLnz4M,5546
|
|
71
|
-
schemathesis/runner/serialization.py,sha256=
|
|
72
|
+
schemathesis/runner/serialization.py,sha256=NkeXNQUvzpnTiT4TbojpUC0QXl2Gjv7quPx5HUxpJXI,17356
|
|
72
73
|
schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
|
|
73
|
-
schemathesis/runner/impl/core.py,sha256=
|
|
74
|
+
schemathesis/runner/impl/core.py,sha256=E38yRz4c5hc_QjZnMSvZwbRYjt6VpoS2KxH2V7l3RIk,39802
|
|
74
75
|
schemathesis/runner/impl/solo.py,sha256=Y_tNhVBVxcuxv65hN0FjxLlVSC41ebHMOM1xSzVrNk8,3358
|
|
75
76
|
schemathesis/runner/impl/threadpool.py,sha256=2-2Wvw7msezZtenZY5vU_x3FGLLVlH-ywvhU9hTwAAo,15073
|
|
76
77
|
schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
|
|
@@ -94,7 +95,7 @@ schemathesis/specs/graphql/scalars.py,sha256=W5oj6AcjiXpR-Z6eSSp1oPWl0mjH2NF-w87
|
|
|
94
95
|
schemathesis/specs/graphql/schemas.py,sha256=6BlTF9iAongsDxzy4_l85YjDob2QzpB0Vt7mlbIUxaw,12637
|
|
95
96
|
schemathesis/specs/graphql/validation.py,sha256=SqQbj9uymGUQxlHXc8HkQccIq25uwP5CvLF1zReb1Xg,1636
|
|
96
97
|
schemathesis/specs/openapi/__init__.py,sha256=HDcx3bqpa6qWPpyMrxAbM3uTo0Lqpg-BUNZhDJSJKnw,279
|
|
97
|
-
schemathesis/specs/openapi/_hypothesis.py,sha256=
|
|
98
|
+
schemathesis/specs/openapi/_hypothesis.py,sha256=Qrm6Po2W2wsxZAlLFRUwq0svO8SmQp-5InCFa2cwYaw,23275
|
|
98
99
|
schemathesis/specs/openapi/checks.py,sha256=1WB_UGNqptfJThWLUNbds1Q-IzOGbbHCOYPIhBYk-zs,5411
|
|
99
100
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
|
100
101
|
schemathesis/specs/openapi/converter.py,sha256=9TKeKvNi9MVvoNMfqoPz_cODO8oNrMSTXTOwLLfjD_Q,2799
|
|
@@ -106,8 +107,8 @@ schemathesis/specs/openapi/links.py,sha256=FUjqEf6Sv6PeS0UX7CLL5p2VHa9vA1MGDPhx2
|
|
|
106
107
|
schemathesis/specs/openapi/loaders.py,sha256=QoEFJ7fxJz5XcGTEWV_ZGXARiektmyyT_xmVtcHco6Q,24131
|
|
107
108
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
|
108
109
|
schemathesis/specs/openapi/parameters.py,sha256=5jMZQZFsZNBFjG22Bqot-Rc65emiSA4E95rIzwImThk,13610
|
|
109
|
-
schemathesis/specs/openapi/references.py,sha256=
|
|
110
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
|
110
|
+
schemathesis/specs/openapi/references.py,sha256=zEDzoJbUAwVYTy9CtJ9w1E1Xzp60CVsjebDkFjMXnno,8982
|
|
111
|
+
schemathesis/specs/openapi/schemas.py,sha256=oFRfFucQ3Sf5iZvnVCGJDW0MH7lyA1pSAn7S7bnaSmQ,50466
|
|
111
112
|
schemathesis/specs/openapi/security.py,sha256=nCUIaZTzI6t26HAfd8YTHW6mFxXAPN9Ds-P9UnXxmNA,6628
|
|
112
113
|
schemathesis/specs/openapi/serialization.py,sha256=jajqowTIiyEVWEx-Gy4ZinXZewNg0n_ipsGzz7JXP7c,11383
|
|
113
114
|
schemathesis/specs/openapi/utils.py,sha256=gmW4v6o6sZQifajfPquhFeWmZWGQM89mOOBYZvjnE7g,741
|
|
@@ -126,13 +127,13 @@ schemathesis/specs/openapi/stateful/__init__.py,sha256=R8RhPJD3rDzqL4eA9jSnUwh9Q
|
|
|
126
127
|
schemathesis/specs/openapi/stateful/links.py,sha256=cSIwho2Hroty6au-gyCD-OwqnuCcIpnIIHU6FvF0SwA,3533
|
|
127
128
|
schemathesis/stateful/__init__.py,sha256=T7rvhzftfl3wumEsir33DBBzCTK2PtRp9CxBxMLdMSE,4693
|
|
128
129
|
schemathesis/stateful/state_machine.py,sha256=ZcKpbvEl1QGhVOYnA7Ow6zkuFHtEPDAyCjroPrj-FgU,11343
|
|
129
|
-
schemathesis/transports/__init__.py,sha256=
|
|
130
|
+
schemathesis/transports/__init__.py,sha256=vQ5pyMefhVpbl0FFXtivGsFHWX-7V1PXNwvqCKMJHoI,11423
|
|
130
131
|
schemathesis/transports/auth.py,sha256=ZKFku9gjhIG6445qNC2p_64Yt9Iz_4azbvja8AMptBk,404
|
|
131
132
|
schemathesis/transports/content_types.py,sha256=xU8RZWxz-CyWRqQTI2fGYQacB7KasoY7LL_bxPQdyPY,2144
|
|
132
133
|
schemathesis/transports/headers.py,sha256=EDxpm8su0AuhyqZUkMex-OFZMAJN_5NHah7fDT2HDZE,989
|
|
133
134
|
schemathesis/transports/responses.py,sha256=j_-udvWbmi6XtEYmpdX8WoFnlrQ6-i3PSBizfozRjQI,1673
|
|
134
|
-
schemathesis-3.
|
|
135
|
-
schemathesis-3.
|
|
136
|
-
schemathesis-3.
|
|
137
|
-
schemathesis-3.
|
|
138
|
-
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
|