schemathesis 3.15.4__py3-none-any.whl → 4.4.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/__init__.py +53 -25
- schemathesis/auths.py +507 -0
- schemathesis/checks.py +190 -25
- schemathesis/cli/__init__.py +27 -1219
- schemathesis/cli/__main__.py +4 -0
- schemathesis/cli/commands/__init__.py +133 -0
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +602 -0
- schemathesis/cli/commands/run/context.py +228 -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 +45 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
- schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
- schemathesis/cli/commands/run/handlers/output.py +1750 -0
- schemathesis/cli/commands/run/loaders.py +118 -0
- schemathesis/cli/commands/run/validation.py +256 -0
- schemathesis/cli/constants.py +5 -0
- schemathesis/cli/core.py +19 -0
- schemathesis/cli/ext/fs.py +16 -0
- schemathesis/cli/ext/groups.py +203 -0
- schemathesis/cli/ext/options.py +81 -0
- schemathesis/config/__init__.py +202 -0
- schemathesis/config/_auth.py +51 -0
- schemathesis/config/_checks.py +268 -0
- schemathesis/config/_diff_base.py +101 -0
- schemathesis/config/_env.py +21 -0
- schemathesis/config/_error.py +163 -0
- schemathesis/config/_generation.py +157 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +335 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +253 -0
- schemathesis/config/_projects.py +543 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +120 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/_warnings.py +89 -0
- schemathesis/config/schema.json +975 -0
- schemathesis/core/__init__.py +72 -0
- schemathesis/core/adapter.py +34 -0
- schemathesis/core/compat.py +32 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +100 -0
- schemathesis/core/deserialization.py +210 -0
- schemathesis/core/errors.py +588 -0
- schemathesis/core/failures.py +316 -0
- schemathesis/core/fs.py +19 -0
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/jsonschema/__init__.py +13 -0
- schemathesis/core/jsonschema/bundler.py +183 -0
- schemathesis/core/jsonschema/keywords.py +40 -0
- schemathesis/core/jsonschema/references.py +222 -0
- schemathesis/core/jsonschema/types.py +41 -0
- schemathesis/core/lazy_import.py +15 -0
- schemathesis/core/loaders.py +107 -0
- schemathesis/core/marks.py +66 -0
- schemathesis/core/media_types.py +79 -0
- schemathesis/core/output/__init__.py +46 -0
- schemathesis/core/output/sanitization.py +54 -0
- schemathesis/core/parameters.py +45 -0
- schemathesis/core/rate_limit.py +60 -0
- schemathesis/core/registries.py +34 -0
- schemathesis/core/result.py +27 -0
- schemathesis/core/schema_analysis.py +17 -0
- schemathesis/core/shell.py +203 -0
- schemathesis/core/transforms.py +144 -0
- schemathesis/core/transport.py +223 -0
- schemathesis/core/validation.py +73 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +28 -0
- schemathesis/engine/context.py +152 -0
- schemathesis/engine/control.py +44 -0
- schemathesis/engine/core.py +201 -0
- schemathesis/engine/errors.py +446 -0
- schemathesis/engine/events.py +284 -0
- schemathesis/engine/observations.py +42 -0
- schemathesis/engine/phases/__init__.py +108 -0
- schemathesis/engine/phases/analysis.py +28 -0
- schemathesis/engine/phases/probes.py +172 -0
- schemathesis/engine/phases/stateful/__init__.py +68 -0
- schemathesis/engine/phases/stateful/_executor.py +364 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +220 -0
- schemathesis/engine/phases/unit/_executor.py +459 -0
- schemathesis/engine/phases/unit/_pool.py +82 -0
- schemathesis/engine/recorder.py +254 -0
- schemathesis/errors.py +47 -0
- schemathesis/filters.py +395 -0
- schemathesis/generation/__init__.py +25 -0
- schemathesis/generation/case.py +478 -0
- schemathesis/generation/coverage.py +1528 -0
- schemathesis/generation/hypothesis/__init__.py +121 -0
- schemathesis/generation/hypothesis/builder.py +992 -0
- schemathesis/generation/hypothesis/examples.py +56 -0
- schemathesis/generation/hypothesis/given.py +66 -0
- schemathesis/generation/hypothesis/reporting.py +285 -0
- schemathesis/generation/meta.py +227 -0
- schemathesis/generation/metrics.py +93 -0
- schemathesis/generation/modes.py +20 -0
- schemathesis/generation/overrides.py +127 -0
- schemathesis/generation/stateful/__init__.py +37 -0
- schemathesis/generation/stateful/state_machine.py +294 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +285 -0
- schemathesis/hooks.py +270 -91
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +467 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +72 -0
- schemathesis/openapi/loaders.py +315 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +341 -0
- schemathesis/pytest/loaders.py +36 -0
- schemathesis/pytest/plugin.py +357 -0
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +682 -257
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/nodes.py +26 -2
- schemathesis/specs/graphql/scalars.py +77 -12
- schemathesis/specs/graphql/schemas.py +367 -148
- schemathesis/specs/graphql/validation.py +33 -0
- schemathesis/specs/openapi/__init__.py +9 -1
- schemathesis/specs/openapi/_hypothesis.py +555 -318
- schemathesis/specs/openapi/adapter/__init__.py +10 -0
- schemathesis/specs/openapi/adapter/parameters.py +729 -0
- schemathesis/specs/openapi/adapter/protocol.py +59 -0
- schemathesis/specs/openapi/adapter/references.py +19 -0
- schemathesis/specs/openapi/adapter/responses.py +368 -0
- schemathesis/specs/openapi/adapter/security.py +144 -0
- schemathesis/specs/openapi/adapter/v2.py +30 -0
- schemathesis/specs/openapi/adapter/v3_0.py +30 -0
- schemathesis/specs/openapi/adapter/v3_1.py +30 -0
- schemathesis/specs/openapi/analysis.py +96 -0
- schemathesis/specs/openapi/checks.py +748 -82
- schemathesis/specs/openapi/converter.py +176 -37
- schemathesis/specs/openapi/definitions.py +599 -4
- schemathesis/specs/openapi/examples.py +581 -165
- schemathesis/specs/openapi/expressions/__init__.py +52 -5
- schemathesis/specs/openapi/expressions/extractors.py +25 -0
- schemathesis/specs/openapi/expressions/lexer.py +34 -31
- schemathesis/specs/openapi/expressions/nodes.py +97 -46
- schemathesis/specs/openapi/expressions/parser.py +35 -13
- schemathesis/specs/openapi/formats.py +122 -0
- schemathesis/specs/openapi/media_types.py +75 -0
- schemathesis/specs/openapi/negative/__init__.py +93 -73
- schemathesis/specs/openapi/negative/mutations.py +294 -103
- schemathesis/specs/openapi/negative/utils.py +0 -9
- schemathesis/specs/openapi/patterns.py +458 -0
- schemathesis/specs/openapi/references.py +60 -81
- schemathesis/specs/openapi/schemas.py +647 -666
- schemathesis/specs/openapi/serialization.py +53 -30
- schemathesis/specs/openapi/stateful/__init__.py +403 -68
- schemathesis/specs/openapi/stateful/control.py +87 -0
- schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
- schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
- schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
- schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
- schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
- schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
- schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
- schemathesis/specs/openapi/stateful/inference.py +254 -0
- schemathesis/specs/openapi/stateful/links.py +219 -78
- schemathesis/specs/openapi/types/__init__.py +3 -0
- schemathesis/specs/openapi/types/common.py +23 -0
- schemathesis/specs/openapi/types/v2.py +129 -0
- schemathesis/specs/openapi/types/v3.py +134 -0
- schemathesis/specs/openapi/utils.py +7 -6
- schemathesis/specs/openapi/warnings.py +75 -0
- schemathesis/transport/__init__.py +224 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +126 -0
- schemathesis/transport/requests.py +278 -0
- schemathesis/transport/serialization.py +329 -0
- schemathesis/transport/wsgi.py +175 -0
- schemathesis-4.4.2.dist-info/METADATA +213 -0
- schemathesis-4.4.2.dist-info/RECORD +192 -0
- {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
- schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
- {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
- schemathesis/_compat.py +0 -57
- schemathesis/_hypothesis.py +0 -123
- schemathesis/auth.py +0 -214
- schemathesis/cli/callbacks.py +0 -240
- schemathesis/cli/cassettes.py +0 -351
- schemathesis/cli/context.py +0 -38
- schemathesis/cli/debug.py +0 -21
- schemathesis/cli/handlers.py +0 -11
- schemathesis/cli/junitxml.py +0 -41
- schemathesis/cli/options.py +0 -70
- schemathesis/cli/output/__init__.py +0 -1
- schemathesis/cli/output/default.py +0 -521
- schemathesis/cli/output/short.py +0 -40
- schemathesis/constants.py +0 -88
- schemathesis/exceptions.py +0 -257
- schemathesis/extra/_aiohttp.py +0 -27
- schemathesis/extra/_flask.py +0 -10
- schemathesis/extra/_server.py +0 -16
- schemathesis/extra/pytest_plugin.py +0 -251
- schemathesis/failures.py +0 -145
- schemathesis/fixups/__init__.py +0 -29
- schemathesis/fixups/fast_api.py +0 -30
- schemathesis/graphql.py +0 -5
- schemathesis/internal.py +0 -6
- schemathesis/lazy.py +0 -301
- schemathesis/models.py +0 -1113
- schemathesis/parameters.py +0 -91
- schemathesis/runner/__init__.py +0 -470
- schemathesis/runner/events.py +0 -242
- schemathesis/runner/impl/__init__.py +0 -3
- schemathesis/runner/impl/core.py +0 -791
- schemathesis/runner/impl/solo.py +0 -85
- schemathesis/runner/impl/threadpool.py +0 -367
- schemathesis/runner/serialization.py +0 -206
- schemathesis/serializers.py +0 -253
- schemathesis/service/__init__.py +0 -18
- schemathesis/service/auth.py +0 -10
- schemathesis/service/client.py +0 -62
- schemathesis/service/constants.py +0 -25
- schemathesis/service/events.py +0 -39
- schemathesis/service/handler.py +0 -46
- schemathesis/service/hosts.py +0 -74
- schemathesis/service/metadata.py +0 -42
- schemathesis/service/models.py +0 -21
- schemathesis/service/serialization.py +0 -184
- schemathesis/service/worker.py +0 -39
- schemathesis/specs/graphql/loaders.py +0 -215
- schemathesis/specs/openapi/constants.py +0 -7
- schemathesis/specs/openapi/expressions/context.py +0 -12
- schemathesis/specs/openapi/expressions/pointers.py +0 -29
- schemathesis/specs/openapi/filters.py +0 -44
- schemathesis/specs/openapi/links.py +0 -303
- schemathesis/specs/openapi/loaders.py +0 -453
- schemathesis/specs/openapi/parameters.py +0 -430
- schemathesis/specs/openapi/security.py +0 -129
- schemathesis/specs/openapi/validation.py +0 -24
- schemathesis/stateful.py +0 -358
- schemathesis/targets.py +0 -32
- schemathesis/types.py +0 -38
- schemathesis/utils.py +0 -475
- schemathesis-3.15.4.dist-info/METADATA +0 -202
- schemathesis-3.15.4.dist-info/RECORD +0 -99
- schemathesis-3.15.4.dist-info/entry_points.txt +0 -7
- /schemathesis/{extra → cli/ext}/__init__.py +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Collection
|
|
4
|
+
|
|
5
|
+
from schemathesis.transport import SerializationContext
|
|
6
|
+
from schemathesis.transport.asgi import ASGI_TRANSPORT
|
|
7
|
+
from schemathesis.transport.requests import REQUESTS_TRANSPORT
|
|
8
|
+
from schemathesis.transport.wsgi import WSGI_TRANSPORT
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from hypothesis import strategies as st
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
MEDIA_TYPES: dict[str, st.SearchStrategy[bytes]] = {}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_media_type(name: str, strategy: st.SearchStrategy[bytes], *, aliases: Collection[str] = ()) -> None:
|
|
18
|
+
r"""Register a custom Hypothesis strategy for generating media type content.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
name: Media type name that matches your OpenAPI requestBody content type
|
|
22
|
+
strategy: Hypothesis strategy that generates bytes for this media type
|
|
23
|
+
aliases: Additional media type names that use the same strategy
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
import schemathesis
|
|
28
|
+
from hypothesis import strategies as st
|
|
29
|
+
|
|
30
|
+
# Register PDF file strategy
|
|
31
|
+
pdf_strategy = st.sampled_from([
|
|
32
|
+
b"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n>>\nendobj\n%%EOF",
|
|
33
|
+
b"%PDF-1.5\n%\xe2\xe3\xcf\xd3\n1 0 obj\n<<\n/Type /Catalog\n>>\nendobj\n%%EOF"
|
|
34
|
+
])
|
|
35
|
+
schemathesis.openapi.media_type("application/pdf", pdf_strategy)
|
|
36
|
+
|
|
37
|
+
# Dynamic content generation
|
|
38
|
+
@st.composite
|
|
39
|
+
def xml_content(draw):
|
|
40
|
+
tag = draw(st.text(min_size=3, max_size=10))
|
|
41
|
+
content = draw(st.text(min_size=1, max_size=50))
|
|
42
|
+
return f"<?xml version='1.0'?><{tag}>{content}</{tag}>".encode()
|
|
43
|
+
|
|
44
|
+
schemathesis.openapi.media_type("application/xml", xml_content())
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Schema usage:
|
|
48
|
+
```yaml
|
|
49
|
+
requestBody:
|
|
50
|
+
content:
|
|
51
|
+
application/pdf: # Uses your PDF strategy
|
|
52
|
+
schema:
|
|
53
|
+
type: string
|
|
54
|
+
format: binary
|
|
55
|
+
application/xml: # Uses your XML strategy
|
|
56
|
+
schema:
|
|
57
|
+
type: string
|
|
58
|
+
format: binary
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
@REQUESTS_TRANSPORT.serializer(name, *aliases)
|
|
64
|
+
@ASGI_TRANSPORT.serializer(name, *aliases)
|
|
65
|
+
@WSGI_TRANSPORT.serializer(name, *aliases)
|
|
66
|
+
def serialize(ctx: SerializationContext, value: Any) -> dict[str, Any]:
|
|
67
|
+
return {"data": value}
|
|
68
|
+
|
|
69
|
+
MEDIA_TYPES[name] = strategy
|
|
70
|
+
for alias in aliases:
|
|
71
|
+
MEDIA_TYPES[alias] = strategy
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def unregister_all() -> None:
|
|
75
|
+
MEDIA_TYPES.clear()
|
|
@@ -1,86 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
1
4
|
from functools import lru_cache
|
|
2
|
-
from typing import
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
3
6
|
from urllib.parse import urlencode
|
|
4
7
|
|
|
5
|
-
import attr
|
|
6
8
|
import jsonschema
|
|
7
9
|
from hypothesis import strategies as st
|
|
8
10
|
from hypothesis_jsonschema import from_schema
|
|
9
11
|
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
+
from schemathesis.config import GenerationConfig
|
|
13
|
+
from schemathesis.core.jsonschema import ALL_KEYWORDS
|
|
14
|
+
from schemathesis.core.jsonschema.types import JsonSchema
|
|
15
|
+
from schemathesis.core.parameters import ParameterLocation
|
|
16
|
+
|
|
17
|
+
from .mutations import MutationContext, MutationMetadata
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from .types import Draw, Schema
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class GeneratedValue:
|
|
25
|
+
"""Wrapper for generated values with optional mutation metadata.
|
|
26
|
+
|
|
27
|
+
This allows us to pass both the value and metadata through the generation pipeline
|
|
28
|
+
without using tuples, making the code cleaner and type-safe.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
value: Any
|
|
32
|
+
meta: MutationMetadata | None
|
|
12
33
|
|
|
34
|
+
__slots__ = ("value", "meta")
|
|
13
35
|
|
|
14
|
-
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
15
38
|
class CacheKey:
|
|
16
39
|
"""A cache key for API Operation / location.
|
|
17
40
|
|
|
18
41
|
Carries the schema around but don't use it for hashing to simplify LRU cache usage.
|
|
19
42
|
"""
|
|
20
43
|
|
|
21
|
-
operation_name: str
|
|
22
|
-
location: str
|
|
23
|
-
schema:
|
|
44
|
+
operation_name: str
|
|
45
|
+
location: str
|
|
46
|
+
schema: JsonSchema
|
|
47
|
+
validator_cls: type[jsonschema.Validator]
|
|
48
|
+
|
|
49
|
+
__slots__ = ("operation_name", "location", "schema", "validator_cls")
|
|
24
50
|
|
|
25
51
|
def __hash__(self) -> int:
|
|
26
52
|
return hash((self.operation_name, self.location))
|
|
27
53
|
|
|
28
54
|
|
|
29
|
-
@lru_cache
|
|
30
|
-
def get_validator(cache_key: CacheKey) -> jsonschema.
|
|
55
|
+
@lru_cache
|
|
56
|
+
def get_validator(cache_key: CacheKey) -> jsonschema.Validator:
|
|
31
57
|
"""Get JSON Schema validator for the given schema."""
|
|
32
58
|
# Each operation / location combo has only a single schema, therefore could be cached
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"additionalProperties",
|
|
39
|
-
"allOf",
|
|
40
|
-
"anyOf",
|
|
41
|
-
"const",
|
|
42
|
-
"contains",
|
|
43
|
-
"contentEncoding",
|
|
44
|
-
"contentMediaType",
|
|
45
|
-
"dependencies",
|
|
46
|
-
"enum",
|
|
47
|
-
"else",
|
|
48
|
-
"exclusiveMaximum",
|
|
49
|
-
"exclusiveMinimum",
|
|
50
|
-
"format",
|
|
51
|
-
"if",
|
|
52
|
-
"items",
|
|
53
|
-
"maxItems",
|
|
54
|
-
"maxLength",
|
|
55
|
-
"maxProperties",
|
|
56
|
-
"maximum",
|
|
57
|
-
"minItems",
|
|
58
|
-
"minLength",
|
|
59
|
-
"minProperties",
|
|
60
|
-
"minimum",
|
|
61
|
-
"multipleOf",
|
|
62
|
-
"not",
|
|
63
|
-
"oneOf",
|
|
64
|
-
"pattern",
|
|
65
|
-
"patternProperties",
|
|
66
|
-
"properties",
|
|
67
|
-
"propertyNames",
|
|
68
|
-
"$ref",
|
|
69
|
-
"required",
|
|
70
|
-
"then",
|
|
71
|
-
"type",
|
|
72
|
-
"uniqueItems",
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@lru_cache()
|
|
77
|
-
def split_schema(cache_key: CacheKey) -> Tuple[Schema, Schema]:
|
|
59
|
+
return cache_key.validator_cls(cache_key.schema)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@lru_cache
|
|
63
|
+
def split_schema(cache_key: CacheKey) -> tuple[Schema, Schema]:
|
|
78
64
|
"""Split the schema in two parts.
|
|
79
65
|
|
|
80
66
|
The first one contains only validation JSON Schema keywords, the second one everything else.
|
|
81
67
|
"""
|
|
82
68
|
keywords, non_keywords = {}, {}
|
|
83
|
-
|
|
69
|
+
schema = {} if isinstance(cache_key.schema, bool) else cache_key.schema
|
|
70
|
+
for keyword, value in schema.items():
|
|
84
71
|
if keyword in ALL_KEYWORDS:
|
|
85
72
|
keywords[keyword] = value
|
|
86
73
|
else:
|
|
@@ -89,39 +76,60 @@ def split_schema(cache_key: CacheKey) -> Tuple[Schema, Schema]:
|
|
|
89
76
|
|
|
90
77
|
|
|
91
78
|
def negative_schema(
|
|
92
|
-
schema:
|
|
79
|
+
schema: JsonSchema,
|
|
93
80
|
operation_name: str,
|
|
94
|
-
location:
|
|
95
|
-
media_type:
|
|
81
|
+
location: ParameterLocation,
|
|
82
|
+
media_type: str | None,
|
|
83
|
+
generation_config: GenerationConfig,
|
|
96
84
|
*,
|
|
97
|
-
custom_formats:
|
|
85
|
+
custom_formats: dict[str, st.SearchStrategy[str]],
|
|
86
|
+
validator_cls: type[jsonschema.Validator],
|
|
98
87
|
) -> st.SearchStrategy:
|
|
99
88
|
"""A strategy for instances that DO NOT match the input schema.
|
|
100
89
|
|
|
101
90
|
It is used to cover the input space that is not possible to cover with the "positive" strategy.
|
|
91
|
+
|
|
92
|
+
Returns a strategy that produces GeneratedValue instances with mutation metadata.
|
|
102
93
|
"""
|
|
103
94
|
# The mutated schema is passed to `from_schema` and guarded against producing instances valid against
|
|
104
95
|
# the original schema.
|
|
105
|
-
cache_key = CacheKey(operation_name, location, schema)
|
|
96
|
+
cache_key = CacheKey(operation_name, location, schema, validator_cls)
|
|
106
97
|
validator = get_validator(cache_key)
|
|
107
98
|
keywords, non_keywords = split_schema(cache_key)
|
|
108
99
|
|
|
109
|
-
if location ==
|
|
100
|
+
if location == ParameterLocation.QUERY:
|
|
110
101
|
|
|
111
|
-
def filter_values(value:
|
|
102
|
+
def filter_values(value: dict[str, Any]) -> bool:
|
|
112
103
|
return is_non_empty_query(value) and not validator.is_valid(value)
|
|
113
104
|
|
|
114
105
|
else:
|
|
115
106
|
|
|
116
|
-
def filter_values(value:
|
|
107
|
+
def filter_values(value: dict[str, Any]) -> bool:
|
|
117
108
|
return not validator.is_valid(value)
|
|
118
109
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
110
|
+
def generate_value_with_metadata(value: tuple[dict, MutationMetadata]) -> st.SearchStrategy:
|
|
111
|
+
schema, metadata = value
|
|
112
|
+
return (
|
|
113
|
+
from_schema(
|
|
114
|
+
schema,
|
|
115
|
+
custom_formats=custom_formats,
|
|
116
|
+
allow_x00=generation_config.allow_x00,
|
|
117
|
+
codec=generation_config.codec,
|
|
118
|
+
)
|
|
119
|
+
.filter(filter_values)
|
|
120
|
+
.map(lambda value: GeneratedValue(value, metadata))
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return mutated(
|
|
124
|
+
keywords=keywords,
|
|
125
|
+
non_keywords=non_keywords,
|
|
126
|
+
location=location,
|
|
127
|
+
media_type=media_type,
|
|
128
|
+
allow_extra_parameters=generation_config.allow_extra_parameters,
|
|
129
|
+
).flatmap(generate_value_with_metadata)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def is_non_empty_query(query: dict[str, Any]) -> bool:
|
|
125
133
|
# Whether this query parameters will be encoded to a non-empty query string
|
|
126
134
|
result = []
|
|
127
135
|
for key, values in query.items():
|
|
@@ -138,8 +146,20 @@ def is_non_empty_query(query: Dict[str, Any]) -> bool:
|
|
|
138
146
|
return urlencode(result, doseq=True) != ""
|
|
139
147
|
|
|
140
148
|
|
|
141
|
-
@st.composite # type: ignore
|
|
142
|
-
def mutated(
|
|
149
|
+
@st.composite # type: ignore[misc]
|
|
150
|
+
def mutated(
|
|
151
|
+
draw: Draw,
|
|
152
|
+
*,
|
|
153
|
+
keywords: Schema,
|
|
154
|
+
non_keywords: Schema,
|
|
155
|
+
location: ParameterLocation,
|
|
156
|
+
media_type: str | None,
|
|
157
|
+
allow_extra_parameters: bool,
|
|
158
|
+
) -> Any:
|
|
143
159
|
return MutationContext(
|
|
144
|
-
keywords=keywords,
|
|
160
|
+
keywords=keywords,
|
|
161
|
+
non_keywords=non_keywords,
|
|
162
|
+
location=location,
|
|
163
|
+
media_type=media_type,
|
|
164
|
+
allow_extra_parameters=allow_extra_parameters,
|
|
145
165
|
).mutate(draw)
|