schemathesis 3.39.16__py3-none-any.whl → 4.0.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 +41 -79
- schemathesis/auths.py +111 -122
- schemathesis/checks.py +169 -60
- schemathesis/cli/__init__.py +15 -2117
- schemathesis/cli/commands/__init__.py +85 -0
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +590 -0
- schemathesis/cli/commands/run/context.py +204 -0
- schemathesis/cli/commands/run/events.py +60 -0
- schemathesis/cli/commands/run/executor.py +157 -0
- schemathesis/cli/commands/run/filters.py +53 -0
- schemathesis/cli/commands/run/handlers/__init__.py +46 -0
- schemathesis/cli/commands/run/handlers/base.py +18 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +474 -0
- schemathesis/cli/commands/run/handlers/junitxml.py +55 -0
- schemathesis/cli/commands/run/handlers/output.py +1628 -0
- schemathesis/cli/commands/run/loaders.py +114 -0
- schemathesis/cli/commands/run/validation.py +246 -0
- schemathesis/cli/constants.py +5 -58
- schemathesis/cli/core.py +19 -0
- schemathesis/cli/ext/fs.py +16 -0
- schemathesis/cli/ext/groups.py +84 -0
- schemathesis/cli/{options.py → ext/options.py} +36 -34
- schemathesis/config/__init__.py +189 -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 +149 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +327 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +187 -0
- schemathesis/config/_projects.py +527 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +120 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/_warnings.py +25 -0
- schemathesis/config/schema.json +885 -0
- schemathesis/core/__init__.py +67 -0
- schemathesis/core/compat.py +32 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +58 -0
- schemathesis/core/deserialization.py +65 -0
- schemathesis/core/errors.py +459 -0
- schemathesis/core/failures.py +315 -0
- schemathesis/core/fs.py +19 -0
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/loaders.py +104 -0
- schemathesis/core/marks.py +66 -0
- schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
- schemathesis/core/output/__init__.py +46 -0
- schemathesis/core/output/sanitization.py +54 -0
- schemathesis/{throttling.py → core/rate_limit.py} +16 -17
- schemathesis/core/registries.py +31 -0
- schemathesis/core/transforms.py +113 -0
- schemathesis/core/transport.py +223 -0
- schemathesis/core/validation.py +54 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +28 -0
- schemathesis/engine/context.py +118 -0
- schemathesis/engine/control.py +36 -0
- schemathesis/engine/core.py +169 -0
- schemathesis/engine/errors.py +464 -0
- schemathesis/engine/events.py +258 -0
- schemathesis/engine/phases/__init__.py +88 -0
- schemathesis/{runner → engine/phases}/probes.py +52 -68
- schemathesis/engine/phases/stateful/__init__.py +68 -0
- schemathesis/engine/phases/stateful/_executor.py +356 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +212 -0
- schemathesis/engine/phases/unit/_executor.py +416 -0
- schemathesis/engine/phases/unit/_pool.py +82 -0
- schemathesis/engine/recorder.py +247 -0
- schemathesis/errors.py +43 -0
- schemathesis/filters.py +17 -98
- schemathesis/generation/__init__.py +5 -33
- schemathesis/generation/case.py +317 -0
- schemathesis/generation/coverage.py +282 -175
- schemathesis/generation/hypothesis/__init__.py +36 -0
- schemathesis/generation/hypothesis/builder.py +800 -0
- schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
- schemathesis/generation/hypothesis/given.py +66 -0
- schemathesis/generation/hypothesis/reporting.py +14 -0
- schemathesis/generation/hypothesis/strategies.py +16 -0
- schemathesis/generation/meta.py +115 -0
- schemathesis/generation/metrics.py +93 -0
- schemathesis/generation/modes.py +20 -0
- schemathesis/generation/overrides.py +116 -0
- schemathesis/generation/stateful/__init__.py +37 -0
- schemathesis/generation/stateful/state_machine.py +278 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +284 -0
- schemathesis/hooks.py +80 -101
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +455 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +72 -0
- schemathesis/openapi/loaders.py +313 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +281 -0
- schemathesis/pytest/loaders.py +36 -0
- schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +128 -108
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +537 -273
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/_cache.py +1 -2
- schemathesis/specs/graphql/scalars.py +42 -6
- schemathesis/specs/graphql/schemas.py +141 -137
- schemathesis/specs/graphql/validation.py +11 -17
- schemathesis/specs/openapi/__init__.py +6 -1
- schemathesis/specs/openapi/_cache.py +1 -2
- schemathesis/specs/openapi/_hypothesis.py +142 -156
- schemathesis/specs/openapi/checks.py +368 -257
- schemathesis/specs/openapi/converter.py +4 -4
- schemathesis/specs/openapi/definitions.py +1 -1
- schemathesis/specs/openapi/examples.py +23 -21
- schemathesis/specs/openapi/expressions/__init__.py +31 -19
- schemathesis/specs/openapi/expressions/extractors.py +1 -4
- schemathesis/specs/openapi/expressions/lexer.py +1 -1
- schemathesis/specs/openapi/expressions/nodes.py +36 -41
- schemathesis/specs/openapi/expressions/parser.py +1 -1
- schemathesis/specs/openapi/formats.py +35 -7
- schemathesis/specs/openapi/media_types.py +53 -12
- schemathesis/specs/openapi/negative/__init__.py +7 -4
- schemathesis/specs/openapi/negative/mutations.py +6 -5
- schemathesis/specs/openapi/parameters.py +7 -10
- schemathesis/specs/openapi/patterns.py +94 -31
- schemathesis/specs/openapi/references.py +12 -53
- schemathesis/specs/openapi/schemas.py +233 -307
- schemathesis/specs/openapi/security.py +1 -1
- schemathesis/specs/openapi/serialization.py +12 -6
- schemathesis/specs/openapi/stateful/__init__.py +268 -133
- schemathesis/specs/openapi/stateful/control.py +87 -0
- schemathesis/specs/openapi/stateful/links.py +209 -0
- schemathesis/transport/__init__.py +142 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +124 -0
- schemathesis/transport/requests.py +244 -0
- schemathesis/{_xml.py → transport/serialization.py} +69 -11
- schemathesis/transport/wsgi.py +171 -0
- schemathesis-4.0.0.dist-info/METADATA +204 -0
- schemathesis-4.0.0.dist-info/RECORD +164 -0
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/entry_points.txt +1 -1
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/licenses/LICENSE +1 -1
- schemathesis/_compat.py +0 -74
- schemathesis/_dependency_versions.py +0 -19
- schemathesis/_hypothesis.py +0 -717
- schemathesis/_override.py +0 -50
- schemathesis/_patches.py +0 -21
- schemathesis/_rate_limiter.py +0 -7
- schemathesis/cli/callbacks.py +0 -466
- schemathesis/cli/cassettes.py +0 -561
- schemathesis/cli/context.py +0 -75
- schemathesis/cli/debug.py +0 -27
- schemathesis/cli/handlers.py +0 -19
- schemathesis/cli/junitxml.py +0 -124
- schemathesis/cli/output/__init__.py +0 -1
- schemathesis/cli/output/default.py +0 -920
- schemathesis/cli/output/short.py +0 -59
- schemathesis/cli/reporting.py +0 -79
- schemathesis/cli/sanitization.py +0 -26
- schemathesis/code_samples.py +0 -151
- schemathesis/constants.py +0 -54
- schemathesis/contrib/__init__.py +0 -11
- schemathesis/contrib/openapi/__init__.py +0 -11
- schemathesis/contrib/openapi/fill_missing_examples.py +0 -24
- schemathesis/contrib/openapi/formats/__init__.py +0 -9
- schemathesis/contrib/openapi/formats/uuid.py +0 -16
- schemathesis/contrib/unique_data.py +0 -41
- schemathesis/exceptions.py +0 -571
- schemathesis/experimental/__init__.py +0 -109
- schemathesis/extra/_aiohttp.py +0 -28
- schemathesis/extra/_flask.py +0 -13
- schemathesis/extra/_server.py +0 -18
- schemathesis/failures.py +0 -284
- schemathesis/fixups/__init__.py +0 -37
- schemathesis/fixups/fast_api.py +0 -41
- schemathesis/fixups/utf8_bom.py +0 -28
- schemathesis/generation/_methods.py +0 -44
- schemathesis/graphql.py +0 -3
- schemathesis/internal/__init__.py +0 -7
- schemathesis/internal/checks.py +0 -86
- schemathesis/internal/copy.py +0 -32
- schemathesis/internal/datetime.py +0 -5
- schemathesis/internal/deprecation.py +0 -37
- schemathesis/internal/diff.py +0 -15
- schemathesis/internal/extensions.py +0 -27
- schemathesis/internal/jsonschema.py +0 -36
- schemathesis/internal/output.py +0 -68
- schemathesis/internal/transformation.py +0 -26
- schemathesis/internal/validation.py +0 -34
- schemathesis/lazy.py +0 -474
- schemathesis/loaders.py +0 -122
- schemathesis/models.py +0 -1341
- schemathesis/parameters.py +0 -90
- schemathesis/runner/__init__.py +0 -605
- schemathesis/runner/events.py +0 -389
- schemathesis/runner/impl/__init__.py +0 -3
- schemathesis/runner/impl/context.py +0 -88
- schemathesis/runner/impl/core.py +0 -1280
- schemathesis/runner/impl/solo.py +0 -80
- schemathesis/runner/impl/threadpool.py +0 -391
- schemathesis/runner/serialization.py +0 -544
- schemathesis/sanitization.py +0 -252
- schemathesis/serializers.py +0 -328
- schemathesis/service/__init__.py +0 -18
- schemathesis/service/auth.py +0 -11
- schemathesis/service/ci.py +0 -202
- schemathesis/service/client.py +0 -133
- schemathesis/service/constants.py +0 -38
- schemathesis/service/events.py +0 -61
- schemathesis/service/extensions.py +0 -224
- schemathesis/service/hosts.py +0 -111
- schemathesis/service/metadata.py +0 -71
- schemathesis/service/models.py +0 -258
- schemathesis/service/report.py +0 -255
- schemathesis/service/serialization.py +0 -173
- schemathesis/service/usage.py +0 -66
- schemathesis/specs/graphql/loaders.py +0 -364
- schemathesis/specs/openapi/expressions/context.py +0 -16
- schemathesis/specs/openapi/links.py +0 -389
- schemathesis/specs/openapi/loaders.py +0 -707
- schemathesis/specs/openapi/stateful/statistic.py +0 -198
- schemathesis/specs/openapi/stateful/types.py +0 -14
- schemathesis/specs/openapi/validation.py +0 -26
- schemathesis/stateful/__init__.py +0 -147
- schemathesis/stateful/config.py +0 -97
- schemathesis/stateful/context.py +0 -135
- schemathesis/stateful/events.py +0 -274
- schemathesis/stateful/runner.py +0 -309
- schemathesis/stateful/sink.py +0 -68
- schemathesis/stateful/state_machine.py +0 -328
- schemathesis/stateful/statistic.py +0 -22
- schemathesis/stateful/validation.py +0 -100
- schemathesis/targets.py +0 -77
- schemathesis/transports/__init__.py +0 -369
- schemathesis/transports/asgi.py +0 -7
- schemathesis/transports/auth.py +0 -38
- schemathesis/transports/headers.py +0 -36
- schemathesis/transports/responses.py +0 -57
- schemathesis/types.py +0 -44
- schemathesis/utils.py +0 -164
- schemathesis-3.39.16.dist-info/METADATA +0 -293
- schemathesis-3.39.16.dist-info/RECORD +0 -160
- /schemathesis/{extra → cli/ext}/__init__.py +0 -0
- /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
- /schemathesis/{internal → core}/result.py +0 -0
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,317 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
5
|
+
|
6
|
+
from schemathesis import transport
|
7
|
+
from schemathesis.checks import CHECKS, CheckContext, CheckFunction, run_checks
|
8
|
+
from schemathesis.core import NOT_SET, SCHEMATHESIS_TEST_CASE_HEADER, NotSet, curl
|
9
|
+
from schemathesis.core.failures import FailureGroup, failure_report_title, format_failures
|
10
|
+
from schemathesis.core.transport import Response
|
11
|
+
from schemathesis.generation import generate_random_case_id
|
12
|
+
from schemathesis.generation.meta import CaseMetadata
|
13
|
+
from schemathesis.generation.overrides import Override, store_components
|
14
|
+
from schemathesis.hooks import HookContext, dispatch
|
15
|
+
from schemathesis.transport.prepare import prepare_path, prepare_request
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
import httpx
|
19
|
+
import requests
|
20
|
+
import requests.auth
|
21
|
+
from requests.structures import CaseInsensitiveDict
|
22
|
+
from werkzeug.test import TestResponse
|
23
|
+
|
24
|
+
from schemathesis.schemas import APIOperation
|
25
|
+
|
26
|
+
|
27
|
+
def _default_headers() -> CaseInsensitiveDict:
|
28
|
+
from requests.structures import CaseInsensitiveDict
|
29
|
+
|
30
|
+
return CaseInsensitiveDict()
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class Case:
|
35
|
+
"""Generated test case data for a single API operation."""
|
36
|
+
|
37
|
+
operation: APIOperation
|
38
|
+
method: str
|
39
|
+
"""HTTP verb (`GET`, `POST`, etc.)"""
|
40
|
+
path: str
|
41
|
+
"""Path template from schema (e.g., `/users/{user_id}`)"""
|
42
|
+
id: str
|
43
|
+
"""Random ID sent in headers for log correlation"""
|
44
|
+
path_parameters: dict[str, Any]
|
45
|
+
"""Generated path variables (e.g., `{"user_id": "123"}`)"""
|
46
|
+
headers: CaseInsensitiveDict
|
47
|
+
"""Generated HTTP headers"""
|
48
|
+
cookies: dict[str, Any]
|
49
|
+
"""Generated cookies"""
|
50
|
+
query: dict[str, Any]
|
51
|
+
"""Generated query parameters"""
|
52
|
+
# By default, there is no body, but we can't use `None` as the default value because it clashes with `null`
|
53
|
+
# which is a valid payload.
|
54
|
+
body: list | dict[str, Any] | str | int | float | bool | bytes | NotSet
|
55
|
+
"""Generated request body"""
|
56
|
+
media_type: str | None
|
57
|
+
"""Media type from OpenAPI schema (e.g., "multipart/form-data")"""
|
58
|
+
|
59
|
+
meta: CaseMetadata | None
|
60
|
+
|
61
|
+
_auth: requests.auth.AuthBase | None
|
62
|
+
_has_explicit_auth: bool
|
63
|
+
|
64
|
+
__slots__ = (
|
65
|
+
"operation",
|
66
|
+
"method",
|
67
|
+
"path",
|
68
|
+
"id",
|
69
|
+
"path_parameters",
|
70
|
+
"headers",
|
71
|
+
"cookies",
|
72
|
+
"query",
|
73
|
+
"body",
|
74
|
+
"media_type",
|
75
|
+
"meta",
|
76
|
+
"_auth",
|
77
|
+
"_has_explicit_auth",
|
78
|
+
"_components",
|
79
|
+
)
|
80
|
+
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
operation: APIOperation,
|
84
|
+
method: str,
|
85
|
+
path: str,
|
86
|
+
*,
|
87
|
+
id: str | None = None,
|
88
|
+
path_parameters: dict[str, Any] | None = None,
|
89
|
+
headers: CaseInsensitiveDict | None = None,
|
90
|
+
cookies: dict[str, Any] | None = None,
|
91
|
+
query: dict[str, Any] | None = None,
|
92
|
+
body: list | dict[str, Any] | str | int | float | bool | bytes | "NotSet" = NOT_SET,
|
93
|
+
media_type: str | None = None,
|
94
|
+
meta: CaseMetadata | None = None,
|
95
|
+
_auth: requests.auth.AuthBase | None = None,
|
96
|
+
_has_explicit_auth: bool = False,
|
97
|
+
) -> None:
|
98
|
+
self.operation = operation
|
99
|
+
self.method = method
|
100
|
+
self.path = path
|
101
|
+
|
102
|
+
self.id = id if id is not None else generate_random_case_id()
|
103
|
+
self.path_parameters = path_parameters if path_parameters is not None else {}
|
104
|
+
self.headers = headers if headers is not None else _default_headers()
|
105
|
+
self.cookies = cookies if cookies is not None else {}
|
106
|
+
self.query = query if query is not None else {}
|
107
|
+
self.body = body
|
108
|
+
self.media_type = media_type
|
109
|
+
self.meta = meta
|
110
|
+
self._auth = _auth
|
111
|
+
self._has_explicit_auth = _has_explicit_auth
|
112
|
+
self._components = store_components(self)
|
113
|
+
|
114
|
+
def __eq__(self, other: object) -> bool:
|
115
|
+
if not isinstance(other, Case):
|
116
|
+
return NotImplemented
|
117
|
+
|
118
|
+
return (
|
119
|
+
self.operation == other.operation
|
120
|
+
and self.method == other.method
|
121
|
+
and self.path == other.path
|
122
|
+
and self.path_parameters == other.path_parameters
|
123
|
+
and self.headers == other.headers
|
124
|
+
and self.cookies == other.cookies
|
125
|
+
and self.query == other.query
|
126
|
+
and self.body == other.body
|
127
|
+
and self.media_type == other.media_type
|
128
|
+
)
|
129
|
+
|
130
|
+
@property
|
131
|
+
def _override(self) -> Override:
|
132
|
+
return Override.from_components(self._components, self)
|
133
|
+
|
134
|
+
def __repr__(self) -> str:
|
135
|
+
output = f"{self.__class__.__name__}("
|
136
|
+
first = True
|
137
|
+
for name in ("path_parameters", "headers", "cookies", "query", "body"):
|
138
|
+
value = getattr(self, name)
|
139
|
+
if name != "body" and not value:
|
140
|
+
continue
|
141
|
+
if value is not None and not isinstance(value, NotSet):
|
142
|
+
if first:
|
143
|
+
first = False
|
144
|
+
else:
|
145
|
+
output += ", "
|
146
|
+
output += f"{name}={value!r}"
|
147
|
+
return f"{output})"
|
148
|
+
|
149
|
+
def __hash__(self) -> int:
|
150
|
+
return hash(self.as_curl_command({SCHEMATHESIS_TEST_CASE_HEADER: "0"}))
|
151
|
+
|
152
|
+
def _repr_pretty_(self, *args: Any, **kwargs: Any) -> None: ...
|
153
|
+
|
154
|
+
@property
|
155
|
+
def formatted_path(self) -> str:
|
156
|
+
"""Path template with variables substituted (e.g., /users/{user_id} → /users/123)."""
|
157
|
+
return prepare_path(self.path, self.path_parameters)
|
158
|
+
|
159
|
+
def as_curl_command(self, headers: Mapping[str, Any] | None = None, verify: bool = True) -> str:
|
160
|
+
"""Generate a curl command that reproduces this test case.
|
161
|
+
|
162
|
+
Args:
|
163
|
+
headers: Additional headers to include in the command.
|
164
|
+
verify: When False, adds `--insecure` flag to curl command.
|
165
|
+
|
166
|
+
"""
|
167
|
+
request_data = prepare_request(self, headers, config=self.operation.schema.config.output.sanitization)
|
168
|
+
return curl.generate(
|
169
|
+
method=str(request_data.method),
|
170
|
+
url=str(request_data.url),
|
171
|
+
body=request_data.body,
|
172
|
+
verify=verify,
|
173
|
+
headers=dict(request_data.headers),
|
174
|
+
known_generated_headers=dict(self.headers or {}),
|
175
|
+
)
|
176
|
+
|
177
|
+
def as_transport_kwargs(self, base_url: str | None = None, headers: dict[str, str] | None = None) -> dict[str, Any]:
|
178
|
+
return self.operation.schema.transport.serialize_case(self, base_url=base_url, headers=headers)
|
179
|
+
|
180
|
+
def call(
|
181
|
+
self,
|
182
|
+
base_url: str | None = None,
|
183
|
+
session: requests.Session | None = None,
|
184
|
+
headers: dict[str, Any] | None = None,
|
185
|
+
params: dict[str, Any] | None = None,
|
186
|
+
cookies: dict[str, Any] | None = None,
|
187
|
+
**kwargs: Any,
|
188
|
+
) -> Response:
|
189
|
+
"""Make an HTTP request using this test case's data without validation.
|
190
|
+
|
191
|
+
Use when you need to validate response separately
|
192
|
+
|
193
|
+
Args:
|
194
|
+
base_url: Override the schema's base URL.
|
195
|
+
session: Reuse an existing requests session.
|
196
|
+
headers: Additional headers.
|
197
|
+
params: Additional query parameters.
|
198
|
+
cookies: Additional cookies.
|
199
|
+
**kwargs: Additional transport-level arguments.
|
200
|
+
|
201
|
+
"""
|
202
|
+
hook_context = HookContext(operation=self.operation)
|
203
|
+
dispatch("before_call", hook_context, self, **kwargs)
|
204
|
+
if self.operation.app is not None:
|
205
|
+
kwargs.setdefault("app", self.operation.app)
|
206
|
+
if "app" in kwargs:
|
207
|
+
transport_ = transport.get(kwargs["app"])
|
208
|
+
else:
|
209
|
+
transport_ = self.operation.schema.transport
|
210
|
+
response = transport_.send(
|
211
|
+
self,
|
212
|
+
session=session,
|
213
|
+
base_url=base_url,
|
214
|
+
headers=headers,
|
215
|
+
params=params,
|
216
|
+
cookies=cookies,
|
217
|
+
**kwargs,
|
218
|
+
)
|
219
|
+
dispatch("after_call", hook_context, self, response)
|
220
|
+
return response
|
221
|
+
|
222
|
+
def validate_response(
|
223
|
+
self,
|
224
|
+
response: Response | httpx.Response | requests.Response | TestResponse,
|
225
|
+
checks: list[CheckFunction] | None = None,
|
226
|
+
additional_checks: list[CheckFunction] | None = None,
|
227
|
+
excluded_checks: list[CheckFunction] | None = None,
|
228
|
+
headers: dict[str, Any] | None = None,
|
229
|
+
transport_kwargs: dict[str, Any] | None = None,
|
230
|
+
) -> None:
|
231
|
+
"""Validate a response against the API schema and built-in checks.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
response: Response to validate.
|
235
|
+
checks: Explicit set of checks to run.
|
236
|
+
additional_checks: Additional custom checks to run.
|
237
|
+
excluded_checks: Built-in checks to skip.
|
238
|
+
headers: Headers used in the original request.
|
239
|
+
transport_kwargs: Transport arguments used in the original request.
|
240
|
+
|
241
|
+
"""
|
242
|
+
__tracebackhide__ = True
|
243
|
+
from requests.structures import CaseInsensitiveDict
|
244
|
+
|
245
|
+
response = Response.from_any(response)
|
246
|
+
|
247
|
+
checks = [
|
248
|
+
check
|
249
|
+
for check in list(checks or CHECKS.get_all()) + list(additional_checks or [])
|
250
|
+
if check not in set(excluded_checks or [])
|
251
|
+
]
|
252
|
+
|
253
|
+
ctx = CheckContext(
|
254
|
+
override=self._override,
|
255
|
+
auth=None,
|
256
|
+
headers=CaseInsensitiveDict(headers) if headers else None,
|
257
|
+
config=self.operation.schema.config.checks_config_for(
|
258
|
+
operation=self.operation, phase=self.meta.phase.name.value if self.meta is not None else None
|
259
|
+
),
|
260
|
+
transport_kwargs=transport_kwargs,
|
261
|
+
recorder=None,
|
262
|
+
)
|
263
|
+
failures = run_checks(
|
264
|
+
case=self,
|
265
|
+
response=response,
|
266
|
+
ctx=ctx,
|
267
|
+
checks=checks,
|
268
|
+
on_failure=lambda _, collected, failure: collected.add(failure),
|
269
|
+
)
|
270
|
+
if failures:
|
271
|
+
_failures = list(failures)
|
272
|
+
message = failure_report_title(_failures) + "\n"
|
273
|
+
verify = getattr(response, "verify", True)
|
274
|
+
curl = self.as_curl_command(headers=dict(response.request.headers), verify=verify)
|
275
|
+
message += format_failures(
|
276
|
+
case_id=None,
|
277
|
+
response=response,
|
278
|
+
failures=_failures,
|
279
|
+
curl=curl,
|
280
|
+
config=self.operation.schema.config.output,
|
281
|
+
)
|
282
|
+
message += "\n\n"
|
283
|
+
raise FailureGroup(_failures, message) from None
|
284
|
+
|
285
|
+
def call_and_validate(
|
286
|
+
self,
|
287
|
+
base_url: str | None = None,
|
288
|
+
session: requests.Session | None = None,
|
289
|
+
headers: dict[str, Any] | None = None,
|
290
|
+
checks: list[CheckFunction] | None = None,
|
291
|
+
additional_checks: list[CheckFunction] | None = None,
|
292
|
+
excluded_checks: list[CheckFunction] | None = None,
|
293
|
+
**kwargs: Any,
|
294
|
+
) -> Response:
|
295
|
+
"""Make an HTTP request and validates the response automatically.
|
296
|
+
|
297
|
+
Args:
|
298
|
+
base_url: Override the schema's base URL.
|
299
|
+
session: Reuse an existing requests session.
|
300
|
+
headers: Additional headers to send.
|
301
|
+
checks: Explicit set of checks to run.
|
302
|
+
additional_checks: Additional custom checks to run.
|
303
|
+
excluded_checks: Built-in checks to skip.
|
304
|
+
**kwargs: Additional transport-level arguments.
|
305
|
+
|
306
|
+
"""
|
307
|
+
__tracebackhide__ = True
|
308
|
+
response = self.call(base_url, session, headers, **kwargs)
|
309
|
+
self.validate_response(
|
310
|
+
response,
|
311
|
+
checks,
|
312
|
+
headers=headers,
|
313
|
+
additional_checks=additional_checks,
|
314
|
+
excluded_checks=excluded_checks,
|
315
|
+
transport_kwargs=kwargs,
|
316
|
+
)
|
317
|
+
return response
|