schemathesis 3.35.0__py3-none-any.whl → 3.35.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/_hypothesis.py +35 -11
- schemathesis/cli/__init__.py +352 -357
- schemathesis/cli/cassettes.py +2 -0
- schemathesis/cli/context.py +7 -3
- schemathesis/cli/output/default.py +13 -2
- schemathesis/generation/coverage.py +170 -100
- schemathesis/models.py +14 -1
- schemathesis/runner/events.py +3 -1
- schemathesis/runner/serialization.py +3 -1
- schemathesis/schemas.py +3 -9
- schemathesis/specs/graphql/loaders.py +2 -1
- schemathesis/specs/openapi/_hypothesis.py +3 -1
- schemathesis/specs/openapi/examples.py +3 -1
- schemathesis/specs/openapi/loaders.py +3 -1
- schemathesis/specs/openapi/parameters.py +2 -0
- schemathesis/stateful/context.py +3 -0
- schemathesis/stateful/runner.py +8 -1
- schemathesis/types.py +8 -0
- {schemathesis-3.35.0.dist-info → schemathesis-3.35.2.dist-info}/METADATA +69 -168
- {schemathesis-3.35.0.dist-info → schemathesis-3.35.2.dist-info}/RECORD +23 -23
- {schemathesis-3.35.0.dist-info → schemathesis-3.35.2.dist-info}/WHEEL +0 -0
- {schemathesis-3.35.0.dist-info → schemathesis-3.35.2.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.35.0.dist-info → schemathesis-3.35.2.dist-info}/licenses/LICENSE +0 -0
schemathesis/_hypothesis.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import json
|
|
6
7
|
import warnings
|
|
7
8
|
from typing import Any, Callable, Generator, Mapping, Optional, Tuple
|
|
8
9
|
|
|
@@ -19,9 +20,10 @@ from .exceptions import OperationSchemaError, SerializationNotPossible
|
|
|
19
20
|
from .experimental import COVERAGE_PHASE
|
|
20
21
|
from .generation import DataGenerationMethod, GenerationConfig, combine_strategies, coverage, get_single_example
|
|
21
22
|
from .hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher
|
|
22
|
-
from .models import APIOperation, Case
|
|
23
|
+
from .models import APIOperation, Case, GenerationMetadata, TestPhase
|
|
23
24
|
from .transports.content_types import parse_content_type
|
|
24
25
|
from .transports.headers import has_invalid_characters, is_latin_1_encodable
|
|
26
|
+
from .types import NotSet
|
|
25
27
|
from .utils import GivenInput
|
|
26
28
|
|
|
27
29
|
# Forcefully initializes Hypothesis' global PRNG to avoid races that initilize it
|
|
@@ -215,39 +217,61 @@ def _iter_coverage_cases(
|
|
|
215
217
|
from .specs.openapi.constants import LOCATION_TO_CONTAINER
|
|
216
218
|
|
|
217
219
|
ctx = coverage.CoverageContext(data_generation_methods=data_generation_methods)
|
|
218
|
-
|
|
220
|
+
meta = GenerationMetadata(
|
|
221
|
+
query=None, path_parameters=None, headers=None, cookies=None, body=None, phase=TestPhase.COVERAGE
|
|
222
|
+
)
|
|
223
|
+
generators: dict[tuple[str, str], Generator[coverage.GeneratedValue, None, None]] = {}
|
|
219
224
|
template: dict[str, Any] = {}
|
|
220
225
|
for parameter in operation.iter_parameters():
|
|
221
226
|
schema = parameter.as_json_schema(operation)
|
|
222
227
|
gen = coverage.cover_schema_iter(ctx, schema)
|
|
223
228
|
value = next(gen, NOT_SET)
|
|
224
|
-
if value
|
|
229
|
+
if isinstance(value, NotSet):
|
|
225
230
|
continue
|
|
226
231
|
location = parameter.location
|
|
227
232
|
name = parameter.name
|
|
228
233
|
container = template.setdefault(LOCATION_TO_CONTAINER[location], {})
|
|
229
|
-
|
|
234
|
+
if location in ("header", "cookie") and not isinstance(value.value, str):
|
|
235
|
+
container[name] = json.dumps(value.value)
|
|
236
|
+
else:
|
|
237
|
+
container[name] = value.value
|
|
230
238
|
generators[(location, name)] = gen
|
|
231
239
|
if operation.body:
|
|
232
240
|
for body in operation.body:
|
|
233
241
|
schema = body.as_json_schema(operation)
|
|
234
242
|
gen = coverage.cover_schema_iter(ctx, schema)
|
|
235
243
|
value = next(gen, NOT_SET)
|
|
236
|
-
if value
|
|
244
|
+
if isinstance(value, NotSet):
|
|
237
245
|
continue
|
|
238
246
|
if "body" not in template:
|
|
239
|
-
template["body"] = value
|
|
247
|
+
template["body"] = value.value
|
|
240
248
|
template["media_type"] = body.media_type
|
|
241
|
-
|
|
249
|
+
case = operation.make_case(**{**template, "body": value.value, "media_type": body.media_type})
|
|
250
|
+
case.data_generation_method = value.data_generation_method
|
|
251
|
+
case.meta = meta
|
|
252
|
+
yield case
|
|
242
253
|
for next_value in gen:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
case = operation.make_case(**{**template, "body": next_value.value, "media_type": body.media_type})
|
|
255
|
+
case.data_generation_method = next_value.data_generation_method
|
|
256
|
+
case.meta = meta
|
|
257
|
+
yield case
|
|
258
|
+
elif DataGenerationMethod.positive in data_generation_methods:
|
|
259
|
+
case = operation.make_case(**template)
|
|
260
|
+
case.data_generation_method = DataGenerationMethod.positive
|
|
261
|
+
case.meta = meta
|
|
262
|
+
yield case
|
|
246
263
|
for (location, name), gen in generators.items():
|
|
247
264
|
container_name = LOCATION_TO_CONTAINER[location]
|
|
248
265
|
container = template[container_name]
|
|
249
266
|
for value in gen:
|
|
250
|
-
|
|
267
|
+
if location in ("header", "cookie") and not isinstance(value.value, str):
|
|
268
|
+
generated = json.dumps(value.value)
|
|
269
|
+
else:
|
|
270
|
+
generated = value.value
|
|
271
|
+
case = operation.make_case(**{**template, container_name: {**container, name: generated}})
|
|
272
|
+
case.data_generation_method = value.data_generation_method
|
|
273
|
+
case.meta = meta
|
|
274
|
+
yield case
|
|
251
275
|
|
|
252
276
|
|
|
253
277
|
def find_invalid_headers(headers: Mapping) -> Generator[Tuple[str, str], None, None]:
|