schemathesis 3.30.0__py3-none-any.whl → 3.30.2__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/__init__.py +20 -0
- schemathesis/cli/context.py +2 -0
- schemathesis/cli/junitxml.py +5 -4
- schemathesis/cli/output/default.py +7 -3
- schemathesis/cli/reporting.py +19 -14
- schemathesis/exceptions.py +2 -35
- schemathesis/failures.py +10 -5
- schemathesis/internal/output.py +68 -0
- schemathesis/lazy.py +8 -0
- schemathesis/models.py +2 -2
- schemathesis/runner/events.py +1 -0
- schemathesis/runner/impl/core.py +13 -2
- schemathesis/schemas.py +6 -0
- schemathesis/service/serialization.py +22 -14
- schemathesis/specs/graphql/loaders.py +16 -0
- schemathesis/specs/openapi/checks.py +6 -1
- schemathesis/specs/openapi/loaders.py +18 -0
- schemathesis/specs/openapi/schemas.py +1 -1
- schemathesis/stateful/context.py +13 -3
- schemathesis/stateful/events.py +7 -1
- schemathesis/stateful/runner.py +8 -3
- {schemathesis-3.30.0.dist-info → schemathesis-3.30.2.dist-info}/METADATA +3 -3
- {schemathesis-3.30.0.dist-info → schemathesis-3.30.2.dist-info}/RECORD +26 -25
- {schemathesis-3.30.0.dist-info → schemathesis-3.30.2.dist-info}/WHEEL +0 -0
- {schemathesis-3.30.0.dist-info → schemathesis-3.30.2.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.30.0.dist-info → schemathesis-3.30.2.dist-info}/licenses/LICENSE +0 -0
schemathesis/cli/__init__.py
CHANGED
|
@@ -38,6 +38,7 @@ from ..fixups import ALL_FIXUPS
|
|
|
38
38
|
from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod
|
|
39
39
|
from ..hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, HookScope
|
|
40
40
|
from ..internal.datetime import current_datetime
|
|
41
|
+
from ..internal.output import OutputConfig
|
|
41
42
|
from ..internal.validation import file_exists
|
|
42
43
|
from ..loaders import load_app, load_yaml
|
|
43
44
|
from ..models import Case, CheckFunction
|
|
@@ -694,6 +695,14 @@ The report data, consisting of a tar gz file with multiple JSON files, is subjec
|
|
|
694
695
|
callback=callbacks.convert_experimental,
|
|
695
696
|
multiple=True,
|
|
696
697
|
)
|
|
698
|
+
@click.option(
|
|
699
|
+
"--output-truncate",
|
|
700
|
+
help="Specifies whether to truncate schemas and responses in error messages.",
|
|
701
|
+
type=str,
|
|
702
|
+
default="true",
|
|
703
|
+
show_default=True,
|
|
704
|
+
callback=callbacks.convert_boolean_string,
|
|
705
|
+
)
|
|
697
706
|
@click.option(
|
|
698
707
|
"--generation-allow-x00",
|
|
699
708
|
help="Determines whether to allow the generation of `\x00` bytes within strings.",
|
|
@@ -783,6 +792,7 @@ def run(
|
|
|
783
792
|
stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
|
|
784
793
|
force_schema_version: str | None = None,
|
|
785
794
|
sanitize_output: bool = True,
|
|
795
|
+
output_truncate: bool = True,
|
|
786
796
|
contrib_unique_data: bool = False,
|
|
787
797
|
contrib_openapi_formats_uuid: bool = False,
|
|
788
798
|
contrib_openapi_fill_missing_examples: bool = False,
|
|
@@ -863,6 +873,8 @@ def run(
|
|
|
863
873
|
click.secho(DEPRECATED_CASSETTE_PATH_OPTION_WARNING, fg="yellow")
|
|
864
874
|
cassette_path = store_network_log
|
|
865
875
|
|
|
876
|
+
output_config = OutputConfig(truncate=output_truncate)
|
|
877
|
+
|
|
866
878
|
schemathesis_io_hostname = urlparse(schemathesis_io_url).netloc
|
|
867
879
|
token = schemathesis_io_token or service.hosts.get_token(hostname=schemathesis_io_hostname, hosts_file=hosts_file)
|
|
868
880
|
schema_kind = callbacks.parse_schema_kind(schema, app)
|
|
@@ -982,6 +994,7 @@ def run(
|
|
|
982
994
|
stateful_recursion_limit=stateful_recursion_limit,
|
|
983
995
|
hypothesis_settings=hypothesis_settings,
|
|
984
996
|
generation_config=generation_config,
|
|
997
|
+
output_config=output_config,
|
|
985
998
|
service_client=client,
|
|
986
999
|
)
|
|
987
1000
|
execute(
|
|
@@ -1008,6 +1021,7 @@ def run(
|
|
|
1008
1021
|
location=schema,
|
|
1009
1022
|
base_url=base_url,
|
|
1010
1023
|
started_at=started_at,
|
|
1024
|
+
output_config=output_config,
|
|
1011
1025
|
)
|
|
1012
1026
|
|
|
1013
1027
|
|
|
@@ -1036,6 +1050,7 @@ class LoaderConfig:
|
|
|
1036
1050
|
request_cert: RequestCert | None
|
|
1037
1051
|
wait_for_schema: float | None
|
|
1038
1052
|
rate_limit: str | None
|
|
1053
|
+
output_config: OutputConfig
|
|
1039
1054
|
# Network request parameters
|
|
1040
1055
|
auth: tuple[str, str] | None
|
|
1041
1056
|
auth_type: str | None
|
|
@@ -1079,6 +1094,7 @@ def into_event_stream(
|
|
|
1079
1094
|
workers_num: int,
|
|
1080
1095
|
hypothesis_settings: hypothesis.settings | None,
|
|
1081
1096
|
generation_config: generation.GenerationConfig,
|
|
1097
|
+
output_config: OutputConfig,
|
|
1082
1098
|
seed: int | None,
|
|
1083
1099
|
exit_first: bool,
|
|
1084
1100
|
max_failures: int | None,
|
|
@@ -1112,6 +1128,7 @@ def into_event_stream(
|
|
|
1112
1128
|
method=method or None,
|
|
1113
1129
|
tag=tag or None,
|
|
1114
1130
|
operation_id=operation_id or None,
|
|
1131
|
+
output_config=output_config,
|
|
1115
1132
|
)
|
|
1116
1133
|
schema = load_schema(config)
|
|
1117
1134
|
yield from runner.from_schema(
|
|
@@ -1257,6 +1274,7 @@ def get_loader_kwargs(loader: Callable, config: LoaderConfig) -> dict[str, Any]:
|
|
|
1257
1274
|
"force_schema_version": config.force_schema_version,
|
|
1258
1275
|
"data_generation_methods": config.data_generation_methods,
|
|
1259
1276
|
"rate_limit": config.rate_limit,
|
|
1277
|
+
"output_config": config.output_config,
|
|
1260
1278
|
}
|
|
1261
1279
|
if loader not in (oas_loaders.from_path, oas_loaders.from_dict):
|
|
1262
1280
|
kwargs["headers"] = config.headers
|
|
@@ -1384,6 +1402,7 @@ def execute(
|
|
|
1384
1402
|
location: str,
|
|
1385
1403
|
base_url: str | None,
|
|
1386
1404
|
started_at: str,
|
|
1405
|
+
output_config: OutputConfig,
|
|
1387
1406
|
) -> None:
|
|
1388
1407
|
"""Execute a prepared runner by drawing events from it and passing to a proper handler."""
|
|
1389
1408
|
handlers: list[EventHandler] = []
|
|
@@ -1447,6 +1466,7 @@ def execute(
|
|
|
1447
1466
|
verbosity=verbosity,
|
|
1448
1467
|
code_sample_style=code_sample_style,
|
|
1449
1468
|
report=report_context,
|
|
1469
|
+
output_config=output_config,
|
|
1450
1470
|
)
|
|
1451
1471
|
|
|
1452
1472
|
def shutdown() -> None:
|
schemathesis/cli/context.py
CHANGED
|
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
|
|
9
9
|
from ..code_samples import CodeSampleStyle
|
|
10
10
|
from ..internal.deprecation import deprecated_property
|
|
11
|
+
from ..internal.output import OutputConfig
|
|
11
12
|
from ..internal.result import Result
|
|
12
13
|
from ..runner.probes import ProbeRun
|
|
13
14
|
from ..runner.serialization import SerializedTestResult
|
|
@@ -57,6 +58,7 @@ class ExecutionContext:
|
|
|
57
58
|
report: ServiceReportContext | FileReportContext | None = None
|
|
58
59
|
probes: list[ProbeRun] | None = None
|
|
59
60
|
analysis: Result[AnalysisResult, Exception] | None = None
|
|
61
|
+
output_config: OutputConfig = field(default_factory=OutputConfig)
|
|
60
62
|
# Special flag to display a warning about Windows-specific encoding issue
|
|
61
63
|
encountered_windows_encoding_issue: bool = False
|
|
62
64
|
state_machine_sink: StateMachineSink | None = None
|
schemathesis/cli/junitxml.py
CHANGED
|
@@ -7,7 +7,8 @@ from typing import TYPE_CHECKING, cast
|
|
|
7
7
|
|
|
8
8
|
from junit_xml import TestCase, TestSuite, to_xml_report_file
|
|
9
9
|
|
|
10
|
-
from ..exceptions import RuntimeErrorType
|
|
10
|
+
from ..exceptions import RuntimeErrorType
|
|
11
|
+
from ..internal.output import prepare_response_payload
|
|
11
12
|
from ..models import Status
|
|
12
13
|
from ..runner import events
|
|
13
14
|
from ..runner.serialization import SerializedCheck, SerializedError
|
|
@@ -37,7 +38,7 @@ class JunitXMLHandler(EventHandler):
|
|
|
37
38
|
group_by_case(event.result.checks, context.code_sample_style), 1
|
|
38
39
|
):
|
|
39
40
|
checks = sorted(group, key=lambda c: c.name != "not_a_server_error")
|
|
40
|
-
test_case.add_failure_info(message=build_failure_message(idx, code_sample, checks))
|
|
41
|
+
test_case.add_failure_info(message=build_failure_message(context, idx, code_sample, checks))
|
|
41
42
|
elif event.status == Status.error:
|
|
42
43
|
test_case.add_error_info(message=build_error_message(context, event.result.errors[-1]))
|
|
43
44
|
elif event.status == Status.skip:
|
|
@@ -48,7 +49,7 @@ class JunitXMLHandler(EventHandler):
|
|
|
48
49
|
to_xml_report_file(file_descriptor=self.file_handle, test_suites=test_suites, prettyprint=True)
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def build_failure_message(idx: int, code_sample: str, checks: list[SerializedCheck]) -> str:
|
|
52
|
+
def build_failure_message(context: ExecutionContext, idx: int, code_sample: str, checks: list[SerializedCheck]) -> str:
|
|
52
53
|
from ..transports.responses import get_reason
|
|
53
54
|
|
|
54
55
|
message = ""
|
|
@@ -73,7 +74,7 @@ def build_failure_message(idx: int, code_sample: str, checks: list[SerializedChe
|
|
|
73
74
|
# Checked that is not None
|
|
74
75
|
body = cast(bytes, check.response.deserialize_body())
|
|
75
76
|
payload = body.decode(encoding)
|
|
76
|
-
payload = prepare_response_payload(payload)
|
|
77
|
+
payload = prepare_response_payload(payload, config=context.output_config)
|
|
77
78
|
payload = textwrap.indent(f"\n`{payload}`\n", prefix=" ")
|
|
78
79
|
message += payload
|
|
79
80
|
except UnicodeDecodeError:
|
|
@@ -26,9 +26,9 @@ from ...exceptions import (
|
|
|
26
26
|
RuntimeErrorType,
|
|
27
27
|
extract_requests_exception_details,
|
|
28
28
|
format_exception,
|
|
29
|
-
prepare_response_payload,
|
|
30
29
|
)
|
|
31
30
|
from ...experimental import GLOBAL_EXPERIMENTS
|
|
31
|
+
from ...internal.output import prepare_response_payload
|
|
32
32
|
from ...internal.result import Ok
|
|
33
33
|
from ...models import Status
|
|
34
34
|
from ...runner import events
|
|
@@ -314,7 +314,7 @@ def display_failures_for_single_test(context: ExecutionContext, result: Serializ
|
|
|
314
314
|
# Checked that is not None
|
|
315
315
|
body = cast(bytes, check.response.deserialize_body())
|
|
316
316
|
payload = body.decode(encoding)
|
|
317
|
-
payload = prepare_response_payload(payload)
|
|
317
|
+
payload = prepare_response_payload(payload, config=context.output_config)
|
|
318
318
|
payload = textwrap.indent(f"\n`{payload}`", prefix=" ")
|
|
319
319
|
click.echo(payload)
|
|
320
320
|
except UnicodeDecodeError:
|
|
@@ -878,7 +878,11 @@ def handle_stateful_event(context: ExecutionContext, event: events.StatefulEvent
|
|
|
878
878
|
if not experimental.STATEFUL_ONLY.is_enabled:
|
|
879
879
|
click.echo()
|
|
880
880
|
click.secho("Stateful tests\n", bold=True)
|
|
881
|
-
elif
|
|
881
|
+
elif (
|
|
882
|
+
isinstance(event.data, stateful_events.ScenarioFinished)
|
|
883
|
+
and not event.data.is_final
|
|
884
|
+
and event.data.status != stateful_events.ScenarioStatus.REJECTED
|
|
885
|
+
):
|
|
882
886
|
display_execution_result(context, event.data.status.value)
|
|
883
887
|
elif isinstance(event.data, stateful_events.RunFinished):
|
|
884
888
|
click.echo()
|
schemathesis/cli/reporting.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from itertools import groupby
|
|
4
|
-
from typing import Callable, Generator
|
|
4
|
+
from typing import Callable, Generator, Iterator
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
@@ -14,20 +14,25 @@ TEST_CASE_ID_TITLE = "Test Case ID"
|
|
|
14
14
|
|
|
15
15
|
def group_by_case(
|
|
16
16
|
checks: list[SerializedCheck], code_sample_style: CodeSampleStyle
|
|
17
|
-
) -> Generator[tuple[str,
|
|
17
|
+
) -> Generator[tuple[str, Iterator[SerializedCheck]], None, None]:
|
|
18
18
|
checks = deduplicate_failures(checks)
|
|
19
|
-
checks = sorted(checks, key=lambda c:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
checks = sorted(checks, key=lambda c: _by_unique_key(c, code_sample_style))
|
|
20
|
+
for (sample, _, _), gen in groupby(checks, lambda c: _by_unique_key(c, code_sample_style)):
|
|
21
|
+
yield (sample, gen)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _by_unique_key(check: SerializedCheck, code_sample_style: CodeSampleStyle) -> tuple[str, int | None, str | None]:
|
|
25
|
+
return (
|
|
26
|
+
code_sample_style.generate(
|
|
27
|
+
method=check.example.method,
|
|
28
|
+
url=check.example.url,
|
|
29
|
+
body=check.example.deserialize_body(),
|
|
30
|
+
headers=check.example.headers,
|
|
31
|
+
verify=check.example.verify,
|
|
32
|
+
extra_headers=check.example.extra_headers,
|
|
33
|
+
),
|
|
34
|
+
None if not check.response else check.response.status_code,
|
|
35
|
+
None if not check.response else check.response.body,
|
|
31
36
|
)
|
|
32
37
|
|
|
33
38
|
|
schemathesis/exceptions.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
|
-
import json
|
|
5
4
|
import re
|
|
6
5
|
import traceback
|
|
7
6
|
from dataclasses import dataclass, field
|
|
@@ -12,6 +11,7 @@ from typing import TYPE_CHECKING, Any, Callable, Generator, NoReturn
|
|
|
12
11
|
|
|
13
12
|
from .constants import SERIALIZERS_SUGGESTION_MESSAGE
|
|
14
13
|
from .failures import FailureContext
|
|
14
|
+
from .internal.output import truncate_json
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
import hypothesis.errors
|
|
@@ -207,7 +207,7 @@ class OperationSchemaError(Exception):
|
|
|
207
207
|
message = "Invalid schema definition"
|
|
208
208
|
error_path = " -> ".join(str(entry) for entry in error.path) or "[root]"
|
|
209
209
|
message += f"\n\nLocation:\n {error_path}"
|
|
210
|
-
instance =
|
|
210
|
+
instance = truncate_json(error.instance)
|
|
211
211
|
message += f"\n\nProblematic definition:\n{instance}"
|
|
212
212
|
message += "\n\nError details:\n "
|
|
213
213
|
# This default message contains the instance which we already printed
|
|
@@ -300,39 +300,6 @@ class InvalidHeadersExample(OperationSchemaError):
|
|
|
300
300
|
return cls(message)
|
|
301
301
|
|
|
302
302
|
|
|
303
|
-
def truncated_json(data: Any, max_lines: int = 10, max_width: int = 80) -> str:
|
|
304
|
-
# Convert JSON to string with indentation
|
|
305
|
-
indent = 4
|
|
306
|
-
serialized = json.dumps(data, indent=indent)
|
|
307
|
-
|
|
308
|
-
# Split string by lines
|
|
309
|
-
|
|
310
|
-
lines = [line[: max_width - 3] + "..." if len(line) > max_width else line for line in serialized.split("\n")]
|
|
311
|
-
|
|
312
|
-
if len(lines) <= max_lines:
|
|
313
|
-
return "\n".join(lines)
|
|
314
|
-
|
|
315
|
-
truncated_lines = lines[: max_lines - 1]
|
|
316
|
-
indentation = " " * indent
|
|
317
|
-
truncated_lines.append(f"{indentation}// Output truncated...")
|
|
318
|
-
truncated_lines.append(lines[-1])
|
|
319
|
-
|
|
320
|
-
return "\n".join(truncated_lines)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
MAX_PAYLOAD_SIZE = 512
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def prepare_response_payload(payload: str, max_size: int = MAX_PAYLOAD_SIZE) -> str:
|
|
327
|
-
if payload.endswith("\r\n"):
|
|
328
|
-
payload = payload[:-2]
|
|
329
|
-
elif payload.endswith("\n"):
|
|
330
|
-
payload = payload[:-1]
|
|
331
|
-
if len(payload) > max_size:
|
|
332
|
-
payload = payload[:max_size] + " // Output truncated..."
|
|
333
|
-
return payload
|
|
334
|
-
|
|
335
|
-
|
|
336
303
|
class DeadlineExceeded(Exception):
|
|
337
304
|
"""Test took too long to run."""
|
|
338
305
|
|
schemathesis/failures.py
CHANGED
|
@@ -5,6 +5,8 @@ from dataclasses import dataclass
|
|
|
5
5
|
from json import JSONDecodeError
|
|
6
6
|
from typing import TYPE_CHECKING, Any
|
|
7
7
|
|
|
8
|
+
from schemathesis.internal.output import OutputConfig
|
|
9
|
+
|
|
8
10
|
if TYPE_CHECKING:
|
|
9
11
|
from graphql.error import GraphQLFormattedError
|
|
10
12
|
from jsonschema import ValidationError
|
|
@@ -42,11 +44,14 @@ class ValidationErrorContext(FailureContext):
|
|
|
42
44
|
return ("/".join(map(str, self.schema_path)),)
|
|
43
45
|
|
|
44
46
|
@classmethod
|
|
45
|
-
def from_exception(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
def from_exception(
|
|
48
|
+
cls, exc: ValidationError, *, output_config: OutputConfig | None = None
|
|
49
|
+
) -> ValidationErrorContext:
|
|
50
|
+
from .internal.output import truncate_json
|
|
51
|
+
|
|
52
|
+
output_config = OutputConfig.from_parent(output_config, max_lines=20)
|
|
53
|
+
schema = textwrap.indent(truncate_json(exc.schema, config=output_config), prefix=" ")
|
|
54
|
+
value = textwrap.indent(truncate_json(exc.instance, config=output_config), prefix=" ")
|
|
50
55
|
schema_path = list(exc.absolute_schema_path)
|
|
51
56
|
if len(schema_path) > 1:
|
|
52
57
|
# Exclude the last segment, which is already in the schema
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass, replace
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
TRUNCATED = "// Output truncated..."
|
|
8
|
+
MAX_PAYLOAD_SIZE = 512
|
|
9
|
+
MAX_LINES = 10
|
|
10
|
+
MAX_WIDTH = 80
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class OutputConfig:
|
|
15
|
+
"""Options for configuring various aspects of Schemathesis output."""
|
|
16
|
+
|
|
17
|
+
truncate: bool = True
|
|
18
|
+
max_payload_size: int = MAX_PAYLOAD_SIZE
|
|
19
|
+
max_lines: int = MAX_LINES
|
|
20
|
+
max_width: int = MAX_WIDTH
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_parent(cls, parent: OutputConfig | None = None, **changes: Any) -> OutputConfig:
|
|
24
|
+
parent = parent or OutputConfig()
|
|
25
|
+
return parent.replace(**changes)
|
|
26
|
+
|
|
27
|
+
def replace(self, **changes: Any) -> OutputConfig:
|
|
28
|
+
"""Create a new instance with updated values."""
|
|
29
|
+
return replace(self, **changes)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def truncate_json(data: Any, *, config: OutputConfig | None = None) -> str:
|
|
33
|
+
config = config or OutputConfig()
|
|
34
|
+
# Convert JSON to string with indentation
|
|
35
|
+
indent = 4
|
|
36
|
+
serialized = json.dumps(data, indent=indent)
|
|
37
|
+
if not config.truncate:
|
|
38
|
+
return serialized
|
|
39
|
+
|
|
40
|
+
# Split string by lines
|
|
41
|
+
|
|
42
|
+
lines = [
|
|
43
|
+
line[: config.max_width - 3] + "..." if len(line) > config.max_width else line
|
|
44
|
+
for line in serialized.split("\n")
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
if len(lines) <= config.max_lines:
|
|
48
|
+
return "\n".join(lines)
|
|
49
|
+
|
|
50
|
+
truncated_lines = lines[: config.max_lines - 1]
|
|
51
|
+
indentation = " " * indent
|
|
52
|
+
truncated_lines.append(f"{indentation}{TRUNCATED}")
|
|
53
|
+
truncated_lines.append(lines[-1])
|
|
54
|
+
|
|
55
|
+
return "\n".join(truncated_lines)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def prepare_response_payload(payload: str, *, config: OutputConfig | None = None) -> str:
|
|
59
|
+
if payload.endswith("\r\n"):
|
|
60
|
+
payload = payload[:-2]
|
|
61
|
+
elif payload.endswith("\n"):
|
|
62
|
+
payload = payload[:-1]
|
|
63
|
+
config = config or OutputConfig()
|
|
64
|
+
if not config.truncate:
|
|
65
|
+
return payload
|
|
66
|
+
if len(payload) > config.max_payload_size:
|
|
67
|
+
payload = payload[: config.max_payload_size] + f" {TRUNCATED}"
|
|
68
|
+
return payload
|
schemathesis/lazy.py
CHANGED
|
@@ -21,6 +21,7 @@ from .constants import FLAKY_FAILURE_MESSAGE, NOT_SET
|
|
|
21
21
|
from .exceptions import CheckFailed, OperationSchemaError, SkipTest, get_grouped_exception
|
|
22
22
|
from .generation import DataGenerationMethodInput, GenerationConfig
|
|
23
23
|
from .hooks import HookDispatcher, HookScope
|
|
24
|
+
from .internal.output import OutputConfig
|
|
24
25
|
from .internal.result import Ok
|
|
25
26
|
from .models import APIOperation
|
|
26
27
|
from .schemas import BaseSchema
|
|
@@ -52,6 +53,7 @@ class LazySchema:
|
|
|
52
53
|
skip_deprecated_operations: bool = False
|
|
53
54
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET
|
|
54
55
|
generation_config: GenerationConfig | NotSet = NOT_SET
|
|
56
|
+
output_config: OutputConfig | NotSet = NOT_SET
|
|
55
57
|
code_sample_style: CodeSampleStyle = CodeSampleStyle.default()
|
|
56
58
|
rate_limiter: Limiter | None = None
|
|
57
59
|
sanitize_output: bool = True
|
|
@@ -69,6 +71,7 @@ class LazySchema:
|
|
|
69
71
|
skip_deprecated_operations: bool | NotSet = NOT_SET,
|
|
70
72
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
|
|
71
73
|
generation_config: GenerationConfig | NotSet = NOT_SET,
|
|
74
|
+
output_config: OutputConfig | NotSet = NOT_SET,
|
|
72
75
|
code_sample_style: str | NotSet = NOT_SET,
|
|
73
76
|
) -> Callable:
|
|
74
77
|
if method is NOT_SET:
|
|
@@ -83,6 +86,8 @@ class LazySchema:
|
|
|
83
86
|
data_generation_methods = self.data_generation_methods
|
|
84
87
|
if generation_config is NOT_SET:
|
|
85
88
|
generation_config = self.generation_config
|
|
89
|
+
if output_config is NOT_SET:
|
|
90
|
+
output_config = self.output_config
|
|
86
91
|
if isinstance(code_sample_style, str):
|
|
87
92
|
_code_sample_style = CodeSampleStyle.from_str(code_sample_style)
|
|
88
93
|
else:
|
|
@@ -122,6 +127,7 @@ class LazySchema:
|
|
|
122
127
|
skip_deprecated_operations=skip_deprecated_operations,
|
|
123
128
|
data_generation_methods=data_generation_methods,
|
|
124
129
|
generation_config=generation_config,
|
|
130
|
+
output_config=output_config,
|
|
125
131
|
code_sample_style=_code_sample_style,
|
|
126
132
|
app=self.app,
|
|
127
133
|
rate_limiter=self.rate_limiter,
|
|
@@ -326,6 +332,7 @@ def get_schema(
|
|
|
326
332
|
skip_deprecated_operations: bool | NotSet = NOT_SET,
|
|
327
333
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
|
|
328
334
|
generation_config: GenerationConfig | NotSet = NOT_SET,
|
|
335
|
+
output_config: OutputConfig | NotSet = NOT_SET,
|
|
329
336
|
code_sample_style: CodeSampleStyle,
|
|
330
337
|
rate_limiter: Limiter | None,
|
|
331
338
|
sanitize_output: bool,
|
|
@@ -348,6 +355,7 @@ def get_schema(
|
|
|
348
355
|
skip_deprecated_operations=skip_deprecated_operations,
|
|
349
356
|
data_generation_methods=data_generation_methods,
|
|
350
357
|
generation_config=generation_config,
|
|
358
|
+
output_config=output_config,
|
|
351
359
|
code_sample_style=code_sample_style,
|
|
352
360
|
rate_limiter=rate_limiter,
|
|
353
361
|
sanitize_output=sanitize_output,
|
schemathesis/models.py
CHANGED
|
@@ -46,12 +46,12 @@ from .exceptions import (
|
|
|
46
46
|
deduplicate_failed_checks,
|
|
47
47
|
get_grouped_exception,
|
|
48
48
|
maybe_set_assertion_message,
|
|
49
|
-
prepare_response_payload,
|
|
50
49
|
)
|
|
51
50
|
from .generation import DataGenerationMethod, GenerationConfig, generate_random_case_id
|
|
52
51
|
from .hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, dispatch
|
|
53
52
|
from .internal.copy import fast_deepcopy
|
|
54
53
|
from .internal.deprecation import deprecated_function, deprecated_property
|
|
54
|
+
from .internal.output import prepare_response_payload
|
|
55
55
|
from .parameters import Parameter, ParameterSet, PayloadAlternatives
|
|
56
56
|
from .sanitization import sanitize_request, sanitize_response
|
|
57
57
|
from .serializers import Serializer
|
|
@@ -420,7 +420,7 @@ class Case:
|
|
|
420
420
|
if not payload:
|
|
421
421
|
formatted += "\n\n <EMPTY>"
|
|
422
422
|
else:
|
|
423
|
-
payload = prepare_response_payload(payload)
|
|
423
|
+
payload = prepare_response_payload(payload, config=self.operation.schema.output_config)
|
|
424
424
|
payload = textwrap.indent(f"\n`{payload}`", prefix=" ")
|
|
425
425
|
formatted += f"\n{payload}"
|
|
426
426
|
code_sample_style = (
|
schemathesis/runner/events.py
CHANGED
schemathesis/runner/impl/core.py
CHANGED
|
@@ -6,6 +6,7 @@ import threading
|
|
|
6
6
|
import time
|
|
7
7
|
import unittest
|
|
8
8
|
import uuid
|
|
9
|
+
import warnings
|
|
9
10
|
from contextlib import contextmanager
|
|
10
11
|
from dataclasses import dataclass, field
|
|
11
12
|
from types import TracebackType
|
|
@@ -20,6 +21,7 @@ from hypothesis_jsonschema._canonicalise import HypothesisRefResolutionError
|
|
|
20
21
|
from jsonschema.exceptions import SchemaError as JsonSchemaError
|
|
21
22
|
from jsonschema.exceptions import ValidationError
|
|
22
23
|
from requests.auth import HTTPDigestAuth, _basic_auth_str
|
|
24
|
+
from urllib3.exceptions import InsecureRequestWarning
|
|
23
25
|
|
|
24
26
|
from ... import experimental, failures, hooks
|
|
25
27
|
from ..._compat import MultipleFailures
|
|
@@ -192,6 +194,7 @@ class BaseRunner:
|
|
|
192
194
|
return
|
|
193
195
|
|
|
194
196
|
try:
|
|
197
|
+
warnings.simplefilter("ignore", InsecureRequestWarning)
|
|
195
198
|
if not experimental.STATEFUL_ONLY.is_enabled:
|
|
196
199
|
yield from self._execute(results, stop_event)
|
|
197
200
|
yield from self._run_stateful_tests(results)
|
|
@@ -216,7 +219,7 @@ class BaseRunner:
|
|
|
216
219
|
|
|
217
220
|
def _run_stateful_tests(self, results: TestResultSet) -> Generator[events.ExecutionEvent, None, None]:
|
|
218
221
|
# Run new-style stateful tests
|
|
219
|
-
if self.stateful is not None and experimental.STATEFUL_TEST_RUNNER.is_enabled:
|
|
222
|
+
if self.stateful is not None and experimental.STATEFUL_TEST_RUNNER.is_enabled and self.schema.links_count > 0:
|
|
220
223
|
result = TestResult(
|
|
221
224
|
method="",
|
|
222
225
|
path="",
|
|
@@ -232,13 +235,21 @@ class BaseRunner:
|
|
|
232
235
|
)
|
|
233
236
|
state_machine = self.schema.as_state_machine()
|
|
234
237
|
runner = state_machine.runner(config=config)
|
|
238
|
+
status = Status.success
|
|
235
239
|
for stateful_event in runner.execute():
|
|
236
240
|
if isinstance(stateful_event, stateful_events.SuiteFinished):
|
|
241
|
+
if stateful_event.failures and status != Status.error:
|
|
242
|
+
status = Status.failure
|
|
237
243
|
for failure in stateful_event.failures:
|
|
238
244
|
result.checks.append(failure)
|
|
245
|
+
elif isinstance(stateful_event, stateful_events.Errored):
|
|
246
|
+
status = Status.error
|
|
239
247
|
yield events.StatefulEvent(data=stateful_event)
|
|
240
248
|
results.append(result)
|
|
241
|
-
yield events.AfterStatefulExecution(
|
|
249
|
+
yield events.AfterStatefulExecution(
|
|
250
|
+
status=status,
|
|
251
|
+
result=SerializedTestResult.from_test_result(result),
|
|
252
|
+
)
|
|
242
253
|
|
|
243
254
|
def _run_tests(
|
|
244
255
|
self,
|
schemathesis/schemas.py
CHANGED
|
@@ -44,6 +44,7 @@ from .generation import (
|
|
|
44
44
|
GenerationConfig,
|
|
45
45
|
)
|
|
46
46
|
from .hooks import HookContext, HookDispatcher, HookScope, dispatch
|
|
47
|
+
from .internal.output import OutputConfig
|
|
47
48
|
from .internal.result import Ok, Result
|
|
48
49
|
from .models import APIOperation, Case
|
|
49
50
|
from .stateful import Stateful, StatefulTest
|
|
@@ -94,6 +95,7 @@ class BaseSchema(Mapping):
|
|
|
94
95
|
default_factory=lambda: list(DEFAULT_DATA_GENERATION_METHODS)
|
|
95
96
|
)
|
|
96
97
|
generation_config: GenerationConfig = field(default_factory=GenerationConfig)
|
|
98
|
+
output_config: OutputConfig = field(default_factory=OutputConfig)
|
|
97
99
|
code_sample_style: CodeSampleStyle = CodeSampleStyle.default()
|
|
98
100
|
rate_limiter: Limiter | None = None
|
|
99
101
|
sanitize_output: bool = True
|
|
@@ -286,6 +288,7 @@ class BaseSchema(Mapping):
|
|
|
286
288
|
skip_deprecated_operations: bool | NotSet = NOT_SET,
|
|
287
289
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
|
|
288
290
|
generation_config: GenerationConfig | NotSet = NOT_SET,
|
|
291
|
+
output_config: OutputConfig | NotSet = NOT_SET,
|
|
289
292
|
code_sample_style: CodeSampleStyle | NotSet = NOT_SET,
|
|
290
293
|
rate_limiter: Limiter | None = NOT_SET,
|
|
291
294
|
sanitize_output: bool | NotSet | None = NOT_SET,
|
|
@@ -314,6 +317,8 @@ class BaseSchema(Mapping):
|
|
|
314
317
|
data_generation_methods = self.data_generation_methods
|
|
315
318
|
if generation_config is NOT_SET:
|
|
316
319
|
generation_config = self.generation_config
|
|
320
|
+
if output_config is NOT_SET:
|
|
321
|
+
output_config = self.output_config
|
|
317
322
|
if code_sample_style is NOT_SET:
|
|
318
323
|
code_sample_style = self.code_sample_style
|
|
319
324
|
if rate_limiter is NOT_SET:
|
|
@@ -337,6 +342,7 @@ class BaseSchema(Mapping):
|
|
|
337
342
|
skip_deprecated_operations=skip_deprecated_operations, # type: ignore
|
|
338
343
|
data_generation_methods=data_generation_methods, # type: ignore
|
|
339
344
|
generation_config=generation_config, # type: ignore
|
|
345
|
+
output_config=output_config, # type: ignore
|
|
340
346
|
code_sample_style=code_sample_style, # type: ignore
|
|
341
347
|
rate_limiter=rate_limiter, # type: ignore
|
|
342
348
|
sanitize_output=sanitize_output, # type: ignore
|
|
@@ -150,22 +150,30 @@ def serialize_finished(event: events.Finished) -> dict[str, Any] | None:
|
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
def serialize_stateful_event(event: events.StatefulEvent) -> dict[str, Any] | None:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
return _serialize_stateful_event(event.data)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _serialize_stateful_event(event: stateful_events.StatefulEvent) -> dict[str, Any] | None:
|
|
157
|
+
data: dict[str, Any]
|
|
158
|
+
if isinstance(event, stateful_events.RunStarted):
|
|
159
|
+
data = {
|
|
160
|
+
"timestamp": event.timestamp,
|
|
161
|
+
"started_at": event.started_at,
|
|
159
162
|
}
|
|
160
|
-
elif isinstance(event
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
elif isinstance(event, stateful_events.SuiteFinished):
|
|
164
|
+
data = {
|
|
165
|
+
"timestamp": event.timestamp,
|
|
166
|
+
"status": event.status,
|
|
167
|
+
"failures": [_serialize_check(SerializedCheck.from_check(failure)) for failure in event.failures],
|
|
168
|
+
}
|
|
169
|
+
elif isinstance(event, stateful_events.Errored):
|
|
170
|
+
data = {
|
|
171
|
+
"timestamp": event.timestamp,
|
|
172
|
+
"exception": format_exception(event.exception, True),
|
|
167
173
|
}
|
|
168
|
-
|
|
174
|
+
else:
|
|
175
|
+
data = asdict(event)
|
|
176
|
+
return {"data": {event.__class__.__name__: data}}
|
|
169
177
|
|
|
170
178
|
|
|
171
179
|
def serialize_after_stateful_execution(event: events.AfterStatefulExecution) -> dict[str, Any] | None:
|
|
@@ -16,6 +16,7 @@ from ...generation import (
|
|
|
16
16
|
GenerationConfig,
|
|
17
17
|
)
|
|
18
18
|
from ...hooks import HookContext, dispatch
|
|
19
|
+
from ...internal.output import OutputConfig
|
|
19
20
|
from ...internal.validation import require_relative_url
|
|
20
21
|
from ...loaders import load_schema_from_url
|
|
21
22
|
from ...throttling import build_limiter
|
|
@@ -52,6 +53,7 @@ def from_path(
|
|
|
52
53
|
base_url: str | None = None,
|
|
53
54
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
54
55
|
generation_config: GenerationConfig | None = None,
|
|
56
|
+
output_config: OutputConfig | None = None,
|
|
55
57
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
56
58
|
rate_limit: str | None = None,
|
|
57
59
|
encoding: str = "utf8",
|
|
@@ -69,6 +71,8 @@ def from_path(
|
|
|
69
71
|
base_url=base_url,
|
|
70
72
|
data_generation_methods=data_generation_methods,
|
|
71
73
|
code_sample_style=code_sample_style,
|
|
74
|
+
generation_config=generation_config,
|
|
75
|
+
output_config=output_config,
|
|
72
76
|
location=pathlib.Path(path).absolute().as_uri(),
|
|
73
77
|
rate_limit=rate_limit,
|
|
74
78
|
sanitize_output=sanitize_output,
|
|
@@ -161,6 +165,7 @@ def from_file(
|
|
|
161
165
|
base_url: str | None = None,
|
|
162
166
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
163
167
|
generation_config: GenerationConfig | None = None,
|
|
168
|
+
output_config: OutputConfig | None = None,
|
|
164
169
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
165
170
|
location: str | None = None,
|
|
166
171
|
rate_limit: str | None = None,
|
|
@@ -198,6 +203,8 @@ def from_file(
|
|
|
198
203
|
app=app,
|
|
199
204
|
base_url=base_url,
|
|
200
205
|
data_generation_methods=data_generation_methods,
|
|
206
|
+
generation_config=generation_config,
|
|
207
|
+
output_config=output_config,
|
|
201
208
|
code_sample_style=code_sample_style,
|
|
202
209
|
location=location,
|
|
203
210
|
rate_limit=rate_limit,
|
|
@@ -221,6 +228,7 @@ def from_dict(
|
|
|
221
228
|
location: str | None = None,
|
|
222
229
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
223
230
|
generation_config: GenerationConfig | None = None,
|
|
231
|
+
output_config: OutputConfig | None = None,
|
|
224
232
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
225
233
|
rate_limit: str | None = None,
|
|
226
234
|
sanitize_output: bool = True,
|
|
@@ -250,6 +258,8 @@ def from_dict(
|
|
|
250
258
|
base_url=base_url,
|
|
251
259
|
app=app,
|
|
252
260
|
data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
|
|
261
|
+
generation_config=generation_config or GenerationConfig(),
|
|
262
|
+
output_config=output_config or OutputConfig(),
|
|
253
263
|
code_sample_style=_code_sample_style,
|
|
254
264
|
rate_limiter=rate_limiter,
|
|
255
265
|
sanitize_output=sanitize_output,
|
|
@@ -266,6 +276,7 @@ def from_wsgi(
|
|
|
266
276
|
base_url: str | None = None,
|
|
267
277
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
268
278
|
generation_config: GenerationConfig | None = None,
|
|
279
|
+
output_config: OutputConfig | None = None,
|
|
269
280
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
270
281
|
rate_limit: str | None = None,
|
|
271
282
|
sanitize_output: bool = True,
|
|
@@ -294,6 +305,8 @@ def from_wsgi(
|
|
|
294
305
|
base_url=base_url,
|
|
295
306
|
app=app,
|
|
296
307
|
data_generation_methods=data_generation_methods,
|
|
308
|
+
generation_config=generation_config,
|
|
309
|
+
output_config=output_config,
|
|
297
310
|
code_sample_style=code_sample_style,
|
|
298
311
|
rate_limit=rate_limit,
|
|
299
312
|
sanitize_output=sanitize_output,
|
|
@@ -307,6 +320,7 @@ def from_asgi(
|
|
|
307
320
|
base_url: str | None = None,
|
|
308
321
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
309
322
|
generation_config: GenerationConfig | None = None,
|
|
323
|
+
output_config: OutputConfig | None = None,
|
|
310
324
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
311
325
|
rate_limit: str | None = None,
|
|
312
326
|
sanitize_output: bool = True,
|
|
@@ -332,6 +346,8 @@ def from_asgi(
|
|
|
332
346
|
base_url=base_url,
|
|
333
347
|
app=app,
|
|
334
348
|
data_generation_methods=data_generation_methods,
|
|
349
|
+
generation_config=generation_config,
|
|
350
|
+
output_config=output_config,
|
|
335
351
|
code_sample_style=code_sample_style,
|
|
336
352
|
rate_limit=rate_limit,
|
|
337
353
|
sanitize_output=sanitize_output,
|
|
@@ -79,7 +79,12 @@ def content_type_conformance(response: GenericResponse, case: Case) -> bool | No
|
|
|
79
79
|
received_main, received_sub = parse_content_type(content_type)
|
|
80
80
|
except ValueError as exc:
|
|
81
81
|
_reraise_malformed_media_type(case, exc, "Response", content_type, option)
|
|
82
|
-
if (
|
|
82
|
+
if (
|
|
83
|
+
(expected_main == "*" and expected_sub == "*")
|
|
84
|
+
or (expected_main == received_main and expected_sub == "*")
|
|
85
|
+
or (expected_main == "*" and expected_sub == received_sub)
|
|
86
|
+
or (expected_main == received_main and expected_sub == received_sub)
|
|
87
|
+
):
|
|
83
88
|
return None
|
|
84
89
|
exc_class = get_response_type_error(
|
|
85
90
|
case.operation.verbose_name, f"{expected_main}_{expected_sub}", f"{received_main}_{received_sub}"
|
|
@@ -18,6 +18,7 @@ from ...generation import (
|
|
|
18
18
|
GenerationConfig,
|
|
19
19
|
)
|
|
20
20
|
from ...hooks import HookContext, dispatch
|
|
21
|
+
from ...internal.output import OutputConfig
|
|
21
22
|
from ...internal.validation import require_relative_url
|
|
22
23
|
from ...loaders import load_schema_from_url, load_yaml
|
|
23
24
|
from ...throttling import build_limiter
|
|
@@ -79,6 +80,7 @@ def from_path(
|
|
|
79
80
|
force_schema_version: str | None = None,
|
|
80
81
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
81
82
|
generation_config: GenerationConfig | None = None,
|
|
83
|
+
output_config: OutputConfig | None = None,
|
|
82
84
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
83
85
|
rate_limit: str | None = None,
|
|
84
86
|
encoding: str = "utf8",
|
|
@@ -103,6 +105,7 @@ def from_path(
|
|
|
103
105
|
force_schema_version=force_schema_version,
|
|
104
106
|
data_generation_methods=data_generation_methods,
|
|
105
107
|
generation_config=generation_config,
|
|
108
|
+
output_config=output_config,
|
|
106
109
|
code_sample_style=code_sample_style,
|
|
107
110
|
location=pathlib.Path(path).absolute().as_uri(),
|
|
108
111
|
rate_limit=rate_limit,
|
|
@@ -127,6 +130,7 @@ def from_uri(
|
|
|
127
130
|
force_schema_version: str | None = None,
|
|
128
131
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
129
132
|
generation_config: GenerationConfig | None = None,
|
|
133
|
+
output_config: OutputConfig | None = None,
|
|
130
134
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
131
135
|
wait_for_schema: float | None = None,
|
|
132
136
|
rate_limit: str | None = None,
|
|
@@ -176,6 +180,7 @@ def from_uri(
|
|
|
176
180
|
force_schema_version=force_schema_version,
|
|
177
181
|
data_generation_methods=data_generation_methods,
|
|
178
182
|
generation_config=generation_config,
|
|
183
|
+
output_config=output_config,
|
|
179
184
|
code_sample_style=code_sample_style,
|
|
180
185
|
location=uri,
|
|
181
186
|
rate_limit=rate_limit,
|
|
@@ -221,6 +226,7 @@ def from_file(
|
|
|
221
226
|
force_schema_version: str | None = None,
|
|
222
227
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
223
228
|
generation_config: GenerationConfig | None = None,
|
|
229
|
+
output_config: OutputConfig | None = None,
|
|
224
230
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
225
231
|
location: str | None = None,
|
|
226
232
|
rate_limit: str | None = None,
|
|
@@ -267,6 +273,7 @@ def from_file(
|
|
|
267
273
|
force_schema_version=force_schema_version,
|
|
268
274
|
data_generation_methods=data_generation_methods,
|
|
269
275
|
generation_config=generation_config,
|
|
276
|
+
output_config=output_config,
|
|
270
277
|
code_sample_style=code_sample_style,
|
|
271
278
|
location=location,
|
|
272
279
|
rate_limit=rate_limit,
|
|
@@ -295,6 +302,7 @@ def from_dict(
|
|
|
295
302
|
force_schema_version: str | None = None,
|
|
296
303
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
297
304
|
generation_config: GenerationConfig | None = None,
|
|
305
|
+
output_config: OutputConfig | None = None,
|
|
298
306
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
299
307
|
location: str | None = None,
|
|
300
308
|
rate_limit: str | None = None,
|
|
@@ -336,6 +344,7 @@ def from_dict(
|
|
|
336
344
|
validate_schema=validate_schema,
|
|
337
345
|
data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
|
|
338
346
|
generation_config=generation_config or GenerationConfig(),
|
|
347
|
+
output_config=output_config or OutputConfig(),
|
|
339
348
|
code_sample_style=_code_sample_style,
|
|
340
349
|
location=location,
|
|
341
350
|
rate_limiter=rate_limiter,
|
|
@@ -378,6 +387,7 @@ def from_dict(
|
|
|
378
387
|
validate_schema=validate_schema,
|
|
379
388
|
data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
|
|
380
389
|
generation_config=generation_config or GenerationConfig(),
|
|
390
|
+
output_config=output_config or OutputConfig(),
|
|
381
391
|
code_sample_style=_code_sample_style,
|
|
382
392
|
location=location,
|
|
383
393
|
rate_limiter=rate_limiter,
|
|
@@ -467,6 +477,7 @@ def from_pytest_fixture(
|
|
|
467
477
|
validate_schema: bool = False,
|
|
468
478
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
|
|
469
479
|
generation_config: GenerationConfig | NotSet = NOT_SET,
|
|
480
|
+
output_config: OutputConfig | NotSet = NOT_SET,
|
|
470
481
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
471
482
|
rate_limit: str | None = None,
|
|
472
483
|
sanitize_output: bool = True,
|
|
@@ -504,6 +515,7 @@ def from_pytest_fixture(
|
|
|
504
515
|
validate_schema=validate_schema,
|
|
505
516
|
data_generation_methods=_data_generation_methods,
|
|
506
517
|
generation_config=generation_config,
|
|
518
|
+
output_config=output_config,
|
|
507
519
|
code_sample_style=_code_sample_style,
|
|
508
520
|
rate_limiter=rate_limiter,
|
|
509
521
|
sanitize_output=sanitize_output,
|
|
@@ -524,6 +536,7 @@ def from_wsgi(
|
|
|
524
536
|
force_schema_version: str | None = None,
|
|
525
537
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
526
538
|
generation_config: GenerationConfig | None = None,
|
|
539
|
+
output_config: OutputConfig | None = None,
|
|
527
540
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
528
541
|
rate_limit: str | None = None,
|
|
529
542
|
sanitize_output: bool = True,
|
|
@@ -555,6 +568,7 @@ def from_wsgi(
|
|
|
555
568
|
force_schema_version=force_schema_version,
|
|
556
569
|
data_generation_methods=data_generation_methods,
|
|
557
570
|
generation_config=generation_config,
|
|
571
|
+
output_config=output_config,
|
|
558
572
|
code_sample_style=code_sample_style,
|
|
559
573
|
location=schema_path,
|
|
560
574
|
rate_limit=rate_limit,
|
|
@@ -587,6 +601,7 @@ def from_aiohttp(
|
|
|
587
601
|
force_schema_version: str | None = None,
|
|
588
602
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
589
603
|
generation_config: GenerationConfig | None = None,
|
|
604
|
+
output_config: OutputConfig | None = None,
|
|
590
605
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
591
606
|
rate_limit: str | None = None,
|
|
592
607
|
sanitize_output: bool = True,
|
|
@@ -614,6 +629,7 @@ def from_aiohttp(
|
|
|
614
629
|
force_schema_version=force_schema_version,
|
|
615
630
|
data_generation_methods=data_generation_methods,
|
|
616
631
|
generation_config=generation_config,
|
|
632
|
+
output_config=output_config,
|
|
617
633
|
code_sample_style=code_sample_style,
|
|
618
634
|
rate_limit=rate_limit,
|
|
619
635
|
sanitize_output=sanitize_output,
|
|
@@ -635,6 +651,7 @@ def from_asgi(
|
|
|
635
651
|
force_schema_version: str | None = None,
|
|
636
652
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
637
653
|
generation_config: GenerationConfig | None = None,
|
|
654
|
+
output_config: OutputConfig | None = None,
|
|
638
655
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
639
656
|
rate_limit: str | None = None,
|
|
640
657
|
sanitize_output: bool = True,
|
|
@@ -664,6 +681,7 @@ def from_asgi(
|
|
|
664
681
|
force_schema_version=force_schema_version,
|
|
665
682
|
data_generation_methods=data_generation_methods,
|
|
666
683
|
generation_config=generation_config,
|
|
684
|
+
output_config=output_config,
|
|
667
685
|
code_sample_style=code_sample_style,
|
|
668
686
|
location=schema_path,
|
|
669
687
|
rate_limit=rate_limit,
|
|
@@ -657,7 +657,7 @@ class BaseOpenAPISchema(BaseSchema):
|
|
|
657
657
|
jsonschema.validate(data, schema, cls=cls, resolver=resolver)
|
|
658
658
|
except jsonschema.ValidationError as exc:
|
|
659
659
|
exc_class = get_schema_validation_error(operation.verbose_name, exc)
|
|
660
|
-
ctx = failures.ValidationErrorContext.from_exception(exc)
|
|
660
|
+
ctx = failures.ValidationErrorContext.from_exception(exc, output_config=operation.schema.output_config)
|
|
661
661
|
try:
|
|
662
662
|
raise exc_class(ctx.title, context=ctx) from exc
|
|
663
663
|
except Exception as exc:
|
schemathesis/stateful/context.py
CHANGED
|
@@ -40,7 +40,7 @@ class RunnerContext:
|
|
|
40
40
|
# Unique failures collected in the current suite
|
|
41
41
|
failures_for_suite: list[Check] = field(default_factory=list)
|
|
42
42
|
# Status of the current step
|
|
43
|
-
current_step_status: events.StepStatus =
|
|
43
|
+
current_step_status: events.StepStatus | None = None
|
|
44
44
|
current_response: GenericResponse | None = None
|
|
45
45
|
|
|
46
46
|
@property
|
|
@@ -49,18 +49,28 @@ class RunnerContext:
|
|
|
49
49
|
return events.ScenarioStatus.SUCCESS
|
|
50
50
|
elif self.current_step_status == events.StepStatus.FAILURE:
|
|
51
51
|
return events.ScenarioStatus.FAILURE
|
|
52
|
-
|
|
52
|
+
elif self.current_step_status == events.StepStatus.ERROR:
|
|
53
|
+
return events.ScenarioStatus.ERROR
|
|
54
|
+
elif self.current_step_status == events.StepStatus.INTERRUPTED:
|
|
55
|
+
return events.ScenarioStatus.INTERRUPTED
|
|
56
|
+
return events.ScenarioStatus.REJECTED
|
|
53
57
|
|
|
54
58
|
def reset_step(self) -> None:
|
|
55
|
-
self.current_step_status =
|
|
59
|
+
self.current_step_status = None
|
|
56
60
|
self.current_response = None
|
|
57
61
|
|
|
62
|
+
def step_succeeded(self) -> None:
|
|
63
|
+
self.current_step_status = events.StepStatus.SUCCESS
|
|
64
|
+
|
|
58
65
|
def step_failed(self) -> None:
|
|
59
66
|
self.current_step_status = events.StepStatus.FAILURE
|
|
60
67
|
|
|
61
68
|
def step_errored(self) -> None:
|
|
62
69
|
self.current_step_status = events.StepStatus.ERROR
|
|
63
70
|
|
|
71
|
+
def step_interrupted(self) -> None:
|
|
72
|
+
self.current_step_status = events.StepStatus.INTERRUPTED
|
|
73
|
+
|
|
64
74
|
def mark_as_seen_in_run(self, exc: CheckFailed) -> None:
|
|
65
75
|
key = _failure_cache_key(exc)
|
|
66
76
|
self.seen_in_run.add(key)
|
schemathesis/stateful/events.py
CHANGED
|
@@ -23,6 +23,8 @@ class RunStatus(str, Enum):
|
|
|
23
23
|
class StatefulEvent:
|
|
24
24
|
"""Basic stateful test event."""
|
|
25
25
|
|
|
26
|
+
timestamp: float
|
|
27
|
+
|
|
26
28
|
__slots__ = ("timestamp",)
|
|
27
29
|
|
|
28
30
|
|
|
@@ -30,6 +32,7 @@ class StatefulEvent:
|
|
|
30
32
|
class RunStarted(StatefulEvent):
|
|
31
33
|
"""Before executing all scenarios."""
|
|
32
34
|
|
|
35
|
+
started_at: float
|
|
33
36
|
state_machine: Type[APIStateMachine]
|
|
34
37
|
|
|
35
38
|
__slots__ = ("state_machine", "timestamp", "started_at")
|
|
@@ -92,8 +95,10 @@ class ScenarioStatus(str, Enum):
|
|
|
92
95
|
|
|
93
96
|
SUCCESS = "success"
|
|
94
97
|
FAILURE = "failure"
|
|
95
|
-
# TODO: Count for Hypothesis' rejected?
|
|
96
98
|
ERROR = "error"
|
|
99
|
+
# Rejected by Hypothesis
|
|
100
|
+
REJECTED = "rejected"
|
|
101
|
+
INTERRUPTED = "interrupted"
|
|
97
102
|
|
|
98
103
|
|
|
99
104
|
@dataclass
|
|
@@ -132,6 +137,7 @@ class StepStatus(str, Enum):
|
|
|
132
137
|
SUCCESS = "success"
|
|
133
138
|
FAILURE = "failure"
|
|
134
139
|
ERROR = "error"
|
|
140
|
+
INTERRUPTED = "interrupted"
|
|
135
141
|
|
|
136
142
|
|
|
137
143
|
@dataclass
|
schemathesis/stateful/runner.py
CHANGED
|
@@ -4,7 +4,7 @@ import queue
|
|
|
4
4
|
import threading
|
|
5
5
|
from contextlib import contextmanager
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Generator, Iterator, Type
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Generator, Iterator, Type, cast
|
|
8
8
|
|
|
9
9
|
from hypothesis.control import current_build_context
|
|
10
10
|
from hypothesis.errors import Flaky
|
|
@@ -134,15 +134,18 @@ def _execute_state_machine_loop(
|
|
|
134
134
|
if stop_event.is_set():
|
|
135
135
|
raise KeyboardInterrupt
|
|
136
136
|
event_queue.put(events.StepStarted())
|
|
137
|
-
ctx.reset_step()
|
|
138
137
|
try:
|
|
139
138
|
result = super().step(case, previous)
|
|
139
|
+
ctx.step_succeeded()
|
|
140
140
|
except CheckFailed:
|
|
141
141
|
ctx.step_failed()
|
|
142
142
|
raise
|
|
143
143
|
except Exception:
|
|
144
144
|
ctx.step_errored()
|
|
145
145
|
raise
|
|
146
|
+
except KeyboardInterrupt:
|
|
147
|
+
ctx.step_interrupted()
|
|
148
|
+
raise
|
|
146
149
|
finally:
|
|
147
150
|
transition_id: events.TransitionId | None
|
|
148
151
|
if previous is not None:
|
|
@@ -162,9 +165,10 @@ def _execute_state_machine_loop(
|
|
|
162
165
|
)
|
|
163
166
|
else:
|
|
164
167
|
response = None
|
|
168
|
+
status = cast(events.StepStatus, ctx.current_step_status)
|
|
165
169
|
event_queue.put(
|
|
166
170
|
events.StepFinished(
|
|
167
|
-
status=
|
|
171
|
+
status=status,
|
|
168
172
|
transition_id=transition_id,
|
|
169
173
|
target=case.operation.verbose_name,
|
|
170
174
|
response=response,
|
|
@@ -186,6 +190,7 @@ def _execute_state_machine_loop(
|
|
|
186
190
|
is_final=build_ctx.is_final,
|
|
187
191
|
)
|
|
188
192
|
)
|
|
193
|
+
ctx.reset_step()
|
|
189
194
|
super().teardown()
|
|
190
195
|
|
|
191
196
|
while True:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 3.30.
|
|
3
|
+
Version: 3.30.2
|
|
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
|
|
@@ -34,8 +34,8 @@ Requires-Dist: colorama<1.0,>=0.4
|
|
|
34
34
|
Requires-Dist: httpx<1.0,>=0.22.0
|
|
35
35
|
Requires-Dist: hypothesis-graphql<1,>=0.11.0
|
|
36
36
|
Requires-Dist: hypothesis-jsonschema<0.24,>=0.23.1
|
|
37
|
-
Requires-Dist: hypothesis<7,>=6.
|
|
38
|
-
Requires-Dist: hypothesis[zoneinfo]<7,>=6.
|
|
37
|
+
Requires-Dist: hypothesis<7,>=6.103.4; python_version > '3.8'
|
|
38
|
+
Requires-Dist: hypothesis[zoneinfo]<7,>=6.103.4; python_version == '3.8'
|
|
39
39
|
Requires-Dist: jsonschema<5.0,>=4.18.0
|
|
40
40
|
Requires-Dist: junit-xml<2.0,>=1.9
|
|
41
41
|
Requires-Dist: pyrate-limiter<4.0,>=2.10
|
|
@@ -10,37 +10,37 @@ schemathesis/auths.py,sha256=NeJqsjtDgJtHMyrHc6V1NTpkAh1K8ZKLalpB3v80cT4,14734
|
|
|
10
10
|
schemathesis/checks.py,sha256=XplKduiRbRrxZx-wDMGaW91aIitr5RL9vjMP6bvCFRg,2272
|
|
11
11
|
schemathesis/code_samples.py,sha256=xk1-1jnXg5hS40VzIZp8PEtZwGaazNlVKMT7_X-zG-M,4123
|
|
12
12
|
schemathesis/constants.py,sha256=l1YQ7PXhEj9dyf9CTESVUpPOaFCH7iz-Fe8o4v6Th_s,2673
|
|
13
|
-
schemathesis/exceptions.py,sha256
|
|
14
|
-
schemathesis/failures.py,sha256=
|
|
13
|
+
schemathesis/exceptions.py,sha256=ZX_KqnkQ80GACsBgm6lJV43aFBJws-2vGtnFu72n-O4,19576
|
|
14
|
+
schemathesis/failures.py,sha256=wXz5Kr5i-ojcYc-BdzFlNbNGOfoVXHZM6kd4iULdHK4,7003
|
|
15
15
|
schemathesis/filters.py,sha256=0fYzn9sJ35k3Znx1P8FrbSdoUcdslcibtGh-IOTRwB8,10251
|
|
16
16
|
schemathesis/graphql.py,sha256=YkoKWY5K8lxp7H3ikAs-IsoDbiPwJvChG7O8p3DgwtI,229
|
|
17
17
|
schemathesis/hooks.py,sha256=dveqMmThIvt4fDahUXhU2nCq5pFvYjzzd1Ys_MhrJZA,12398
|
|
18
|
-
schemathesis/lazy.py,sha256=
|
|
18
|
+
schemathesis/lazy.py,sha256=eVdGkTZK0fWvUlFUCFGGlViH2NWEtYIjxiNkF4fBWhI,15218
|
|
19
19
|
schemathesis/loaders.py,sha256=OtCD1o0TVmSNAUF7dgHpouoAXtY6w9vEtsRVGv4lE0g,4588
|
|
20
|
-
schemathesis/models.py,sha256
|
|
20
|
+
schemathesis/models.py,sha256=byTGJD6zyXFvSvQCe-Mxu3lOdvU3EQ9vmadv6Jv6f5I,44025
|
|
21
21
|
schemathesis/parameters.py,sha256=PndmqQRlEYsCt1kWjSShPsFf6vj7X_7FRdz_-A95eNg,2258
|
|
22
22
|
schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
schemathesis/sanitization.py,sha256=mRR4YvXpzqbmgX8Xu6rume6LBcz9g_oyusvbesZl44I,8958
|
|
24
|
-
schemathesis/schemas.py,sha256=
|
|
24
|
+
schemathesis/schemas.py,sha256=aa84Kmin4lu68vU4y3DpPGo4Z1Pt7LFCqe9kzW35Asc,18267
|
|
25
25
|
schemathesis/serializers.py,sha256=kxXZ-UGa1v_vOm0sC4QYcrNv4rfvI7tHGT2elRVbCbc,11649
|
|
26
26
|
schemathesis/targets.py,sha256=N1Zzgqa1PNycWIeGpra7q-6ASn2x4r9Jompn35bmlsE,1163
|
|
27
27
|
schemathesis/throttling.py,sha256=aisUc4MJDGIOGUAs9L2DlWWpdd4KyAFuNVKhYoaUC9M,1719
|
|
28
28
|
schemathesis/types.py,sha256=xOzNAeMs6qqeaJnWs5Fpw5JPbvVjyfRfxTJa3G2Ln5I,920
|
|
29
29
|
schemathesis/utils.py,sha256=NX04p9mO-lCAH3DIISXDXPxWZk6lkGNM4-ubRi8vlvY,5234
|
|
30
|
-
schemathesis/cli/__init__.py,sha256=
|
|
30
|
+
schemathesis/cli/__init__.py,sha256=EmgM2nTU81Q9BdAxwFl6hBsGZl1SYTr9tvCqHDyWu2U,65680
|
|
31
31
|
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
|
32
32
|
schemathesis/cli/callbacks.py,sha256=tZSe6EIyc1fdKZ8xGaE0w0LuGCMtT7m9cW41Rd0Nah0,14949
|
|
33
33
|
schemathesis/cli/cassettes.py,sha256=WLt2svZm23vZX5buQWmWO94QwQEC8OM2g8yH50rfatE,12990
|
|
34
34
|
schemathesis/cli/constants.py,sha256=ogSuZs68KvzHNKF0yaiBkxLcGbo8IVR3xaIfsy1H1IQ,1546
|
|
35
|
-
schemathesis/cli/context.py,sha256=
|
|
35
|
+
schemathesis/cli/context.py,sha256=BsaKvfmh9LmiDU3D7wixSzBjk9KWNRWJwYA3Xa3d35I,2223
|
|
36
36
|
schemathesis/cli/debug.py,sha256=_YA-bX1ujHl4bqQDEum7M-I2XHBTEGbvgkhvcvKhmgU,658
|
|
37
37
|
schemathesis/cli/handlers.py,sha256=RjvogPCqqFTiyVYWrGG6euw9-6h-7uSeFvS-ouU8eWs,389
|
|
38
|
-
schemathesis/cli/junitxml.py,sha256=
|
|
38
|
+
schemathesis/cli/junitxml.py,sha256=yWacOIrTC9UI-IlgZnu8nfSmpkxfMT22NqJ3L1SZa3w,4743
|
|
39
39
|
schemathesis/cli/options.py,sha256=DY5PUzpUNyYgpcqqFTeZjmVUykpbaI9fbN44QIWNOfA,2559
|
|
40
|
-
schemathesis/cli/reporting.py,sha256=
|
|
40
|
+
schemathesis/cli/reporting.py,sha256=AT8ZjSUeH0S9Dl1ZhvTw0CJPy1JdIIRuRWxsJXfFpjc,3521
|
|
41
41
|
schemathesis/cli/sanitization.py,sha256=pVlQnVDC1_Ugp0oe1LEkRorFdBRDHqr_NWWKaOaNdY0,728
|
|
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=0zFolpo2HS2NH-IzRqEIZqfa_6Vk4jEHpfykLQ7apAM,39755
|
|
44
44
|
schemathesis/cli/output/short.py,sha256=CL6-Apxr5tuZ3BL1vecV1MiRY1wDt21g0wiUwZu6mLM,2607
|
|
45
45
|
schemathesis/contrib/__init__.py,sha256=FH8NL8NXgSKBFOF8Jy_EB6T4CJEaiM-tmDhz16B2o4k,187
|
|
46
46
|
schemathesis/contrib/unique_data.py,sha256=_ElPRLNp0XhdETiZ-aplKln4hgU04jxR17kmjAEWf1I,1318
|
|
@@ -63,15 +63,16 @@ schemathesis/internal/copy.py,sha256=lcK01CODz6jogXyH0tsKkPv1PBEX8jeBPxI9MzQ6LN4
|
|
|
63
63
|
schemathesis/internal/datetime.py,sha256=zPLBL0XXLNfP-KYel3H2m8pnsxjsA_4d-zTOhJg2EPQ,136
|
|
64
64
|
schemathesis/internal/deprecation.py,sha256=cF3FYy5syihneSIY7M7aVJW2KTzcd88WOItHFCJtADg,1107
|
|
65
65
|
schemathesis/internal/jsonschema.py,sha256=-7tF15cXo1ZdhiRFYYfEClXihX2Svc5Loi_Dz1x201k,1157
|
|
66
|
+
schemathesis/internal/output.py,sha256=zMaG5knIuBieVH8CrcmPJgbmQukDs2xdekX0BrK7BZs,1989
|
|
66
67
|
schemathesis/internal/result.py,sha256=d449YvyONjqjDs-A5DAPgtAI96iT753K8sU6_1HLo2Q,461
|
|
67
68
|
schemathesis/internal/transformation.py,sha256=3S6AzAqdsEsB5iobFgSuvL0UMUqH0JHC7hGxKwcpqPw,450
|
|
68
69
|
schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QAtMPyEw,966
|
|
69
70
|
schemathesis/runner/__init__.py,sha256=n-4CanmlfMiGnir6xbZH-fM5a0pe2GloBFj2NTtet-M,21419
|
|
70
|
-
schemathesis/runner/events.py,sha256=
|
|
71
|
+
schemathesis/runner/events.py,sha256=PJceb_jC-2BKEdMIrH8mSCtt8lbPPq6Gs5qjPicwxmU,10575
|
|
71
72
|
schemathesis/runner/probes.py,sha256=J-TT0hOKu9j4htWKBcYKmsomcRxmvOl4WpmnKLVXu8M,5546
|
|
72
73
|
schemathesis/runner/serialization.py,sha256=J8fuG8MSJq3rE3IJs73U1YXWFrNa05k7PGd5Bvq1uec,17356
|
|
73
74
|
schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
|
|
74
|
-
schemathesis/runner/impl/core.py,sha256=
|
|
75
|
+
schemathesis/runner/impl/core.py,sha256=u56WvKOJY96ShHj5MoAcwys_th3DAxLAAoipx7wvWm8,42034
|
|
75
76
|
schemathesis/runner/impl/solo.py,sha256=MatxThgqKsY2tX_hVwjy78oKFeKejb6dFJoX3kGzW4U,3359
|
|
76
77
|
schemathesis/runner/impl/threadpool.py,sha256=fj2QYoWxIJIxpTCcJQyM_VCRO1YDnW9XQJJnNVFVQxY,15253
|
|
77
78
|
schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
|
|
@@ -85,12 +86,12 @@ schemathesis/service/hosts.py,sha256=ad2Lxq9Zcc9PP-1eFLQnxen4ImglcGOH8n7CGG72NNg
|
|
|
85
86
|
schemathesis/service/metadata.py,sha256=x2LeCED1mdPf-YQJmjY8xtcIKHfD1ap5V0BGl-UgqNo,2087
|
|
86
87
|
schemathesis/service/models.py,sha256=ihItUJ9CvH4TvmdfJY3W88NR82OODF8a3RD7WRXn6RM,6578
|
|
87
88
|
schemathesis/service/report.py,sha256=4A8nf6_KOjDW3x1VXF8gSf_WY2xXp1Cbz-Owl_GeR7o,8294
|
|
88
|
-
schemathesis/service/serialization.py,sha256=
|
|
89
|
+
schemathesis/service/serialization.py,sha256=LbEUhcDI9hsFDrxjjH-79QhRd71pKHHc9odsVsUuC7s,9776
|
|
89
90
|
schemathesis/service/usage.py,sha256=UbXqxeDq5mAjKkfV4hApZsReZmQHXiqoXUYn_Z6YuZk,2438
|
|
90
91
|
schemathesis/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
92
|
schemathesis/specs/graphql/__init__.py,sha256=fgyHtvWNUVWismBTOqxQtgLoTighTfvMv6v6QCD_Oyc,85
|
|
92
93
|
schemathesis/specs/graphql/_cache.py,sha256=7ras3q_InDJBPykgHroerl9f2jFamC8xJD35376Zs6I,795
|
|
93
|
-
schemathesis/specs/graphql/loaders.py,sha256=
|
|
94
|
+
schemathesis/specs/graphql/loaders.py,sha256=qxNGL67_AfhoRh0hIxlnJVe6do26vqwWS_TrJtB-Lro,12198
|
|
94
95
|
schemathesis/specs/graphql/nodes.py,sha256=bE3G1kNmqJ8OV4igBvIK-UORrkQA6Nofduf87O3TD9I,541
|
|
95
96
|
schemathesis/specs/graphql/scalars.py,sha256=9tvLTiYVe8A_E8ASA0czz3Z0Mp9lyak7R4wHpAE_jKo,1805
|
|
96
97
|
schemathesis/specs/graphql/schemas.py,sha256=ICIejnv05Q9uxljQU06i9RSVa10mAdTESBfM17wMkaI,13491
|
|
@@ -98,7 +99,7 @@ schemathesis/specs/graphql/validation.py,sha256=uINIOt-2E7ZuQV2CxKzwez-7L9tDtqzM
|
|
|
98
99
|
schemathesis/specs/openapi/__init__.py,sha256=HDcx3bqpa6qWPpyMrxAbM3uTo0Lqpg-BUNZhDJSJKnw,279
|
|
99
100
|
schemathesis/specs/openapi/_cache.py,sha256=PAiAu4X_a2PQgD2lG5H3iisXdyg4SaHpU46bRZvfNkM,4320
|
|
100
101
|
schemathesis/specs/openapi/_hypothesis.py,sha256=9O8gTVWtq17UezgvlxHxjTHGVmggQ2mxwAnVIvZKgsk,23619
|
|
101
|
-
schemathesis/specs/openapi/checks.py,sha256=
|
|
102
|
+
schemathesis/specs/openapi/checks.py,sha256=1Fu3Kgai9ySCoGtCrx99Q9oVCEWXgkqHd1gTqG_569s,9364
|
|
102
103
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
|
103
104
|
schemathesis/specs/openapi/converter.py,sha256=TaYgc5BBHPdkN-n0lqpbeVgLu3eL3L8Wu3y_Vo3TJaQ,2800
|
|
104
105
|
schemathesis/specs/openapi/definitions.py,sha256=Z186F0gNBSCmPg-Kk7Q-n6XxEZHIOzgUyeqixlC62XE,94058
|
|
@@ -106,11 +107,11 @@ schemathesis/specs/openapi/examples.py,sha256=OxzcYWlNQzAjkNf8IAGxEGnQ4DelY4mtPw
|
|
|
106
107
|
schemathesis/specs/openapi/filters.py,sha256=6Q9eNQ6zCR-NQkUxgnkSDWxfk3hsZuxemBv7v1rhwb4,1437
|
|
107
108
|
schemathesis/specs/openapi/formats.py,sha256=JmmkQWNAj5XreXb7Edgj4LADAf4m86YulR_Ec8evpJ4,1220
|
|
108
109
|
schemathesis/specs/openapi/links.py,sha256=2ucOLs50OhCqu0PEdbT_BGUM3fKnHBl97YGISLpAxLY,16023
|
|
109
|
-
schemathesis/specs/openapi/loaders.py,sha256=
|
|
110
|
+
schemathesis/specs/openapi/loaders.py,sha256=JJdIz1aT03J9WmUWTLOz6Yhuu69IqmhobQ9_vL6XJ6U,24916
|
|
110
111
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
|
111
112
|
schemathesis/specs/openapi/parameters.py,sha256=_6vNCnPXcdxjfAQbykCRLHjvmTpu_02xDJghxDrGYr8,13611
|
|
112
113
|
schemathesis/specs/openapi/references.py,sha256=i1Jd8EtEpSpauFW29DQsYM-JvB4jtYI7dpJAJcdjYe0,9276
|
|
113
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
|
114
|
+
schemathesis/specs/openapi/schemas.py,sha256=rEsvuRM4mFbUEfaNfANadyfWhA-iaXbaBMZpjq1anBk,51761
|
|
114
115
|
schemathesis/specs/openapi/security.py,sha256=sTOCYEsJmxGY2dglrlZRYmJzNZjEZAHm5bZtbYFHLmI,6612
|
|
115
116
|
schemathesis/specs/openapi/serialization.py,sha256=5qGdFHZ3n80UlbSXrO_bkr4Al_7ci_Z3aSUjZczNDQY,11384
|
|
116
117
|
schemathesis/specs/openapi/utils.py,sha256=-TCu0hTrlwp2x5qHNp-TxiHRMeIZC9OBmlhLssjRIiQ,742
|
|
@@ -131,9 +132,9 @@ schemathesis/specs/openapi/stateful/statistic.py,sha256=EJK4NqeAYRYl1FtU9YEuTLyh
|
|
|
131
132
|
schemathesis/specs/openapi/stateful/types.py,sha256=UuGcCTFvaHsqeLN9ZeUNcbjsEwmthoT3UcHfDHchOYo,419
|
|
132
133
|
schemathesis/stateful/__init__.py,sha256=qyQJ-9Ect-AWZiAsK63F3BTGu-jZnPCOp1q46YAonkQ,4911
|
|
133
134
|
schemathesis/stateful/config.py,sha256=kuKGLNNk0YrR6G0IzcZh6v0oysHHTPfHi9nHUsfQsF0,2298
|
|
134
|
-
schemathesis/stateful/context.py,sha256=
|
|
135
|
-
schemathesis/stateful/events.py,sha256=
|
|
136
|
-
schemathesis/stateful/runner.py,sha256=
|
|
135
|
+
schemathesis/stateful/context.py,sha256=P2kVeMOMsHOpQ0wyeZ2OfTU5fSCgehKJxzai1_tXiwc,3747
|
|
136
|
+
schemathesis/stateful/events.py,sha256=wXh4O9FELmNWAUnupAFpUFOokjesvQOE8joBUAqjbrU,4947
|
|
137
|
+
schemathesis/stateful/runner.py,sha256=ZcnU0lERjenpG0XnQUSeHH0MOesCg7K97fE53eIxvvc,9633
|
|
137
138
|
schemathesis/stateful/sink.py,sha256=V1ReRTpN3VfAXtcnSEke4UHhu7IxrYQs0s_gSDMaqWw,2425
|
|
138
139
|
schemathesis/stateful/state_machine.py,sha256=H-AzMPTKuCKnoCv0b7XPFDsHkzRftNfbvh5xb2H5Hfk,12156
|
|
139
140
|
schemathesis/stateful/statistic.py,sha256=xPLiCw61ofNXQicqcK_sZyLHiqiGcgQARpwd8AiRubM,487
|
|
@@ -143,8 +144,8 @@ schemathesis/transports/auth.py,sha256=4z7c-K7lfyyVqgR6X1v4yiE8ewR_ViAznWFTAsCL0
|
|
|
143
144
|
schemathesis/transports/content_types.py,sha256=VrcRQvF5T_TUjrCyrZcYF2LOwKfs3IrLcMtkVSp1ImI,2189
|
|
144
145
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
|
145
146
|
schemathesis/transports/responses.py,sha256=6-gvVcRK0Ho_lSydUysBNFWoJwZEiEgf6Iv-GWkQGd8,1675
|
|
146
|
-
schemathesis-3.30.
|
|
147
|
-
schemathesis-3.30.
|
|
148
|
-
schemathesis-3.30.
|
|
149
|
-
schemathesis-3.30.
|
|
150
|
-
schemathesis-3.30.
|
|
147
|
+
schemathesis-3.30.2.dist-info/METADATA,sha256=bwU4BSQbhEek_Gp8WnUrOR2rdShENNB3nVlQuKQ_O6g,17671
|
|
148
|
+
schemathesis-3.30.2.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
|
|
149
|
+
schemathesis-3.30.2.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
|
150
|
+
schemathesis-3.30.2.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
|
151
|
+
schemathesis-3.30.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|