schemathesis 4.3.16__py3-none-any.whl → 4.3.17__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.
Potentially problematic release.
This version of schemathesis might be problematic. Click here for more details.
- schemathesis/auths.py +24 -3
- schemathesis/cli/commands/run/handlers/output.py +5 -2
- schemathesis/core/errors.py +21 -0
- schemathesis/engine/errors.py +12 -0
- schemathesis/engine/phases/unit/__init__.py +2 -2
- schemathesis/engine/phases/unit/_executor.py +4 -0
- schemathesis/generation/coverage.py +143 -50
- schemathesis/generation/hypothesis/builder.py +28 -7
- schemathesis/generation/meta.py +77 -2
- schemathesis/pytest/lazy.py +58 -12
- schemathesis/pytest/plugin.py +2 -2
- schemathesis/specs/openapi/_hypothesis.py +18 -98
- schemathesis/specs/openapi/adapter/parameters.py +181 -11
- schemathesis/specs/openapi/checks.py +5 -7
- schemathesis/specs/openapi/schemas.py +5 -4
- schemathesis/transport/prepare.py +4 -3
- {schemathesis-4.3.16.dist-info → schemathesis-4.3.17.dist-info}/METADATA +6 -5
- {schemathesis-4.3.16.dist-info → schemathesis-4.3.17.dist-info}/RECORD +21 -21
- {schemathesis-4.3.16.dist-info → schemathesis-4.3.17.dist-info}/WHEEL +0 -0
- {schemathesis-4.3.16.dist-info → schemathesis-4.3.17.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.3.16.dist-info → schemathesis-4.3.17.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from itertools import chain
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Iterable, Iterator, Mapping, Sequence, cast
|
|
7
7
|
|
|
8
|
+
from schemathesis.config import GenerationConfig
|
|
8
9
|
from schemathesis.core import NOT_SET, NotSet
|
|
9
10
|
from schemathesis.core.adapter import OperationParameter
|
|
10
11
|
from schemathesis.core.errors import InvalidSchema
|
|
@@ -13,12 +14,16 @@ from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY
|
|
|
13
14
|
from schemathesis.core.jsonschema.types import JsonSchema, JsonSchemaObject
|
|
14
15
|
from schemathesis.core.parameters import HEADER_LOCATIONS, ParameterLocation
|
|
15
16
|
from schemathesis.core.validation import check_header_name
|
|
16
|
-
from schemathesis.
|
|
17
|
+
from schemathesis.generation.modes import GenerationMode
|
|
18
|
+
from schemathesis.schemas import APIOperation, ParameterSet
|
|
17
19
|
from schemathesis.specs.openapi.adapter.protocol import SpecificationAdapter
|
|
18
20
|
from schemathesis.specs.openapi.adapter.references import maybe_resolve
|
|
19
21
|
from schemathesis.specs.openapi.converter import to_json_schema
|
|
22
|
+
from schemathesis.specs.openapi.formats import HEADER_FORMAT
|
|
20
23
|
|
|
21
24
|
if TYPE_CHECKING:
|
|
25
|
+
from hypothesis import strategies as st
|
|
26
|
+
|
|
22
27
|
from schemathesis.core.compat import RefResolver
|
|
23
28
|
|
|
24
29
|
|
|
@@ -191,6 +196,8 @@ class OpenApiBody(OpenApiComponent):
|
|
|
191
196
|
"_unoptimized_schema",
|
|
192
197
|
"_raw_schema",
|
|
193
198
|
"_examples",
|
|
199
|
+
"_positive_strategy_cache",
|
|
200
|
+
"_negative_strategy_cache",
|
|
194
201
|
)
|
|
195
202
|
|
|
196
203
|
@classmethod
|
|
@@ -231,6 +238,11 @@ class OpenApiBody(OpenApiComponent):
|
|
|
231
238
|
adapter=adapter,
|
|
232
239
|
)
|
|
233
240
|
|
|
241
|
+
def __post_init__(self) -> None:
|
|
242
|
+
super().__post_init__()
|
|
243
|
+
self._positive_strategy_cache: st.SearchStrategy | NotSet = NOT_SET
|
|
244
|
+
self._negative_strategy_cache: st.SearchStrategy | NotSet = NOT_SET
|
|
245
|
+
|
|
234
246
|
@property
|
|
235
247
|
def location(self) -> ParameterLocation:
|
|
236
248
|
return ParameterLocation.BODY
|
|
@@ -248,6 +260,47 @@ class OpenApiBody(OpenApiComponent):
|
|
|
248
260
|
"""Return default type if body is a form type."""
|
|
249
261
|
return "object" if self.media_type in FORM_MEDIA_TYPES else None
|
|
250
262
|
|
|
263
|
+
def get_strategy(
|
|
264
|
+
self,
|
|
265
|
+
operation: APIOperation,
|
|
266
|
+
generation_config: GenerationConfig,
|
|
267
|
+
generation_mode: GenerationMode,
|
|
268
|
+
) -> st.SearchStrategy:
|
|
269
|
+
"""Get a Hypothesis strategy for this body parameter."""
|
|
270
|
+
# Check cache based on generation mode
|
|
271
|
+
if generation_mode == GenerationMode.POSITIVE:
|
|
272
|
+
if self._positive_strategy_cache is not NOT_SET:
|
|
273
|
+
assert not isinstance(self._positive_strategy_cache, NotSet)
|
|
274
|
+
return self._positive_strategy_cache
|
|
275
|
+
elif self._negative_strategy_cache is not NOT_SET:
|
|
276
|
+
assert not isinstance(self._negative_strategy_cache, NotSet)
|
|
277
|
+
return self._negative_strategy_cache
|
|
278
|
+
|
|
279
|
+
# Import here to avoid circular dependency
|
|
280
|
+
from schemathesis.specs.openapi._hypothesis import GENERATOR_MODE_TO_STRATEGY_FACTORY
|
|
281
|
+
from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
|
|
282
|
+
|
|
283
|
+
# Build the strategy
|
|
284
|
+
strategy_factory = GENERATOR_MODE_TO_STRATEGY_FACTORY[generation_mode]
|
|
285
|
+
schema = self.optimized_schema
|
|
286
|
+
assert isinstance(operation.schema, BaseOpenAPISchema)
|
|
287
|
+
strategy = strategy_factory(
|
|
288
|
+
schema,
|
|
289
|
+
operation.label,
|
|
290
|
+
ParameterLocation.BODY,
|
|
291
|
+
self.media_type,
|
|
292
|
+
generation_config,
|
|
293
|
+
operation.schema.adapter.jsonschema_validator_cls,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Cache the strategy
|
|
297
|
+
if generation_mode == GenerationMode.POSITIVE:
|
|
298
|
+
self._positive_strategy_cache = strategy
|
|
299
|
+
else:
|
|
300
|
+
self._negative_strategy_cache = strategy
|
|
301
|
+
|
|
302
|
+
return strategy
|
|
303
|
+
|
|
251
304
|
|
|
252
305
|
OPENAPI_20_EXCLUDE_KEYS = frozenset(["required", "name", "in", "title", "description"])
|
|
253
306
|
|
|
@@ -461,20 +514,123 @@ def build_path_parameter_v3_1(kwargs: Mapping[str, Any]) -> OpenApiParameter:
|
|
|
461
514
|
@dataclass
|
|
462
515
|
class OpenApiParameterSet(ParameterSet):
|
|
463
516
|
items: list[OpenApiParameter]
|
|
517
|
+
location: ParameterLocation
|
|
464
518
|
|
|
465
|
-
__slots__ = ("items", "_schema")
|
|
519
|
+
__slots__ = ("items", "location", "_schema", "_schema_cache", "_strategy_cache")
|
|
466
520
|
|
|
467
|
-
def __init__(self, items: list[OpenApiParameter] | None = None) -> None:
|
|
521
|
+
def __init__(self, location: ParameterLocation, items: list[OpenApiParameter] | None = None) -> None:
|
|
522
|
+
self.location = location
|
|
468
523
|
self.items = items or []
|
|
469
524
|
self._schema: dict | NotSet = NOT_SET
|
|
525
|
+
self._schema_cache: dict[frozenset[str], dict[str, Any]] = {}
|
|
526
|
+
self._strategy_cache: dict[tuple[frozenset[str], GenerationMode], st.SearchStrategy] = {}
|
|
470
527
|
|
|
471
528
|
@property
|
|
472
529
|
def schema(self) -> dict[str, Any]:
|
|
473
530
|
if self._schema is NOT_SET:
|
|
474
|
-
self._schema = parameters_to_json_schema(self.items)
|
|
531
|
+
self._schema = parameters_to_json_schema(self.items, self.location)
|
|
475
532
|
assert not isinstance(self._schema, NotSet)
|
|
476
533
|
return self._schema
|
|
477
534
|
|
|
535
|
+
def get_schema_with_exclusions(self, exclude: Iterable[str]) -> dict[str, Any]:
|
|
536
|
+
"""Get cached schema with specified parameters excluded."""
|
|
537
|
+
exclude_key = frozenset(exclude)
|
|
538
|
+
|
|
539
|
+
if exclude_key in self._schema_cache:
|
|
540
|
+
return self._schema_cache[exclude_key]
|
|
541
|
+
|
|
542
|
+
schema = self.schema
|
|
543
|
+
if exclude_key:
|
|
544
|
+
# Need to exclude some parameters - create a shallow copy to avoid mutating cached schema
|
|
545
|
+
schema = dict(schema)
|
|
546
|
+
if self.location == ParameterLocation.HEADER:
|
|
547
|
+
# Remove excluded headers case-insensitively
|
|
548
|
+
exclude_lower = {name.lower() for name in exclude_key}
|
|
549
|
+
schema["properties"] = {
|
|
550
|
+
key: value for key, value in schema["properties"].items() if key.lower() not in exclude_lower
|
|
551
|
+
}
|
|
552
|
+
if "required" in schema:
|
|
553
|
+
schema["required"] = [key for key in schema["required"] if key.lower() not in exclude_lower]
|
|
554
|
+
else:
|
|
555
|
+
# Non-header locations: remove by exact name
|
|
556
|
+
schema["properties"] = {
|
|
557
|
+
key: value for key, value in schema["properties"].items() if key not in exclude_key
|
|
558
|
+
}
|
|
559
|
+
if "required" in schema:
|
|
560
|
+
schema["required"] = [key for key in schema["required"] if key not in exclude_key]
|
|
561
|
+
|
|
562
|
+
self._schema_cache[exclude_key] = schema
|
|
563
|
+
return schema
|
|
564
|
+
|
|
565
|
+
def get_strategy(
|
|
566
|
+
self,
|
|
567
|
+
operation: APIOperation,
|
|
568
|
+
generation_config: GenerationConfig,
|
|
569
|
+
generation_mode: GenerationMode,
|
|
570
|
+
exclude: Iterable[str] = (),
|
|
571
|
+
) -> st.SearchStrategy:
|
|
572
|
+
"""Get a Hypothesis strategy for this parameter set with specified exclusions."""
|
|
573
|
+
exclude_key = frozenset(exclude)
|
|
574
|
+
cache_key = (exclude_key, generation_mode)
|
|
575
|
+
|
|
576
|
+
if cache_key in self._strategy_cache:
|
|
577
|
+
return self._strategy_cache[cache_key]
|
|
578
|
+
|
|
579
|
+
# Import here to avoid circular dependency
|
|
580
|
+
from hypothesis import strategies as st
|
|
581
|
+
|
|
582
|
+
from schemathesis.openapi.generation.filters import is_valid_header, is_valid_path, is_valid_query
|
|
583
|
+
from schemathesis.specs.openapi._hypothesis import (
|
|
584
|
+
GENERATOR_MODE_TO_STRATEGY_FACTORY,
|
|
585
|
+
_can_skip_header_filter,
|
|
586
|
+
jsonify_python_specific_types,
|
|
587
|
+
make_negative_strategy,
|
|
588
|
+
quote_all,
|
|
589
|
+
)
|
|
590
|
+
from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
|
|
591
|
+
|
|
592
|
+
# Get schema with exclusions
|
|
593
|
+
schema = self.get_schema_with_exclusions(exclude)
|
|
594
|
+
|
|
595
|
+
strategy_factory = GENERATOR_MODE_TO_STRATEGY_FACTORY[generation_mode]
|
|
596
|
+
|
|
597
|
+
if not schema["properties"] and strategy_factory is make_negative_strategy:
|
|
598
|
+
# Nothing to negate - all properties were excluded
|
|
599
|
+
strategy = st.none()
|
|
600
|
+
else:
|
|
601
|
+
assert isinstance(operation.schema, BaseOpenAPISchema)
|
|
602
|
+
strategy = strategy_factory(
|
|
603
|
+
schema,
|
|
604
|
+
operation.label,
|
|
605
|
+
self.location,
|
|
606
|
+
None,
|
|
607
|
+
generation_config,
|
|
608
|
+
operation.schema.adapter.jsonschema_validator_cls,
|
|
609
|
+
)
|
|
610
|
+
serialize = operation.get_parameter_serializer(self.location)
|
|
611
|
+
if serialize is not None:
|
|
612
|
+
strategy = strategy.map(serialize)
|
|
613
|
+
filter_func = {
|
|
614
|
+
ParameterLocation.PATH: is_valid_path,
|
|
615
|
+
ParameterLocation.HEADER: is_valid_header,
|
|
616
|
+
ParameterLocation.COOKIE: is_valid_header,
|
|
617
|
+
ParameterLocation.QUERY: is_valid_query,
|
|
618
|
+
}[self.location]
|
|
619
|
+
# Headers with special format do not need filtration
|
|
620
|
+
if not (self.location.is_in_header and _can_skip_header_filter(schema)):
|
|
621
|
+
strategy = strategy.filter(filter_func)
|
|
622
|
+
# Path & query parameters will be cast to string anyway, but having their JSON equivalents for
|
|
623
|
+
# `True` / `False` / `None` improves chances of them passing validation in apps
|
|
624
|
+
# that expect boolean / null types
|
|
625
|
+
# and not aware of Python-specific representation of those types
|
|
626
|
+
if self.location == ParameterLocation.PATH:
|
|
627
|
+
strategy = strategy.map(quote_all).map(jsonify_python_specific_types)
|
|
628
|
+
elif self.location == ParameterLocation.QUERY:
|
|
629
|
+
strategy = strategy.map(jsonify_python_specific_types)
|
|
630
|
+
|
|
631
|
+
self._strategy_cache[cache_key] = strategy
|
|
632
|
+
return strategy
|
|
633
|
+
|
|
478
634
|
|
|
479
635
|
COMBINED_FORM_DATA_MARKER = "x-schemathesis-form-parameter"
|
|
480
636
|
|
|
@@ -485,19 +641,21 @@ def form_data_to_json_schema(parameters: Sequence[Mapping[str, Any]]) -> dict[st
|
|
|
485
641
|
(param["name"], extract_parameter_schema_v2(param), param.get("required", False)) for param in parameters
|
|
486
642
|
)
|
|
487
643
|
|
|
488
|
-
merged = _merge_parameters_to_object_schema(parameter_data)
|
|
644
|
+
merged = _merge_parameters_to_object_schema(parameter_data, ParameterLocation.BODY)
|
|
489
645
|
|
|
490
646
|
return {"schema": merged, COMBINED_FORM_DATA_MARKER: True}
|
|
491
647
|
|
|
492
648
|
|
|
493
|
-
def parameters_to_json_schema(parameters: Iterable[OpenApiParameter]) -> dict[str, Any]:
|
|
649
|
+
def parameters_to_json_schema(parameters: Iterable[OpenApiParameter], location: ParameterLocation) -> dict[str, Any]:
|
|
494
650
|
"""Convert multiple Open API parameters to a JSON Schema."""
|
|
495
651
|
parameter_data = ((param.name, param.optimized_schema, param.is_required) for param in parameters)
|
|
496
652
|
|
|
497
|
-
return _merge_parameters_to_object_schema(parameter_data)
|
|
653
|
+
return _merge_parameters_to_object_schema(parameter_data, location)
|
|
498
654
|
|
|
499
655
|
|
|
500
|
-
def _merge_parameters_to_object_schema(
|
|
656
|
+
def _merge_parameters_to_object_schema(
|
|
657
|
+
parameters: Iterable[tuple[str, Any, bool]], location: ParameterLocation
|
|
658
|
+
) -> dict[str, Any]:
|
|
501
659
|
"""Merge parameter data into a JSON Schema object."""
|
|
502
660
|
properties = {}
|
|
503
661
|
required = []
|
|
@@ -512,18 +670,30 @@ def _merge_parameters_to_object_schema(parameters: Iterable[tuple[str, Any, bool
|
|
|
512
670
|
# ensures unique names
|
|
513
671
|
bundled.update(subschema_bundle)
|
|
514
672
|
|
|
673
|
+
# Apply location-specific adjustments to individual parameter schemas
|
|
674
|
+
if isinstance(subschema, dict):
|
|
675
|
+
# Headers: add HEADER_FORMAT for plain string types
|
|
676
|
+
if location.is_in_header and list(subschema) == ["type"] and subschema["type"] == "string":
|
|
677
|
+
subschema = {**subschema, "format": HEADER_FORMAT}
|
|
678
|
+
|
|
679
|
+
# Path parameters: ensure string types have minLength >= 1
|
|
680
|
+
elif location == ParameterLocation.PATH and subschema.get("type") == "string":
|
|
681
|
+
if "minLength" not in subschema:
|
|
682
|
+
subschema = {**subschema, "minLength": 1}
|
|
683
|
+
|
|
515
684
|
properties[name] = subschema
|
|
516
685
|
|
|
517
|
-
#
|
|
518
|
-
if is_required and name not in required:
|
|
686
|
+
# Path parameters are always required
|
|
687
|
+
if (location == ParameterLocation.PATH or is_required) and name not in required:
|
|
519
688
|
required.append(name)
|
|
520
689
|
|
|
521
690
|
merged = {
|
|
522
691
|
"properties": properties,
|
|
523
692
|
"additionalProperties": False,
|
|
524
693
|
"type": "object",
|
|
525
|
-
"required": required,
|
|
526
694
|
}
|
|
695
|
+
if required:
|
|
696
|
+
merged["required"] = required
|
|
527
697
|
if bundled:
|
|
528
698
|
merged[BUNDLE_STORAGE_KEY] = bundled
|
|
529
699
|
|
|
@@ -14,7 +14,7 @@ from schemathesis.core.failures import Failure
|
|
|
14
14
|
from schemathesis.core.parameters import ParameterLocation
|
|
15
15
|
from schemathesis.core.transport import Response
|
|
16
16
|
from schemathesis.generation.case import Case
|
|
17
|
-
from schemathesis.generation.meta import CoveragePhaseData, TestPhase
|
|
17
|
+
from schemathesis.generation.meta import CoveragePhaseData, CoverageScenario, TestPhase
|
|
18
18
|
from schemathesis.openapi.checks import (
|
|
19
19
|
AcceptedNegativeData,
|
|
20
20
|
EnsureResourceAvailability,
|
|
@@ -44,7 +44,7 @@ def is_unexpected_http_status_case(case: Case) -> bool:
|
|
|
44
44
|
return bool(
|
|
45
45
|
case.meta
|
|
46
46
|
and isinstance(case.meta.phase.data, CoveragePhaseData)
|
|
47
|
-
and case.meta.phase.data.
|
|
47
|
+
and case.meta.phase.data.scenario == CoverageScenario.UNSPECIFIED_HTTP_METHOD
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
|
|
@@ -287,8 +287,7 @@ def missing_required_header(ctx: CheckContext, response: Response, case: Case) -
|
|
|
287
287
|
if (
|
|
288
288
|
data.parameter
|
|
289
289
|
and data.parameter_location == ParameterLocation.HEADER
|
|
290
|
-
and data.
|
|
291
|
-
and data.description.startswith("Missing ")
|
|
290
|
+
and data.scenario == CoverageScenario.MISSING_PARAMETER
|
|
292
291
|
):
|
|
293
292
|
if data.parameter.lower() == "authorization":
|
|
294
293
|
expected_statuses = {401}
|
|
@@ -313,7 +312,7 @@ def unsupported_method(ctx: CheckContext, response: Response, case: Case) -> boo
|
|
|
313
312
|
if meta is None or not isinstance(meta.phase.data, CoveragePhaseData) or response.request.method == "OPTIONS":
|
|
314
313
|
return None
|
|
315
314
|
data = meta.phase.data
|
|
316
|
-
if data.
|
|
315
|
+
if data.scenario == CoverageScenario.UNSPECIFIED_HTTP_METHOD:
|
|
317
316
|
if response.status_code != 405:
|
|
318
317
|
raise UnsupportedMethodResponse(
|
|
319
318
|
operation=case.operation.label,
|
|
@@ -340,7 +339,6 @@ def has_only_additional_properties_in_non_body_parameters(case: Case) -> bool:
|
|
|
340
339
|
# Check if the case contains only additional properties in query, headers, or cookies.
|
|
341
340
|
# This function is used to determine if negation is solely in the form of extra properties,
|
|
342
341
|
# which are often ignored for backward-compatibility by the tested apps
|
|
343
|
-
from ._hypothesis import get_schema_for_location
|
|
344
342
|
from .schemas import BaseOpenAPISchema
|
|
345
343
|
|
|
346
344
|
meta = case.meta
|
|
@@ -358,7 +356,7 @@ def has_only_additional_properties_in_non_body_parameters(case: Case) -> bool:
|
|
|
358
356
|
value = getattr(case, location.container_name)
|
|
359
357
|
if value is not None and meta_for_location is not None and meta_for_location.mode.is_negative:
|
|
360
358
|
container = getattr(case.operation, location.container_name)
|
|
361
|
-
schema =
|
|
359
|
+
schema = container.schema
|
|
362
360
|
|
|
363
361
|
if _has_serialization_sensitive_types(schema, container):
|
|
364
362
|
# Can't reliably determine if only additional properties were added
|
|
@@ -35,6 +35,7 @@ from schemathesis.core.errors import (
|
|
|
35
35
|
SchemaLocation,
|
|
36
36
|
)
|
|
37
37
|
from schemathesis.core.failures import Failure, FailureGroup, MalformedJson
|
|
38
|
+
from schemathesis.core.parameters import ParameterLocation
|
|
38
39
|
from schemathesis.core.result import Err, Ok, Result
|
|
39
40
|
from schemathesis.core.transport import Response
|
|
40
41
|
from schemathesis.generation.case import Case
|
|
@@ -389,10 +390,10 @@ class BaseOpenAPISchema(BaseSchema):
|
|
|
389
390
|
schema=self,
|
|
390
391
|
responses=responses,
|
|
391
392
|
security=security,
|
|
392
|
-
path_parameters=OpenApiParameterSet(),
|
|
393
|
-
query=OpenApiParameterSet(),
|
|
394
|
-
headers=OpenApiParameterSet(),
|
|
395
|
-
cookies=OpenApiParameterSet(),
|
|
393
|
+
path_parameters=OpenApiParameterSet(ParameterLocation.PATH),
|
|
394
|
+
query=OpenApiParameterSet(ParameterLocation.QUERY),
|
|
395
|
+
headers=OpenApiParameterSet(ParameterLocation.HEADER),
|
|
396
|
+
cookies=OpenApiParameterSet(ParameterLocation.COOKIE),
|
|
396
397
|
)
|
|
397
398
|
for parameter in parameters:
|
|
398
399
|
operation.add_parameter(parameter)
|
|
@@ -8,8 +8,9 @@ from schemathesis.config import SanitizationConfig
|
|
|
8
8
|
from schemathesis.core import SCHEMATHESIS_TEST_CASE_HEADER, NotSet
|
|
9
9
|
from schemathesis.core.errors import InvalidSchema
|
|
10
10
|
from schemathesis.core.output.sanitization import sanitize_url, sanitize_value
|
|
11
|
+
from schemathesis.core.parameters import ParameterLocation
|
|
11
12
|
from schemathesis.core.transport import USER_AGENT
|
|
12
|
-
from schemathesis.generation.meta import CoveragePhaseData
|
|
13
|
+
from schemathesis.generation.meta import CoveragePhaseData, CoverageScenario
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from requests import PreparedRequest
|
|
@@ -41,8 +42,8 @@ def get_exclude_headers(case: Case) -> list[str]:
|
|
|
41
42
|
if (
|
|
42
43
|
case.meta is not None
|
|
43
44
|
and isinstance(case.meta.phase.data, CoveragePhaseData)
|
|
44
|
-
and case.meta.phase.data.
|
|
45
|
-
and case.meta.phase.data.
|
|
45
|
+
and case.meta.phase.data.scenario == CoverageScenario.MISSING_PARAMETER
|
|
46
|
+
and case.meta.phase.data.parameter_location == ParameterLocation.HEADER
|
|
46
47
|
and case.meta.phase.data.parameter is not None
|
|
47
48
|
):
|
|
48
49
|
return [case.meta.phase.data.parameter]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.17
|
|
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://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
|
|
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.11
|
|
26
26
|
Classifier: Programming Language :: Python :: 3.12
|
|
27
27
|
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
28
29
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
29
30
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
30
31
|
Classifier: Topic :: Software Development :: Testing
|
|
@@ -50,7 +51,7 @@ Requires-Dist: tomli>=2.2.1; python_version < '3.11'
|
|
|
50
51
|
Requires-Dist: typing-extensions>=4.12.2
|
|
51
52
|
Requires-Dist: werkzeug<4,>=0.16.0
|
|
52
53
|
Provides-Extra: bench
|
|
53
|
-
Requires-Dist: pytest-codspeed==
|
|
54
|
+
Requires-Dist: pytest-codspeed==4.2.0; extra == 'bench'
|
|
54
55
|
Provides-Extra: cov
|
|
55
56
|
Requires-Dist: coverage-enable-subprocess; extra == 'cov'
|
|
56
57
|
Requires-Dist: coverage[toml]>=5.3; extra == 'cov'
|
|
@@ -65,8 +66,8 @@ Requires-Dist: hypothesis-openapi<1,>=0.2; (python_version >= '3.10') and extra
|
|
|
65
66
|
Requires-Dist: mkdocs-material; extra == 'dev'
|
|
66
67
|
Requires-Dist: mkdocstrings[python]; extra == 'dev'
|
|
67
68
|
Requires-Dist: pydantic>=1.10.2; extra == 'dev'
|
|
68
|
-
Requires-Dist: pytest-asyncio<
|
|
69
|
-
Requires-Dist: pytest-codspeed==
|
|
69
|
+
Requires-Dist: pytest-asyncio<2.0,>=1.0; extra == 'dev'
|
|
70
|
+
Requires-Dist: pytest-codspeed==4.2.0; extra == 'dev'
|
|
70
71
|
Requires-Dist: pytest-httpserver<2.0,>=1.0; extra == 'dev'
|
|
71
72
|
Requires-Dist: pytest-mock<4.0,>=3.7.0; extra == 'dev'
|
|
72
73
|
Requires-Dist: pytest-trio<1.0,>=0.8; extra == 'dev'
|
|
@@ -85,7 +86,7 @@ Requires-Dist: fastapi>=0.86.0; extra == 'tests'
|
|
|
85
86
|
Requires-Dist: flask<3.0,>=2.1.1; extra == 'tests'
|
|
86
87
|
Requires-Dist: hypothesis-openapi<1,>=0.2; (python_version >= '3.10') and extra == 'tests'
|
|
87
88
|
Requires-Dist: pydantic>=1.10.2; extra == 'tests'
|
|
88
|
-
Requires-Dist: pytest-asyncio<
|
|
89
|
+
Requires-Dist: pytest-asyncio<2.0,>=1.0; extra == 'tests'
|
|
89
90
|
Requires-Dist: pytest-httpserver<2.0,>=1.0; extra == 'tests'
|
|
90
91
|
Requires-Dist: pytest-mock<4.0,>=3.7.0; extra == 'tests'
|
|
91
92
|
Requires-Dist: pytest-trio<1.0,>=0.8; extra == 'tests'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
schemathesis/__init__.py,sha256=QqVUCBQr-RDEstgCZLsxzIa9HJslVSeijrm9gES4b_0,1423
|
|
2
|
-
schemathesis/auths.py,sha256=
|
|
2
|
+
schemathesis/auths.py,sha256=UNMX7Fo_9V-JzkOuiANdWWYsbtmWU_yFaPKG0q34T1I,17414
|
|
3
3
|
schemathesis/checks.py,sha256=F_lsC5JTUKm_ByvilBN_9IpbL4mJiidfLgS8ir2ZDoQ,6873
|
|
4
4
|
schemathesis/errors.py,sha256=K3irHIZkrBH2-9LIjlgXlm8RNC41Nffd39ncfwagUvw,1053
|
|
5
5
|
schemathesis/filters.py,sha256=IevPA5A04GfRLLjmkFLZ0CLhjNO3RmpZq_yw6MqjLIA,13515
|
|
@@ -23,7 +23,7 @@ schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31M
|
|
|
23
23
|
schemathesis/cli/commands/run/handlers/base.py,sha256=qUtDvtr3F6were_BznfnaPpMibGJMnQ5CA9aEzcIUBc,1306
|
|
24
24
|
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=2sXW9jykEFw4HCv25ycRfRd8uw5VV1e26Cp14O7PVhs,19245
|
|
25
25
|
schemathesis/cli/commands/run/handlers/junitxml.py,sha256=qiFvM4-SlM67sep003SkLqPslzaEb4nOm3bkzw-DO-Q,2602
|
|
26
|
-
schemathesis/cli/commands/run/handlers/output.py,sha256=
|
|
26
|
+
schemathesis/cli/commands/run/handlers/output.py,sha256=pwjYBdtlhOGtxAOovBubaRcDeTx03rSwBsN2ZrjNe6o,64397
|
|
27
27
|
schemathesis/cli/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
schemathesis/cli/ext/fs.py,sha256=dHQYBjQozQmuSSfXVp-2KWFK0ESOb_w-lV2SptfMfco,461
|
|
29
29
|
schemathesis/cli/ext/groups.py,sha256=kQ37t6qeArcKaY2y5VxyK3_KwAkBKCVm58IYV8gewds,2720
|
|
@@ -52,7 +52,7 @@ schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1
|
|
|
52
52
|
schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
|
|
53
53
|
schemathesis/core/curl.py,sha256=jrPL9KpNHteyJ6A1oxJRSkL5bfuBeuPs3xh9Z_ml2cE,1892
|
|
54
54
|
schemathesis/core/deserialization.py,sha256=qjXUPaz_mc1OSgXzTUSkC8tuVR8wgVQtb9g3CcAF6D0,2951
|
|
55
|
-
schemathesis/core/errors.py,sha256=
|
|
55
|
+
schemathesis/core/errors.py,sha256=uH98Z4Ft25jm2fIJPEj6gEElrUCNVCEkzmvNupD0b3o,21028
|
|
56
56
|
schemathesis/core/failures.py,sha256=yFpAxWdEnm0Ri8z8RqRI9H7vcLH5ztOeSIi4m4SGx5g,8996
|
|
57
57
|
schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
|
|
58
58
|
schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
|
|
@@ -79,7 +79,7 @@ schemathesis/engine/__init__.py,sha256=QaFE-FinaTAaarteADo2RRMJ-Sz6hZB9TzD5KjMin
|
|
|
79
79
|
schemathesis/engine/context.py,sha256=YaBfwTUyTCZaMq7-jtAKFQj-Eh1aQdbZ0UNcC5d_epU,5792
|
|
80
80
|
schemathesis/engine/control.py,sha256=FXzP8dxL47j1Giqpy2-Bsr_MdMw9YiATSK_UfpFwDtk,1348
|
|
81
81
|
schemathesis/engine/core.py,sha256=qlPHnZVq2RrUe93fOciXd1hC3E1gVyF2BIWMPMeLIj8,6655
|
|
82
|
-
schemathesis/engine/errors.py,sha256=
|
|
82
|
+
schemathesis/engine/errors.py,sha256=mJhD2Z8IopFTMFJxJ9FjQdLGK_kswV6n8d3u8kvdh0k,17398
|
|
83
83
|
schemathesis/engine/events.py,sha256=jpCtMkWWfNe2jUeZh_Ly_wfZEF44EOodL-I_W4C9rgg,6594
|
|
84
84
|
schemathesis/engine/observations.py,sha256=T-5R8GeVIqvxpCMxc6vZ04UUxUTx3w7689r3Dc6bIcE,1416
|
|
85
85
|
schemathesis/engine/recorder.py,sha256=KWyWkGkZxIwSDU92jNWCJXU4G4E5WqfhLM6G1Yi7Jyo,8636
|
|
@@ -88,18 +88,18 @@ schemathesis/engine/phases/probes.py,sha256=YogjJcZJcTMS8sMdGnG4oXKmMUj_4r_J7MY-
|
|
|
88
88
|
schemathesis/engine/phases/stateful/__init__.py,sha256=Lz1rgNqCfUSIz173XqCGsiMuUI5bh4L-RIFexU1-c_Q,2461
|
|
89
89
|
schemathesis/engine/phases/stateful/_executor.py,sha256=yRpUJqKLTKMVRy7hEXPwmI23CtgGIprz341lCJwvTrU,15613
|
|
90
90
|
schemathesis/engine/phases/stateful/context.py,sha256=A7X1SLDOWFpCvFN9IiIeNVZM0emjqatmJL_k9UsO7vM,2946
|
|
91
|
-
schemathesis/engine/phases/unit/__init__.py,sha256=
|
|
92
|
-
schemathesis/engine/phases/unit/_executor.py,sha256=
|
|
91
|
+
schemathesis/engine/phases/unit/__init__.py,sha256=P5w8TdZPIi59Z4Nd85Th8N82UvVlMitsMiKud7PC9Es,8828
|
|
92
|
+
schemathesis/engine/phases/unit/_executor.py,sha256=YBfRcBfVxsB7FaSBMvUunkwU_Nkls3bDfzy0mDALveg,18000
|
|
93
93
|
schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
|
|
94
94
|
schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
|
|
95
95
|
schemathesis/generation/case.py,sha256=SLMw6zkzmeiZdaIij8_0tjTF70BrMlRSWREaqWii0uM,12508
|
|
96
|
-
schemathesis/generation/coverage.py,sha256=
|
|
97
|
-
schemathesis/generation/meta.py,sha256=
|
|
96
|
+
schemathesis/generation/coverage.py,sha256=c6v94OtI-a0BME6Mi-R3ralir_lzLvIhxqmca_YIWOI,65449
|
|
97
|
+
schemathesis/generation/meta.py,sha256=8i1Q8WZKrd0q1nX6QTslZ1zTgbQiR7kjBgM_i1Q6BGA,5348
|
|
98
98
|
schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz5-kt4,2836
|
|
99
99
|
schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
|
|
100
100
|
schemathesis/generation/overrides.py,sha256=xI2djHsa42fzP32xpxgxO52INixKagf5DjDAWJYswM8,3890
|
|
101
101
|
schemathesis/generation/hypothesis/__init__.py,sha256=68BHULoXQC1WjFfw03ga5lvDGZ-c-J7H_fNEuUzFWRw,4976
|
|
102
|
-
schemathesis/generation/hypothesis/builder.py,sha256=
|
|
102
|
+
schemathesis/generation/hypothesis/builder.py,sha256=AssgxGpB41QzebsGY2TgHKEQ1eakPRie0sihumUDrmw,39591
|
|
103
103
|
schemathesis/generation/hypothesis/examples.py,sha256=9fc3fQW1e0ZxJuHyyCGhPeyL_gCtb8h29XK-Yz-s1Vk,1716
|
|
104
104
|
schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
|
|
105
105
|
schemathesis/generation/hypothesis/reporting.py,sha256=d5vuv5KF4bgIoMV2qy_LwikfowK8BQkj8fjORMJT6PU,11481
|
|
@@ -115,9 +115,9 @@ schemathesis/openapi/generation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
|
115
115
|
schemathesis/openapi/generation/filters.py,sha256=pY9cUZdL_kQK80Z2aylTOqqa12zmaYUlYC5BfYgeQMk,2395
|
|
116
116
|
schemathesis/pytest/__init__.py,sha256=7W0q-Thcw03IAQfXE_Mo8JPZpUdHJzfu85fjK1ZdfQM,88
|
|
117
117
|
schemathesis/pytest/control_flow.py,sha256=F8rAPsPeNv_sJiJgbZYtTpwKWjauZmqFUaKroY2GmQI,217
|
|
118
|
-
schemathesis/pytest/lazy.py,sha256=
|
|
118
|
+
schemathesis/pytest/lazy.py,sha256=k8IUZELAN8ph0Q-Em1MeSRMxYNOeySSDnaNjH9K8vlw,13200
|
|
119
119
|
schemathesis/pytest/loaders.py,sha256=Sbv8e5F77_x4amLP50iwubfm6kpOhx7LhLFGsVXW5Ys,925
|
|
120
|
-
schemathesis/pytest/plugin.py,sha256=
|
|
120
|
+
schemathesis/pytest/plugin.py,sha256=Zu8zIZSFdPSbZEGxUcs6KbUG70dSN7ME_qX8QXeO1KM,14900
|
|
121
121
|
schemathesis/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
122
122
|
schemathesis/python/asgi.py,sha256=5PyvuTBaivvyPUEi3pwJni91K1kX5Zc0u9c6c1D8a1Q,287
|
|
123
123
|
schemathesis/python/wsgi.py,sha256=uShAgo_NChbfYaV1117e6UHp0MTg7jaR0Sy_to3Jmf8,219
|
|
@@ -128,8 +128,8 @@ schemathesis/specs/graphql/scalars.py,sha256=6lew8mnwhrtg23leiEbG43mLGPLlRln8mCl
|
|
|
128
128
|
schemathesis/specs/graphql/schemas.py,sha256=oNbluhfxpbxRWVIMoLn3_g-bUaVLCea29jOZiHN1MT0,14509
|
|
129
129
|
schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
|
|
130
130
|
schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
|
|
131
|
-
schemathesis/specs/openapi/_hypothesis.py,sha256
|
|
132
|
-
schemathesis/specs/openapi/checks.py,sha256=
|
|
131
|
+
schemathesis/specs/openapi/_hypothesis.py,sha256=-6F8KRvdT8dJ6zysrUza0abacUT42TAgUlXmsW_wqG4,18353
|
|
132
|
+
schemathesis/specs/openapi/checks.py,sha256=aGkiRysXpungjeZs-9_ZtqRkJneX606a5JzhnYnNkcw,31616
|
|
133
133
|
schemathesis/specs/openapi/converter.py,sha256=OlvGCCDAiIZS_osM9OQBvDGzwEToUZmVWwzQa4wQNws,6463
|
|
134
134
|
schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
|
|
135
135
|
schemathesis/specs/openapi/examples.py,sha256=bNQVYJAb2LGtl_6josaNj5O4B0b_WaqtXVg1ZTvDDv8,24451
|
|
@@ -137,11 +137,11 @@ schemathesis/specs/openapi/formats.py,sha256=4tYRdckauHxkJCmOhmdwDq_eOpHPaKloi89
|
|
|
137
137
|
schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
|
|
138
138
|
schemathesis/specs/openapi/patterns.py,sha256=GqPZEXMRdWENQxanWjBOalIZ2MQUjuxk21kmdiI703E,18027
|
|
139
139
|
schemathesis/specs/openapi/references.py,sha256=Jez2KpfsDIwXSvxZCmRQtlHk2X9UZ-JZkKAJaCPSLk8,2981
|
|
140
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
|
140
|
+
schemathesis/specs/openapi/schemas.py,sha256=MLaqulltv_9jZw6Gik4YSrp8v9VSFnpQGZkBk6ibDSo,34186
|
|
141
141
|
schemathesis/specs/openapi/serialization.py,sha256=RPNdadne5wdhsGmjSvgKLRF58wpzpRx3wura8PsHM3o,12152
|
|
142
142
|
schemathesis/specs/openapi/utils.py,sha256=XkOJT8qD-6uhq-Tmwxk_xYku1Gy5F9pKL3ldNg_DRZw,522
|
|
143
143
|
schemathesis/specs/openapi/adapter/__init__.py,sha256=YEovBgLjnXd3WGPMJXq0KbSGHezkRlEv4dNRO7_evfk,249
|
|
144
|
-
schemathesis/specs/openapi/adapter/parameters.py,sha256=
|
|
144
|
+
schemathesis/specs/openapi/adapter/parameters.py,sha256=RBkyc59CUj1GWOkSkvjeFI0qT0O_yGGAuUvn5uctbVY,27370
|
|
145
145
|
schemathesis/specs/openapi/adapter/protocol.py,sha256=VDF6COcilHEUnmw76YBVur8bFiTFQHsNvaO9pR_i_KM,2709
|
|
146
146
|
schemathesis/specs/openapi/adapter/references.py,sha256=6M59pJy_U_sLh3Xzgu6-izWXtz3bjXnqJYSD65wRHtk,549
|
|
147
147
|
schemathesis/specs/openapi/adapter/responses.py,sha256=UXcYb048SeS0MhydQY518IgYD0s0Q5YpLsBbdX5-5-s,13276
|
|
@@ -174,12 +174,12 @@ schemathesis/specs/openapi/types/__init__.py,sha256=VPsWtLJle__Kodw_QqtQ3OuvBzBc
|
|
|
174
174
|
schemathesis/specs/openapi/types/v3.py,sha256=Vondr9Amk6JKCIM6i6RGcmTUjFfPgOOqzBXqerccLpo,1468
|
|
175
175
|
schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
|
|
176
176
|
schemathesis/transport/asgi.py,sha256=qTClt6oT_xUEWnRHokACN_uqCNNUZrRPT6YG0PjbElY,926
|
|
177
|
-
schemathesis/transport/prepare.py,sha256=
|
|
177
|
+
schemathesis/transport/prepare.py,sha256=4LmGSiguR-rQZAKIX32Mlykp3IohsksTqYwbcN75PVE,4846
|
|
178
178
|
schemathesis/transport/requests.py,sha256=jJAKqmsnlErU066WlUTIZHa3rx5lFtHWT43nm8CewUc,10717
|
|
179
179
|
schemathesis/transport/serialization.py,sha256=GwO6OAVTmL1JyKw7HiZ256tjV4CbrRbhQN0ep1uaZwI,11157
|
|
180
180
|
schemathesis/transport/wsgi.py,sha256=kQtasFre6pjdJWRKwLA_Qb-RyQHCFNpaey9ubzlFWKI,5907
|
|
181
|
-
schemathesis-4.3.
|
|
182
|
-
schemathesis-4.3.
|
|
183
|
-
schemathesis-4.3.
|
|
184
|
-
schemathesis-4.3.
|
|
185
|
-
schemathesis-4.3.
|
|
181
|
+
schemathesis-4.3.17.dist-info/METADATA,sha256=wiFKX2_3kcE1OKc6RhfiZy2i5S41P-fQ4JSMAVMe9ks,8611
|
|
182
|
+
schemathesis-4.3.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
183
|
+
schemathesis-4.3.17.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
|
|
184
|
+
schemathesis-4.3.17.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
|
|
185
|
+
schemathesis-4.3.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|