schemathesis 4.1.4__py3-none-any.whl → 4.2.1__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/cli/commands/run/executor.py +1 -1
- schemathesis/cli/commands/run/handlers/base.py +28 -1
- schemathesis/cli/commands/run/handlers/cassettes.py +109 -137
- schemathesis/cli/commands/run/handlers/junitxml.py +5 -6
- schemathesis/cli/commands/run/handlers/output.py +7 -1
- schemathesis/cli/ext/fs.py +1 -1
- schemathesis/config/_diff_base.py +3 -1
- schemathesis/config/_operations.py +2 -0
- schemathesis/config/_phases.py +21 -4
- schemathesis/config/_projects.py +10 -2
- schemathesis/core/adapter.py +34 -0
- schemathesis/core/errors.py +29 -5
- schemathesis/core/jsonschema/__init__.py +13 -0
- schemathesis/core/jsonschema/bundler.py +163 -0
- schemathesis/{specs/openapi/constants.py → core/jsonschema/keywords.py} +0 -8
- schemathesis/core/jsonschema/references.py +122 -0
- schemathesis/core/jsonschema/types.py +41 -0
- schemathesis/core/media_types.py +6 -4
- schemathesis/core/parameters.py +37 -0
- schemathesis/core/transforms.py +25 -2
- schemathesis/core/validation.py +19 -0
- schemathesis/engine/context.py +1 -1
- schemathesis/engine/errors.py +11 -18
- schemathesis/engine/phases/stateful/_executor.py +1 -1
- schemathesis/engine/phases/unit/_executor.py +30 -13
- schemathesis/errors.py +4 -0
- schemathesis/filters.py +2 -2
- schemathesis/generation/coverage.py +87 -11
- schemathesis/generation/hypothesis/__init__.py +79 -2
- schemathesis/generation/hypothesis/builder.py +108 -70
- schemathesis/generation/meta.py +5 -14
- schemathesis/generation/overrides.py +17 -17
- schemathesis/pytest/lazy.py +1 -1
- schemathesis/pytest/plugin.py +1 -6
- schemathesis/schemas.py +22 -72
- schemathesis/specs/graphql/schemas.py +27 -16
- schemathesis/specs/openapi/_hypothesis.py +83 -68
- schemathesis/specs/openapi/adapter/__init__.py +10 -0
- schemathesis/specs/openapi/adapter/parameters.py +504 -0
- schemathesis/specs/openapi/adapter/protocol.py +57 -0
- schemathesis/specs/openapi/adapter/references.py +19 -0
- schemathesis/specs/openapi/adapter/responses.py +329 -0
- schemathesis/specs/openapi/adapter/security.py +141 -0
- schemathesis/specs/openapi/adapter/v2.py +28 -0
- schemathesis/specs/openapi/adapter/v3_0.py +28 -0
- schemathesis/specs/openapi/adapter/v3_1.py +28 -0
- schemathesis/specs/openapi/checks.py +99 -90
- schemathesis/specs/openapi/converter.py +114 -27
- schemathesis/specs/openapi/examples.py +210 -168
- schemathesis/specs/openapi/negative/__init__.py +12 -7
- schemathesis/specs/openapi/negative/mutations.py +68 -40
- schemathesis/specs/openapi/references.py +2 -175
- schemathesis/specs/openapi/schemas.py +142 -490
- schemathesis/specs/openapi/serialization.py +15 -7
- schemathesis/specs/openapi/stateful/__init__.py +17 -12
- schemathesis/specs/openapi/stateful/inference.py +13 -11
- schemathesis/specs/openapi/stateful/links.py +5 -20
- schemathesis/specs/openapi/types/__init__.py +3 -0
- schemathesis/specs/openapi/types/v3.py +68 -0
- schemathesis/specs/openapi/utils.py +1 -13
- schemathesis/transport/requests.py +3 -11
- schemathesis/transport/serialization.py +63 -27
- schemathesis/transport/wsgi.py +1 -8
- {schemathesis-4.1.4.dist-info → schemathesis-4.2.1.dist-info}/METADATA +2 -2
- {schemathesis-4.1.4.dist-info → schemathesis-4.2.1.dist-info}/RECORD +68 -53
- schemathesis/specs/openapi/parameters.py +0 -405
- schemathesis/specs/openapi/security.py +0 -162
- {schemathesis-4.1.4.dist-info → schemathesis-4.2.1.dist-info}/WHEEL +0 -0
- {schemathesis-4.1.4.dist-info → schemathesis-4.2.1.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.1.4.dist-info → schemathesis-4.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,162 +0,0 @@
|
|
1
|
-
"""Processing of ``securityDefinitions`` or ``securitySchemes`` keywords."""
|
2
|
-
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Generator
|
7
|
-
|
8
|
-
from .parameters import OpenAPI20Parameter, OpenAPI30Parameter, OpenAPIParameter
|
9
|
-
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from jsonschema import RefResolver
|
12
|
-
|
13
|
-
from schemathesis.schemas import APIOperation
|
14
|
-
|
15
|
-
|
16
|
-
@dataclass
|
17
|
-
class BaseSecurityProcessor:
|
18
|
-
api_key_locations: ClassVar[tuple[str, ...]] = ("header", "query")
|
19
|
-
http_security_name: ClassVar[str] = "basic"
|
20
|
-
parameter_cls: ClassVar[type[OpenAPIParameter]] = OpenAPI20Parameter
|
21
|
-
|
22
|
-
def process_definitions(self, schema: dict[str, Any], operation: APIOperation, resolver: RefResolver) -> None:
|
23
|
-
"""Add relevant security parameters to data generation."""
|
24
|
-
__tracebackhide__ = True
|
25
|
-
for definition in self._get_active_definitions(schema, operation, resolver):
|
26
|
-
name = definition.get("name")
|
27
|
-
location = definition.get("in")
|
28
|
-
if name is not None and location is not None and operation.get_parameter(name, location) is not None:
|
29
|
-
# Such parameter is already defined
|
30
|
-
continue
|
31
|
-
if definition["type"] == "apiKey":
|
32
|
-
self.process_api_key_security_definition(definition, operation)
|
33
|
-
self.process_http_security_definition(definition, operation)
|
34
|
-
|
35
|
-
@staticmethod
|
36
|
-
def _get_security_requirements(schema: dict[str, Any], operation: APIOperation) -> list[dict]:
|
37
|
-
global_requirements = schema.get("security", [])
|
38
|
-
local_requirements = operation.definition.raw.get("security", None)
|
39
|
-
if local_requirements is not None:
|
40
|
-
return local_requirements
|
41
|
-
return global_requirements
|
42
|
-
|
43
|
-
@staticmethod
|
44
|
-
def get_security_requirements(schema: dict[str, Any], operation: APIOperation) -> list[str]:
|
45
|
-
"""Get applied security requirements for the given API operation."""
|
46
|
-
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object
|
47
|
-
# > This definition overrides any declared top-level security.
|
48
|
-
# > To remove a top-level security declaration, an empty array can be used.
|
49
|
-
requirements = BaseSecurityProcessor._get_security_requirements(schema, operation)
|
50
|
-
return [key for requirement in requirements for key in requirement]
|
51
|
-
|
52
|
-
@staticmethod
|
53
|
-
def has_optional_auth(schema: dict[str, Any], operation: APIOperation) -> bool:
|
54
|
-
requirements = BaseSecurityProcessor._get_security_requirements(schema, operation)
|
55
|
-
return {} in requirements
|
56
|
-
|
57
|
-
def _get_active_definitions(
|
58
|
-
self, schema: dict[str, Any], operation: APIOperation, resolver: RefResolver
|
59
|
-
) -> Generator[dict[str, Any], None, None]:
|
60
|
-
"""Get only security definitions active for the given API operation."""
|
61
|
-
definitions = self.get_security_definitions(schema, resolver)
|
62
|
-
requirements = self.get_security_requirements(schema, operation)
|
63
|
-
for name, definition in definitions.items():
|
64
|
-
if name in requirements:
|
65
|
-
yield definition
|
66
|
-
|
67
|
-
def get_security_definitions(self, schema: dict[str, Any], resolver: RefResolver) -> dict[str, Any]:
|
68
|
-
return schema.get("securityDefinitions", {})
|
69
|
-
|
70
|
-
def get_security_definitions_as_parameters(
|
71
|
-
self, schema: dict[str, Any], operation: APIOperation, resolver: RefResolver, location: str
|
72
|
-
) -> list[dict[str, Any]]:
|
73
|
-
"""Security definitions converted to OAS parameters.
|
74
|
-
|
75
|
-
We need it to get proper serialization that will be applied on generated values. For this case it is only
|
76
|
-
coercing to a string.
|
77
|
-
"""
|
78
|
-
return [
|
79
|
-
self._to_parameter(definition)
|
80
|
-
for definition in self._get_active_definitions(schema, operation, resolver)
|
81
|
-
if self._is_match(definition, location)
|
82
|
-
]
|
83
|
-
|
84
|
-
def process_api_key_security_definition(self, definition: dict[str, Any], operation: APIOperation) -> None:
|
85
|
-
parameter = self.parameter_cls(self._make_api_key_parameter(definition))
|
86
|
-
operation.add_parameter(parameter)
|
87
|
-
|
88
|
-
def process_http_security_definition(self, definition: dict[str, Any], operation: APIOperation) -> None:
|
89
|
-
if definition["type"] == self.http_security_name:
|
90
|
-
parameter = self.parameter_cls(self._make_http_auth_parameter(definition))
|
91
|
-
operation.add_parameter(parameter)
|
92
|
-
|
93
|
-
def _is_match(self, definition: dict[str, Any], location: str) -> bool:
|
94
|
-
return (definition["type"] == "apiKey" and location in self.api_key_locations) or (
|
95
|
-
definition["type"] == self.http_security_name and location == "header"
|
96
|
-
)
|
97
|
-
|
98
|
-
def _to_parameter(self, definition: dict[str, Any]) -> dict[str, Any]:
|
99
|
-
func = {
|
100
|
-
"apiKey": self._make_api_key_parameter,
|
101
|
-
self.http_security_name: self._make_http_auth_parameter,
|
102
|
-
}[definition["type"]]
|
103
|
-
return func(definition)
|
104
|
-
|
105
|
-
def _make_http_auth_parameter(self, definition: dict[str, Any]) -> dict[str, Any]:
|
106
|
-
schema = make_auth_header_schema(definition)
|
107
|
-
return make_auth_header(**schema)
|
108
|
-
|
109
|
-
def _make_api_key_parameter(self, definition: dict[str, Any]) -> dict[str, Any]:
|
110
|
-
return make_api_key_schema(definition, type="string")
|
111
|
-
|
112
|
-
|
113
|
-
def make_auth_header_schema(definition: dict[str, Any]) -> dict[str, str]:
|
114
|
-
schema = definition.get("scheme", "basic").lower()
|
115
|
-
return {"type": "string", "format": f"_{schema}_auth"}
|
116
|
-
|
117
|
-
|
118
|
-
def make_auth_header(**kwargs: Any) -> dict[str, Any]:
|
119
|
-
return {"name": "Authorization", "in": "header", "required": True, **kwargs}
|
120
|
-
|
121
|
-
|
122
|
-
def make_api_key_schema(definition: dict[str, Any], **kwargs: Any) -> dict[str, Any]:
|
123
|
-
return {"name": definition["name"], "required": True, "in": definition["in"], **kwargs}
|
124
|
-
|
125
|
-
|
126
|
-
SwaggerSecurityProcessor = BaseSecurityProcessor
|
127
|
-
|
128
|
-
|
129
|
-
@dataclass
|
130
|
-
class OpenAPISecurityProcessor(BaseSecurityProcessor):
|
131
|
-
api_key_locations: ClassVar[tuple[str, ...]] = ("header", "cookie", "query")
|
132
|
-
http_security_name: ClassVar[str] = "http"
|
133
|
-
parameter_cls: ClassVar[type[OpenAPIParameter]] = OpenAPI30Parameter
|
134
|
-
|
135
|
-
def get_security_definitions(self, schema: dict[str, Any], resolver: RefResolver) -> dict[str, Any]:
|
136
|
-
"""In Open API 3 security definitions are located in ``components`` and may have references inside."""
|
137
|
-
components = schema.get("components", {})
|
138
|
-
security_schemes = components.get("securitySchemes", {})
|
139
|
-
# At this point, the resolution scope could differ from the root scope, that's why we need to restore it
|
140
|
-
# as now we resolve root-level references
|
141
|
-
if len(resolver._scopes_stack) > 1:
|
142
|
-
scope = resolver.resolution_scope
|
143
|
-
resolver.pop_scope()
|
144
|
-
else:
|
145
|
-
scope = None
|
146
|
-
resolve = resolver.resolve
|
147
|
-
try:
|
148
|
-
if "$ref" in security_schemes:
|
149
|
-
return resolve(security_schemes["$ref"])[1]
|
150
|
-
return {
|
151
|
-
key: resolve(value["$ref"])[1] if "$ref" in value else value for key, value in security_schemes.items()
|
152
|
-
}
|
153
|
-
finally:
|
154
|
-
if scope is not None:
|
155
|
-
resolver._scopes_stack.append(scope)
|
156
|
-
|
157
|
-
def _make_http_auth_parameter(self, definition: dict[str, Any]) -> dict[str, Any]:
|
158
|
-
schema = make_auth_header_schema(definition)
|
159
|
-
return make_auth_header(schema=schema)
|
160
|
-
|
161
|
-
def _make_api_key_parameter(self, definition: dict[str, Any]) -> dict[str, Any]:
|
162
|
-
return make_api_key_schema(definition, schema={"type": "string"})
|
File without changes
|
File without changes
|
File without changes
|