schemathesis 3.39.11__py3-none-any.whl → 3.39.12__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/_hypothesis.py +15 -5
- schemathesis/cli/__init__.py +14 -1
- schemathesis/cli/callbacks.py +8 -0
- schemathesis/cli/options.py +6 -1
- schemathesis/generation/__init__.py +1 -0
- schemathesis/specs/openapi/patterns.py +15 -0
- {schemathesis-3.39.11.dist-info → schemathesis-3.39.12.dist-info}/METADATA +1 -1
- {schemathesis-3.39.11.dist-info → schemathesis-3.39.12.dist-info}/RECORD +11 -11
- {schemathesis-3.39.11.dist-info → schemathesis-3.39.12.dist-info}/WHEEL +0 -0
- {schemathesis-3.39.11.dist-info → schemathesis-3.39.12.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.39.11.dist-info → schemathesis-3.39.12.dist-info}/licenses/LICENSE +0 -0
schemathesis/_hypothesis.py
CHANGED
@@ -114,8 +114,14 @@ def create_test(
|
|
114
114
|
wrapped_test, operation, hook_dispatcher=hook_dispatcher, as_strategy_kwargs=as_strategy_kwargs
|
115
115
|
)
|
116
116
|
if COVERAGE_PHASE.is_enabled:
|
117
|
+
unexpected_methods = generation_config.unexpected_methods if generation_config else None
|
117
118
|
wrapped_test = add_coverage(
|
118
|
-
wrapped_test,
|
119
|
+
wrapped_test,
|
120
|
+
operation,
|
121
|
+
data_generation_methods,
|
122
|
+
auth_storage,
|
123
|
+
as_strategy_kwargs,
|
124
|
+
unexpected_methods,
|
119
125
|
)
|
120
126
|
return wrapped_test
|
121
127
|
|
@@ -225,6 +231,7 @@ def add_coverage(
|
|
225
231
|
data_generation_methods: list[DataGenerationMethod],
|
226
232
|
auth_storage: AuthStorage | None,
|
227
233
|
as_strategy_kwargs: dict[str, Any],
|
234
|
+
unexpected_methods: set[str] | None = None,
|
228
235
|
) -> Callable:
|
229
236
|
from schemathesis.specs.openapi.constants import LOCATION_TO_CONTAINER
|
230
237
|
|
@@ -237,7 +244,7 @@ def add_coverage(
|
|
237
244
|
for container in LOCATION_TO_CONTAINER.values()
|
238
245
|
if container in as_strategy_kwargs
|
239
246
|
}
|
240
|
-
for case in _iter_coverage_cases(operation, data_generation_methods):
|
247
|
+
for case in _iter_coverage_cases(operation, data_generation_methods, unexpected_methods):
|
241
248
|
if case.media_type and get_first_matching_media_type(case.media_type) is None:
|
242
249
|
continue
|
243
250
|
adjust_urlencoded_payload(case)
|
@@ -363,7 +370,9 @@ def _stringify_value(val: Any, container_name: str) -> Any:
|
|
363
370
|
|
364
371
|
|
365
372
|
def _iter_coverage_cases(
|
366
|
-
operation: APIOperation,
|
373
|
+
operation: APIOperation,
|
374
|
+
data_generation_methods: list[DataGenerationMethod],
|
375
|
+
unexpected_methods: set[str] | None = None,
|
367
376
|
) -> Generator[Case, None, None]:
|
368
377
|
from .specs.openapi.constants import LOCATION_TO_CONTAINER
|
369
378
|
from .specs.openapi.examples import find_in_responses, find_matching_in_responses
|
@@ -373,6 +382,8 @@ def _iter_coverage_cases(
|
|
373
382
|
serializers = get_serializers_for_operation(operation)
|
374
383
|
template = Template(serializers)
|
375
384
|
responses = find_in_responses(operation)
|
385
|
+
# NOTE: The HEAD method is excluded
|
386
|
+
unexpected_methods = unexpected_methods or {"get", "put", "post", "delete", "options", "patch", "trace"}
|
376
387
|
for parameter in operation.iter_parameters():
|
377
388
|
location = parameter.location
|
378
389
|
name = parameter.name
|
@@ -448,8 +459,7 @@ def _iter_coverage_cases(
|
|
448
459
|
yield case
|
449
460
|
if DataGenerationMethod.negative in data_generation_methods:
|
450
461
|
# Generate HTTP methods that are not specified in the spec
|
451
|
-
|
452
|
-
methods = {"get", "put", "post", "delete", "options", "patch", "trace"} - set(operation.schema[operation.path])
|
462
|
+
methods = unexpected_methods - set(operation.schema[operation.path])
|
453
463
|
for method in sorted(methods):
|
454
464
|
data = template.unmodified()
|
455
465
|
case = operation.make_case(**data.kwargs)
|
schemathesis/cli/__init__.py
CHANGED
@@ -9,7 +9,7 @@ from collections import defaultdict
|
|
9
9
|
from dataclasses import dataclass
|
10
10
|
from enum import Enum
|
11
11
|
from queue import Queue
|
12
|
-
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Literal, NoReturn, Sequence,
|
12
|
+
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Literal, NoReturn, Sequence, cast
|
13
13
|
from urllib.parse import urlparse
|
14
14
|
|
15
15
|
import click
|
@@ -27,6 +27,7 @@ from ..constants import (
|
|
27
27
|
DEFAULT_STATEFUL_RECURSION_LIMIT,
|
28
28
|
EXTENSIONS_DOCUMENTATION_URL,
|
29
29
|
HOOKS_MODULE_ENV_VAR,
|
30
|
+
HTTP_METHODS,
|
30
31
|
HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER,
|
31
32
|
ISSUE_TRACKER_URL,
|
32
33
|
WAIT_FOR_SCHEMA_ENV_VAR,
|
@@ -323,6 +324,16 @@ REPORT_TO_SERVICE = ReportToService()
|
|
323
324
|
multiple=True,
|
324
325
|
metavar="",
|
325
326
|
)
|
327
|
+
@grouped_option(
|
328
|
+
"--experimental-coverage-unexpected-methods",
|
329
|
+
"coverage_unexpected_methods",
|
330
|
+
help="HTTP methods to use when generating test cases with methods not specified in the API during the coverage phase.",
|
331
|
+
type=CsvChoice(sorted(HTTP_METHODS), case_sensitive=False),
|
332
|
+
callback=callbacks.convert_http_methods,
|
333
|
+
metavar="",
|
334
|
+
default=None,
|
335
|
+
envvar="SCHEMATHESIS_EXPERIMENTAL_COVERAGE_UNEXPECTED_METHODS",
|
336
|
+
)
|
326
337
|
@grouped_option(
|
327
338
|
"--experimental-no-failfast",
|
328
339
|
"no_failfast",
|
@@ -874,6 +885,7 @@ def run(
|
|
874
885
|
set_path: dict[str, str],
|
875
886
|
experiments: list,
|
876
887
|
no_failfast: bool,
|
888
|
+
coverage_unexpected_methods: set[str] | None,
|
877
889
|
missing_required_header_allowed_statuses: list[str],
|
878
890
|
positive_data_acceptance_allowed_statuses: list[str],
|
879
891
|
negative_data_rejection_allowed_statuses: list[str],
|
@@ -1000,6 +1012,7 @@ def run(
|
|
1000
1012
|
graphql_allow_null=generation_graphql_allow_null,
|
1001
1013
|
codec=generation_codec,
|
1002
1014
|
with_security_parameters=generation_with_security_parameters,
|
1015
|
+
unexpected_methods=coverage_unexpected_methods,
|
1003
1016
|
)
|
1004
1017
|
|
1005
1018
|
report: ReportToService | click.utils.LazyFile | None
|
schemathesis/cli/callbacks.py
CHANGED
@@ -344,6 +344,14 @@ def convert_checks(ctx: click.core.Context, param: click.core.Parameter, value:
|
|
344
344
|
return reduce(operator.iadd, value, [])
|
345
345
|
|
346
346
|
|
347
|
+
def convert_http_methods(
|
348
|
+
ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
|
349
|
+
) -> set[str] | None:
|
350
|
+
if value is None:
|
351
|
+
return value
|
352
|
+
return {item.lower() for item in value}
|
353
|
+
|
354
|
+
|
347
355
|
def convert_status_codes(
|
348
356
|
ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
|
349
357
|
) -> list[str] | None:
|
schemathesis/cli/options.py
CHANGED
@@ -26,7 +26,12 @@ class CustomHelpMessageChoice(click.Choice):
|
|
26
26
|
class BaseCsvChoice(click.Choice):
|
27
27
|
def parse_value(self, value: str) -> tuple[list[str], set[str]]:
|
28
28
|
selected = [item for item in value.split(",") if item]
|
29
|
-
|
29
|
+
if not self.case_sensitive:
|
30
|
+
invalid_options = {
|
31
|
+
item for item in selected if item.upper() not in {choice.upper() for choice in self.choices}
|
32
|
+
}
|
33
|
+
else:
|
34
|
+
invalid_options = set(selected) - set(self.choices)
|
30
35
|
return selected, invalid_options
|
31
36
|
|
32
37
|
def fail_on_invalid_options(self, invalid_options: set[str], selected: list[str]) -> NoReturn:
|
@@ -124,6 +124,21 @@ def _handle_anchored_pattern(parsed: list, pattern: str, min_length: int | None,
|
|
124
124
|
|
125
125
|
for op, value in pattern_parts:
|
126
126
|
if op == LITERAL:
|
127
|
+
# Check if the literal comes from a bracketed expression,
|
128
|
+
# e.g. Python regex parses "[+]" as a single LITERAL token.
|
129
|
+
if pattern[current_position] == "[":
|
130
|
+
# Find the matching closing bracket.
|
131
|
+
end_idx = current_position + 1
|
132
|
+
while end_idx < len(pattern):
|
133
|
+
# Check for an unescaped closing bracket.
|
134
|
+
if pattern[end_idx] == "]" and (end_idx == current_position + 1 or pattern[end_idx - 1] != "\\"):
|
135
|
+
end_idx += 1
|
136
|
+
break
|
137
|
+
end_idx += 1
|
138
|
+
# Append the entire character set.
|
139
|
+
result += pattern[current_position:end_idx]
|
140
|
+
current_position = end_idx
|
141
|
+
continue
|
127
142
|
if pattern[current_position] == "\\":
|
128
143
|
# Escaped value
|
129
144
|
current_position += 2
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemathesis
|
3
|
-
Version: 3.39.
|
3
|
+
Version: 3.39.12
|
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
|
@@ -1,7 +1,7 @@
|
|
1
1
|
schemathesis/__init__.py,sha256=UW2Bq8hDDkcBeAAA7PzpBFXkOOxkmHox-mfQwzHDjL0,1914
|
2
2
|
schemathesis/_compat.py,sha256=y4RZd59i2NCnZ91VQhnKeMn_8t3SgvLOk2Xm8nymUHY,1837
|
3
3
|
schemathesis/_dependency_versions.py,sha256=pjEkkGAfOQJYNb-9UOo84V8nj_lKHr_TGDVdFwY2UU0,816
|
4
|
-
schemathesis/_hypothesis.py,sha256=
|
4
|
+
schemathesis/_hypothesis.py,sha256=CEfWX38CsPy-RzwMGdKuJD9mY_AV8fIq_ZhabGp4tW0,30759
|
5
5
|
schemathesis/_lazy_import.py,sha256=aMhWYgbU2JOltyWBb32vnWBb6kykOghucEzI_F70yVE,470
|
6
6
|
schemathesis/_override.py,sha256=TAjYB3eJQmlw9K_xiR9ptt9Wj7if4U7UFlUhGjpBAoM,1625
|
7
7
|
schemathesis/_patches.py,sha256=Hsbpn4UVeXUQD2Kllrbq01CSWsTYENWa0VJTyhX5C2k,895
|
@@ -28,16 +28,16 @@ schemathesis/targets.py,sha256=XIGRghvEzbmEJjse9aZgNEj67L3jAbiazm2rxURWgDE,2351
|
|
28
28
|
schemathesis/throttling.py,sha256=aisUc4MJDGIOGUAs9L2DlWWpdd4KyAFuNVKhYoaUC9M,1719
|
29
29
|
schemathesis/types.py,sha256=Tem2Q_zyMCd0Clp5iGKv6Fu13wdcbxVE8tCVH9WWt7A,1065
|
30
30
|
schemathesis/utils.py,sha256=LwqxqoAKmRiAdj-qUbNmgQgsamc49V5lc5TnOIDuuMA,4904
|
31
|
-
schemathesis/cli/__init__.py,sha256=
|
31
|
+
schemathesis/cli/__init__.py,sha256=jiDVw31fF8oNl0TyAWJpDo-166PkCqNV4t1rUcy_4oo,76352
|
32
32
|
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
33
|
-
schemathesis/cli/callbacks.py,sha256
|
33
|
+
schemathesis/cli/callbacks.py,sha256=UMCYPfoHXgxtT-uui0zlfeNoOLzteJu90q9ID3qt1m4,16540
|
34
34
|
schemathesis/cli/cassettes.py,sha256=zji-B-uuwyr0Z0BzQX-DLMV6lWb58JtLExcUE1v3m4Y,20153
|
35
35
|
schemathesis/cli/constants.py,sha256=wk-0GsoJIel8wFFerQ6Kf_6eAYUtIWkwMFwyAqv3yj4,1635
|
36
36
|
schemathesis/cli/context.py,sha256=j_lvYQiPa6Q7P4P_IGCM9V2y2gJSpDbpxIIzR5oFB2I,2567
|
37
37
|
schemathesis/cli/debug.py,sha256=_YA-bX1ujHl4bqQDEum7M-I2XHBTEGbvgkhvcvKhmgU,658
|
38
38
|
schemathesis/cli/handlers.py,sha256=EXSAFe5TQlHANz1AVlSttfsoDT2oeaeFbqq1N7e2udw,467
|
39
39
|
schemathesis/cli/junitxml.py,sha256=_psBdqGwH4OKySSWeva41mbgGLav86UnWhQyOt99gnU,5331
|
40
|
-
schemathesis/cli/options.py,sha256=
|
40
|
+
schemathesis/cli/options.py,sha256=jPqJxkuAb91wtB_aOUFkGDdGtJ3UDwT2Nn3vvL-odsE,3062
|
41
41
|
schemathesis/cli/reporting.py,sha256=KC3sxSc1u4aFQ-0Q8CQ3G4HTEl7QxlubGnJgNKmVJdQ,3627
|
42
42
|
schemathesis/cli/sanitization.py,sha256=Onw_NWZSom6XTVNJ5NHnC0PAhrYAcGzIXJbsBCzLkn4,1005
|
43
43
|
schemathesis/cli/output/__init__.py,sha256=AXaUzQ1nhQ-vXhW4-X-91vE2VQtEcCOrGtQXXNN55iQ,29
|
@@ -58,7 +58,7 @@ schemathesis/extra/pytest_plugin.py,sha256=3FF7pcqK26J__FGq6uOZdaD-1tZ-khQEwpdwb
|
|
58
58
|
schemathesis/fixups/__init__.py,sha256=RP5QYJVJhp8LXjhH89fCRaIVU26dHCy74jD9seoYMuc,967
|
59
59
|
schemathesis/fixups/fast_api.py,sha256=mn-KzBqnR8jl4W5fY-_ZySabMDMUnpzCIESMHnlvE1c,1304
|
60
60
|
schemathesis/fixups/utf8_bom.py,sha256=lWT9RNmJG8i-l5AXIpaCT3qCPUwRgzXPW3eoOjmZETA,745
|
61
|
-
schemathesis/generation/__init__.py,sha256=
|
61
|
+
schemathesis/generation/__init__.py,sha256=PClFLK3bu-8Gsy71rgdD0ULMqySrzX-Um8Tan77x_5A,1628
|
62
62
|
schemathesis/generation/_hypothesis.py,sha256=74fzLPHugZgMQXerWYFAMqCAjtAXz5E4gek7Gnkhli4,1756
|
63
63
|
schemathesis/generation/_methods.py,sha256=r8oVlJ71_gXcnEhU-byw2E0R2RswQQFm8U7yGErSqbw,1204
|
64
64
|
schemathesis/generation/coverage.py,sha256=1CilQSe2DIdMdeWA6RL22so2bZULPRwc0CQBRxcLRFs,39370
|
@@ -117,7 +117,7 @@ schemathesis/specs/openapi/links.py,sha256=C4Uir2P_EcpqME8ee_a1vdUM8Tm3ZcKNn2YsG
|
|
117
117
|
schemathesis/specs/openapi/loaders.py,sha256=jlTYLoG5sVRh8xycIF2M2VDCZ44M80Sct07a_ycg1Po,25698
|
118
118
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
119
119
|
schemathesis/specs/openapi/parameters.py,sha256=X_3PKqUScIiN_vbSFEauPYyxASyFv-_9lZ_9QEZRLqo,14655
|
120
|
-
schemathesis/specs/openapi/patterns.py,sha256=
|
120
|
+
schemathesis/specs/openapi/patterns.py,sha256=L99UtslPvwObCVf5ndq3vL2YjQ7H1nMb-ZNMcyz_Qvk,12677
|
121
121
|
schemathesis/specs/openapi/references.py,sha256=euxM02kQGMHh4Ss1jWjOY_gyw_HazafKITIsvOEiAvI,9831
|
122
122
|
schemathesis/specs/openapi/schemas.py,sha256=JA9SiBnwYg75kYnd4_0CWOuQv_XTfYwuDeGmFe4RtVo,53724
|
123
123
|
schemathesis/specs/openapi/security.py,sha256=Z-6pk2Ga1PTUtBe298KunjVHsNh5A-teegeso7zcPIE,7138
|
@@ -153,8 +153,8 @@ schemathesis/transports/auth.py,sha256=urSTO9zgFO1qU69xvnKHPFQV0SlJL3d7_Ojl0tLnZ
|
|
153
153
|
schemathesis/transports/content_types.py,sha256=MiKOm-Hy5i75hrROPdpiBZPOTDzOwlCdnthJD12AJzI,2187
|
154
154
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
155
155
|
schemathesis/transports/responses.py,sha256=OFD4ZLqwEFpo7F9vaP_SVgjhxAqatxIj38FS4XVq8Qs,1680
|
156
|
-
schemathesis-3.39.
|
157
|
-
schemathesis-3.39.
|
158
|
-
schemathesis-3.39.
|
159
|
-
schemathesis-3.39.
|
160
|
-
schemathesis-3.39.
|
156
|
+
schemathesis-3.39.12.dist-info/METADATA,sha256=B2ychHNPR-8UTeOqBUcqdtLzYPdZEO61YaYadjp9B2A,11977
|
157
|
+
schemathesis-3.39.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
158
|
+
schemathesis-3.39.12.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
159
|
+
schemathesis-3.39.12.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
160
|
+
schemathesis-3.39.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|