schemathesis 4.0.0a10__py3-none-any.whl → 4.0.0a11__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 +3 -7
- schemathesis/checks.py +17 -7
- schemathesis/cli/commands/__init__.py +51 -3
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +147 -260
- schemathesis/cli/commands/run/context.py +2 -3
- schemathesis/cli/commands/run/events.py +4 -0
- schemathesis/cli/commands/run/executor.py +60 -73
- schemathesis/cli/commands/run/filters.py +15 -165
- schemathesis/cli/commands/run/handlers/cassettes.py +105 -104
- schemathesis/cli/commands/run/handlers/junitxml.py +5 -4
- schemathesis/cli/commands/run/handlers/output.py +26 -47
- schemathesis/cli/commands/run/loaders.py +35 -50
- schemathesis/cli/commands/run/validation.py +36 -161
- schemathesis/cli/core.py +5 -3
- schemathesis/cli/ext/fs.py +7 -5
- schemathesis/cli/ext/options.py +0 -21
- schemathesis/config/__init__.py +188 -0
- schemathesis/config/_auth.py +51 -0
- schemathesis/config/_checks.py +268 -0
- schemathesis/config/_diff_base.py +99 -0
- schemathesis/config/_env.py +21 -0
- schemathesis/config/_error.py +156 -0
- schemathesis/config/_generation.py +150 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +313 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +151 -0
- schemathesis/config/_projects.py +495 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +116 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/schema.json +837 -0
- schemathesis/core/__init__.py +2 -0
- schemathesis/core/compat.py +16 -9
- schemathesis/core/errors.py +19 -2
- schemathesis/core/failures.py +6 -7
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/output/__init__.py +14 -37
- schemathesis/core/output/sanitization.py +3 -146
- schemathesis/core/validation.py +16 -0
- schemathesis/engine/__init__.py +2 -4
- schemathesis/engine/context.py +41 -43
- schemathesis/engine/core.py +7 -5
- schemathesis/engine/phases/__init__.py +10 -0
- schemathesis/engine/phases/probes.py +8 -8
- schemathesis/engine/phases/stateful/_executor.py +68 -43
- schemathesis/engine/phases/unit/__init__.py +23 -15
- schemathesis/engine/phases/unit/_executor.py +77 -17
- schemathesis/engine/phases/unit/_pool.py +1 -1
- schemathesis/errors.py +2 -0
- schemathesis/filters.py +2 -3
- schemathesis/generation/__init__.py +6 -31
- schemathesis/generation/case.py +5 -3
- schemathesis/generation/coverage.py +153 -123
- schemathesis/generation/hypothesis/builder.py +40 -14
- schemathesis/generation/meta.py +3 -3
- schemathesis/generation/overrides.py +37 -1
- schemathesis/generation/stateful/state_machine.py +8 -1
- schemathesis/graphql/loaders.py +21 -12
- schemathesis/openapi/checks.py +12 -8
- schemathesis/openapi/generation/filters.py +10 -8
- schemathesis/openapi/loaders.py +22 -13
- schemathesis/pytest/lazy.py +2 -5
- schemathesis/pytest/plugin.py +11 -2
- schemathesis/schemas.py +13 -61
- schemathesis/specs/graphql/schemas.py +11 -15
- schemathesis/specs/openapi/_hypothesis.py +12 -8
- schemathesis/specs/openapi/checks.py +16 -18
- schemathesis/specs/openapi/examples.py +4 -3
- schemathesis/specs/openapi/formats.py +2 -2
- schemathesis/specs/openapi/negative/__init__.py +2 -2
- schemathesis/specs/openapi/patterns.py +46 -16
- schemathesis/specs/openapi/references.py +2 -3
- schemathesis/specs/openapi/schemas.py +11 -20
- schemathesis/specs/openapi/stateful/__init__.py +10 -5
- schemathesis/transport/prepare.py +7 -6
- schemathesis/transport/requests.py +3 -1
- schemathesis/transport/wsgi.py +3 -4
- {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/METADATA +7 -8
- schemathesis-4.0.0a11.dist-info/RECORD +166 -0
- schemathesis/cli/commands/run/checks.py +0 -79
- schemathesis/cli/commands/run/hypothesis.py +0 -78
- schemathesis/cli/commands/run/reports.py +0 -72
- schemathesis/cli/hooks.py +0 -36
- schemathesis/engine/config.py +0 -59
- schemathesis/experimental/__init__.py +0 -72
- schemathesis-4.0.0a10.dist-info/RECORD +0 -153
- {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/WHEEL +0 -0
- {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/licenses/LICENSE +0 -0
schemathesis/__init__.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from schemathesis import auths, contrib, engine, errors,
|
3
|
+
from schemathesis import auths, contrib, engine, errors, graphql, hooks, openapi, pytest, python
|
4
4
|
from schemathesis.checks import CheckContext, CheckFunction, check
|
5
|
-
from schemathesis.core.output import
|
5
|
+
from schemathesis.core.output import sanitization
|
6
6
|
from schemathesis.core.transport import Response
|
7
7
|
from schemathesis.core.version import SCHEMATHESIS_VERSION
|
8
|
-
from schemathesis.generation import
|
8
|
+
from schemathesis.generation import GenerationMode
|
9
9
|
from schemathesis.generation.case import Case
|
10
10
|
from schemathesis.generation.targets import TargetContext, TargetFunction, target
|
11
11
|
from schemathesis.hooks import HookContext
|
@@ -22,9 +22,6 @@ __all__ = [
|
|
22
22
|
"CheckContext",
|
23
23
|
"CheckFunction",
|
24
24
|
"GenerationMode",
|
25
|
-
"GenerationConfig",
|
26
|
-
"HeaderConfig",
|
27
|
-
"OutputConfig",
|
28
25
|
"Response",
|
29
26
|
"TargetContext",
|
30
27
|
"TargetFunction",
|
@@ -36,7 +33,6 @@ __all__ = [
|
|
36
33
|
"contrib",
|
37
34
|
"engine",
|
38
35
|
"errors",
|
39
|
-
"experimental",
|
40
36
|
"graphql",
|
41
37
|
"hook",
|
42
38
|
"hooks",
|
schemathesis/checks.py
CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations
|
|
3
3
|
import json
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Optional
|
5
5
|
|
6
|
+
from schemathesis.config import ChecksConfig
|
6
7
|
from schemathesis.core.failures import (
|
7
8
|
CustomFailure,
|
8
9
|
Failure,
|
9
10
|
FailureGroup,
|
10
11
|
MalformedJson,
|
11
|
-
MaxResponseTimeConfig,
|
12
12
|
ResponseTimeExceeded,
|
13
13
|
ServerError,
|
14
14
|
)
|
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
|
|
23
23
|
from schemathesis.generation.case import Case
|
24
24
|
|
25
25
|
CheckFunction = Callable[["CheckContext", "Response", "Case"], Optional[bool]]
|
26
|
-
ChecksConfig = dict[CheckFunction, Any]
|
27
26
|
|
28
27
|
|
29
28
|
class CheckContext:
|
@@ -38,8 +37,9 @@ class CheckContext:
|
|
38
37
|
config: ChecksConfig
|
39
38
|
transport_kwargs: dict[str, Any] | None
|
40
39
|
recorder: ScenarioRecorder | None
|
40
|
+
checks: list[CheckFunction]
|
41
41
|
|
42
|
-
__slots__ = ("override", "auth", "headers", "config", "transport_kwargs", "recorder")
|
42
|
+
__slots__ = ("override", "auth", "headers", "config", "transport_kwargs", "recorder", "checks")
|
43
43
|
|
44
44
|
def __init__(
|
45
45
|
self,
|
@@ -56,6 +56,13 @@ class CheckContext:
|
|
56
56
|
self.config = config
|
57
57
|
self.transport_kwargs = transport_kwargs
|
58
58
|
self.recorder = recorder
|
59
|
+
self.checks = []
|
60
|
+
for check in CHECKS.get_all():
|
61
|
+
name = check.__name__
|
62
|
+
if self.config.get_by_name(name=name).enabled:
|
63
|
+
self.checks.append(check)
|
64
|
+
if self.config.max_response_time.enabled:
|
65
|
+
self.checks.append(max_response_time)
|
59
66
|
|
60
67
|
def find_parent(self, *, case_id: str) -> Case | None:
|
61
68
|
if self.recorder is not None:
|
@@ -102,15 +109,18 @@ def not_a_server_error(ctx: CheckContext, response: Response, case: Case) -> boo
|
|
102
109
|
return None
|
103
110
|
|
104
111
|
|
112
|
+
DEFAULT_MAX_RESPONSE_TIME = 10.0
|
113
|
+
|
114
|
+
|
105
115
|
def max_response_time(ctx: CheckContext, response: Response, case: Case) -> bool | None:
|
106
|
-
|
116
|
+
limit = ctx.config.max_response_time.limit or DEFAULT_MAX_RESPONSE_TIME
|
107
117
|
elapsed = response.elapsed
|
108
|
-
if elapsed >
|
118
|
+
if elapsed > limit:
|
109
119
|
raise ResponseTimeExceeded(
|
110
120
|
operation=case.operation.label,
|
111
|
-
message=f"Actual: {elapsed:.2f}ms\nLimit: {
|
121
|
+
message=f"Actual: {elapsed:.2f}ms\nLimit: {limit * 1000:.2f}ms",
|
112
122
|
elapsed=elapsed,
|
113
|
-
deadline=
|
123
|
+
deadline=limit,
|
114
124
|
)
|
115
125
|
return None
|
116
126
|
|
@@ -4,20 +4,68 @@ from dataclasses import dataclass
|
|
4
4
|
from typing import Any
|
5
5
|
|
6
6
|
import click
|
7
|
+
from tomli import TOMLDecodeError
|
7
8
|
|
8
|
-
from schemathesis.cli import
|
9
|
+
from schemathesis.cli.commands.data import Data
|
9
10
|
from schemathesis.cli.commands.run import run as run_command
|
11
|
+
from schemathesis.cli.commands.run.handlers.output import display_header
|
12
|
+
from schemathesis.cli.constants import EXTENSIONS_DOCUMENTATION_URL
|
10
13
|
from schemathesis.cli.core import get_terminal_width
|
11
14
|
from schemathesis.cli.ext.groups import CommandWithGroupedOptions, GroupedOption
|
15
|
+
from schemathesis.config import ConfigError, SchemathesisConfig
|
16
|
+
from schemathesis.core.errors import HookError, format_exception
|
17
|
+
from schemathesis.core.version import SCHEMATHESIS_VERSION
|
12
18
|
|
13
19
|
CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
|
14
20
|
|
15
21
|
|
16
22
|
@click.group(context_settings=CONTEXT_SETTINGS) # type: ignore[misc]
|
23
|
+
@click.option( # type: ignore[misc]
|
24
|
+
"--config-file",
|
25
|
+
"config_file",
|
26
|
+
help="The path to `schemathesis.toml` file to use for configuration",
|
27
|
+
metavar="PATH",
|
28
|
+
type=str,
|
29
|
+
)
|
30
|
+
@click.pass_context # type: ignore[misc]
|
17
31
|
@click.version_option() # type: ignore[misc]
|
18
|
-
def schemathesis() -> None:
|
32
|
+
def schemathesis(ctx: click.Context, config_file: str | None) -> None:
|
19
33
|
"""Property-based API testing for OpenAPI and GraphQL."""
|
20
|
-
|
34
|
+
try:
|
35
|
+
if config_file is not None:
|
36
|
+
config = SchemathesisConfig.from_path(config_file)
|
37
|
+
else:
|
38
|
+
config = SchemathesisConfig.discover()
|
39
|
+
except (TOMLDecodeError, ConfigError) as exc:
|
40
|
+
display_header(SCHEMATHESIS_VERSION)
|
41
|
+
click.secho(
|
42
|
+
f"❌ Failed to load configuration file{f' from {config_file}' if config_file else ''}",
|
43
|
+
fg="red",
|
44
|
+
bold=True,
|
45
|
+
)
|
46
|
+
if isinstance(exc, TOMLDecodeError):
|
47
|
+
detail = "The configuration file content is not valid TOML"
|
48
|
+
else:
|
49
|
+
detail = "The loaded configuration is incorrect"
|
50
|
+
click.echo(f"\n{detail}\n\n{exc}")
|
51
|
+
ctx.exit(1)
|
52
|
+
except HookError as exc:
|
53
|
+
click.secho("Unable to load Schemathesis extension hooks", fg="red", bold=True)
|
54
|
+
formatted_module_name = click.style(f"'{exc.module_path}'", bold=True)
|
55
|
+
cause = exc.__cause__
|
56
|
+
assert isinstance(cause, Exception)
|
57
|
+
if isinstance(cause, ModuleNotFoundError) and cause.name == exc.module_path:
|
58
|
+
click.echo(
|
59
|
+
f"\nAn attempt to import the module {formatted_module_name} failed because it could not be found."
|
60
|
+
)
|
61
|
+
click.echo("\nEnsure the module name is correctly spelled and reachable from the current directory.")
|
62
|
+
else:
|
63
|
+
click.echo(f"\nAn error occurred while importing the module {formatted_module_name}. Traceback:")
|
64
|
+
message = format_exception(cause, with_traceback=True, skip_frames=1)
|
65
|
+
click.secho(f"\n{message}", fg="red")
|
66
|
+
click.echo(f"\nFor more information on how to work with hooks, visit {EXTENSIONS_DOCUMENTATION_URL}")
|
67
|
+
ctx.exit(1)
|
68
|
+
ctx.obj = Data(config=config)
|
21
69
|
|
22
70
|
|
23
71
|
@dataclass
|