schemathesis 3.13.0__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.
Files changed (245) hide show
  1. schemathesis/__init__.py +53 -25
  2. schemathesis/auths.py +507 -0
  3. schemathesis/checks.py +190 -25
  4. schemathesis/cli/__init__.py +27 -1016
  5. schemathesis/cli/__main__.py +4 -0
  6. schemathesis/cli/commands/__init__.py +133 -0
  7. schemathesis/cli/commands/data.py +10 -0
  8. schemathesis/cli/commands/run/__init__.py +602 -0
  9. schemathesis/cli/commands/run/context.py +228 -0
  10. schemathesis/cli/commands/run/events.py +60 -0
  11. schemathesis/cli/commands/run/executor.py +157 -0
  12. schemathesis/cli/commands/run/filters.py +53 -0
  13. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  14. schemathesis/cli/commands/run/handlers/base.py +45 -0
  15. schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
  16. schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
  17. schemathesis/cli/commands/run/handlers/output.py +1750 -0
  18. schemathesis/cli/commands/run/loaders.py +118 -0
  19. schemathesis/cli/commands/run/validation.py +256 -0
  20. schemathesis/cli/constants.py +5 -0
  21. schemathesis/cli/core.py +19 -0
  22. schemathesis/cli/ext/fs.py +16 -0
  23. schemathesis/cli/ext/groups.py +203 -0
  24. schemathesis/cli/ext/options.py +81 -0
  25. schemathesis/config/__init__.py +202 -0
  26. schemathesis/config/_auth.py +51 -0
  27. schemathesis/config/_checks.py +268 -0
  28. schemathesis/config/_diff_base.py +101 -0
  29. schemathesis/config/_env.py +21 -0
  30. schemathesis/config/_error.py +163 -0
  31. schemathesis/config/_generation.py +157 -0
  32. schemathesis/config/_health_check.py +24 -0
  33. schemathesis/config/_operations.py +335 -0
  34. schemathesis/config/_output.py +171 -0
  35. schemathesis/config/_parameters.py +19 -0
  36. schemathesis/config/_phases.py +253 -0
  37. schemathesis/config/_projects.py +543 -0
  38. schemathesis/config/_rate_limit.py +17 -0
  39. schemathesis/config/_report.py +120 -0
  40. schemathesis/config/_validator.py +9 -0
  41. schemathesis/config/_warnings.py +89 -0
  42. schemathesis/config/schema.json +975 -0
  43. schemathesis/core/__init__.py +72 -0
  44. schemathesis/core/adapter.py +34 -0
  45. schemathesis/core/compat.py +32 -0
  46. schemathesis/core/control.py +2 -0
  47. schemathesis/core/curl.py +100 -0
  48. schemathesis/core/deserialization.py +210 -0
  49. schemathesis/core/errors.py +588 -0
  50. schemathesis/core/failures.py +316 -0
  51. schemathesis/core/fs.py +19 -0
  52. schemathesis/core/hooks.py +20 -0
  53. schemathesis/core/jsonschema/__init__.py +13 -0
  54. schemathesis/core/jsonschema/bundler.py +183 -0
  55. schemathesis/core/jsonschema/keywords.py +40 -0
  56. schemathesis/core/jsonschema/references.py +222 -0
  57. schemathesis/core/jsonschema/types.py +41 -0
  58. schemathesis/core/lazy_import.py +15 -0
  59. schemathesis/core/loaders.py +107 -0
  60. schemathesis/core/marks.py +66 -0
  61. schemathesis/core/media_types.py +79 -0
  62. schemathesis/core/output/__init__.py +46 -0
  63. schemathesis/core/output/sanitization.py +54 -0
  64. schemathesis/core/parameters.py +45 -0
  65. schemathesis/core/rate_limit.py +60 -0
  66. schemathesis/core/registries.py +34 -0
  67. schemathesis/core/result.py +27 -0
  68. schemathesis/core/schema_analysis.py +17 -0
  69. schemathesis/core/shell.py +203 -0
  70. schemathesis/core/transforms.py +144 -0
  71. schemathesis/core/transport.py +223 -0
  72. schemathesis/core/validation.py +73 -0
  73. schemathesis/core/version.py +7 -0
  74. schemathesis/engine/__init__.py +28 -0
  75. schemathesis/engine/context.py +152 -0
  76. schemathesis/engine/control.py +44 -0
  77. schemathesis/engine/core.py +201 -0
  78. schemathesis/engine/errors.py +446 -0
  79. schemathesis/engine/events.py +284 -0
  80. schemathesis/engine/observations.py +42 -0
  81. schemathesis/engine/phases/__init__.py +108 -0
  82. schemathesis/engine/phases/analysis.py +28 -0
  83. schemathesis/engine/phases/probes.py +172 -0
  84. schemathesis/engine/phases/stateful/__init__.py +68 -0
  85. schemathesis/engine/phases/stateful/_executor.py +364 -0
  86. schemathesis/engine/phases/stateful/context.py +85 -0
  87. schemathesis/engine/phases/unit/__init__.py +220 -0
  88. schemathesis/engine/phases/unit/_executor.py +459 -0
  89. schemathesis/engine/phases/unit/_pool.py +82 -0
  90. schemathesis/engine/recorder.py +254 -0
  91. schemathesis/errors.py +47 -0
  92. schemathesis/filters.py +395 -0
  93. schemathesis/generation/__init__.py +25 -0
  94. schemathesis/generation/case.py +478 -0
  95. schemathesis/generation/coverage.py +1528 -0
  96. schemathesis/generation/hypothesis/__init__.py +121 -0
  97. schemathesis/generation/hypothesis/builder.py +992 -0
  98. schemathesis/generation/hypothesis/examples.py +56 -0
  99. schemathesis/generation/hypothesis/given.py +66 -0
  100. schemathesis/generation/hypothesis/reporting.py +285 -0
  101. schemathesis/generation/meta.py +227 -0
  102. schemathesis/generation/metrics.py +93 -0
  103. schemathesis/generation/modes.py +20 -0
  104. schemathesis/generation/overrides.py +127 -0
  105. schemathesis/generation/stateful/__init__.py +37 -0
  106. schemathesis/generation/stateful/state_machine.py +294 -0
  107. schemathesis/graphql/__init__.py +15 -0
  108. schemathesis/graphql/checks.py +109 -0
  109. schemathesis/graphql/loaders.py +285 -0
  110. schemathesis/hooks.py +270 -91
  111. schemathesis/openapi/__init__.py +13 -0
  112. schemathesis/openapi/checks.py +467 -0
  113. schemathesis/openapi/generation/__init__.py +0 -0
  114. schemathesis/openapi/generation/filters.py +72 -0
  115. schemathesis/openapi/loaders.py +315 -0
  116. schemathesis/pytest/__init__.py +5 -0
  117. schemathesis/pytest/control_flow.py +7 -0
  118. schemathesis/pytest/lazy.py +341 -0
  119. schemathesis/pytest/loaders.py +36 -0
  120. schemathesis/pytest/plugin.py +357 -0
  121. schemathesis/python/__init__.py +0 -0
  122. schemathesis/python/asgi.py +12 -0
  123. schemathesis/python/wsgi.py +12 -0
  124. schemathesis/schemas.py +683 -247
  125. schemathesis/specs/graphql/__init__.py +0 -1
  126. schemathesis/specs/graphql/nodes.py +27 -0
  127. schemathesis/specs/graphql/scalars.py +86 -0
  128. schemathesis/specs/graphql/schemas.py +395 -123
  129. schemathesis/specs/graphql/validation.py +33 -0
  130. schemathesis/specs/openapi/__init__.py +9 -1
  131. schemathesis/specs/openapi/_hypothesis.py +578 -317
  132. schemathesis/specs/openapi/adapter/__init__.py +10 -0
  133. schemathesis/specs/openapi/adapter/parameters.py +729 -0
  134. schemathesis/specs/openapi/adapter/protocol.py +59 -0
  135. schemathesis/specs/openapi/adapter/references.py +19 -0
  136. schemathesis/specs/openapi/adapter/responses.py +368 -0
  137. schemathesis/specs/openapi/adapter/security.py +144 -0
  138. schemathesis/specs/openapi/adapter/v2.py +30 -0
  139. schemathesis/specs/openapi/adapter/v3_0.py +30 -0
  140. schemathesis/specs/openapi/adapter/v3_1.py +30 -0
  141. schemathesis/specs/openapi/analysis.py +96 -0
  142. schemathesis/specs/openapi/checks.py +753 -74
  143. schemathesis/specs/openapi/converter.py +176 -37
  144. schemathesis/specs/openapi/definitions.py +599 -4
  145. schemathesis/specs/openapi/examples.py +581 -165
  146. schemathesis/specs/openapi/expressions/__init__.py +52 -5
  147. schemathesis/specs/openapi/expressions/extractors.py +25 -0
  148. schemathesis/specs/openapi/expressions/lexer.py +34 -31
  149. schemathesis/specs/openapi/expressions/nodes.py +97 -46
  150. schemathesis/specs/openapi/expressions/parser.py +35 -13
  151. schemathesis/specs/openapi/formats.py +122 -0
  152. schemathesis/specs/openapi/media_types.py +75 -0
  153. schemathesis/specs/openapi/negative/__init__.py +117 -68
  154. schemathesis/specs/openapi/negative/mutations.py +294 -104
  155. schemathesis/specs/openapi/negative/utils.py +3 -6
  156. schemathesis/specs/openapi/patterns.py +458 -0
  157. schemathesis/specs/openapi/references.py +60 -81
  158. schemathesis/specs/openapi/schemas.py +648 -650
  159. schemathesis/specs/openapi/serialization.py +53 -30
  160. schemathesis/specs/openapi/stateful/__init__.py +404 -69
  161. schemathesis/specs/openapi/stateful/control.py +87 -0
  162. schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
  163. schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
  164. schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
  165. schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
  166. schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
  167. schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
  168. schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
  169. schemathesis/specs/openapi/stateful/inference.py +254 -0
  170. schemathesis/specs/openapi/stateful/links.py +219 -78
  171. schemathesis/specs/openapi/types/__init__.py +3 -0
  172. schemathesis/specs/openapi/types/common.py +23 -0
  173. schemathesis/specs/openapi/types/v2.py +129 -0
  174. schemathesis/specs/openapi/types/v3.py +134 -0
  175. schemathesis/specs/openapi/utils.py +7 -6
  176. schemathesis/specs/openapi/warnings.py +75 -0
  177. schemathesis/transport/__init__.py +224 -0
  178. schemathesis/transport/asgi.py +26 -0
  179. schemathesis/transport/prepare.py +126 -0
  180. schemathesis/transport/requests.py +278 -0
  181. schemathesis/transport/serialization.py +329 -0
  182. schemathesis/transport/wsgi.py +175 -0
  183. schemathesis-4.4.2.dist-info/METADATA +213 -0
  184. schemathesis-4.4.2.dist-info/RECORD +192 -0
  185. {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
  186. schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
  187. {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
  188. schemathesis/_compat.py +0 -41
  189. schemathesis/_hypothesis.py +0 -115
  190. schemathesis/cli/callbacks.py +0 -188
  191. schemathesis/cli/cassettes.py +0 -253
  192. schemathesis/cli/context.py +0 -36
  193. schemathesis/cli/debug.py +0 -21
  194. schemathesis/cli/handlers.py +0 -11
  195. schemathesis/cli/junitxml.py +0 -41
  196. schemathesis/cli/options.py +0 -51
  197. schemathesis/cli/output/__init__.py +0 -1
  198. schemathesis/cli/output/default.py +0 -508
  199. schemathesis/cli/output/short.py +0 -40
  200. schemathesis/constants.py +0 -79
  201. schemathesis/exceptions.py +0 -207
  202. schemathesis/extra/_aiohttp.py +0 -27
  203. schemathesis/extra/_flask.py +0 -10
  204. schemathesis/extra/_server.py +0 -16
  205. schemathesis/extra/pytest_plugin.py +0 -216
  206. schemathesis/failures.py +0 -131
  207. schemathesis/fixups/__init__.py +0 -29
  208. schemathesis/fixups/fast_api.py +0 -30
  209. schemathesis/lazy.py +0 -227
  210. schemathesis/models.py +0 -1041
  211. schemathesis/parameters.py +0 -88
  212. schemathesis/runner/__init__.py +0 -460
  213. schemathesis/runner/events.py +0 -240
  214. schemathesis/runner/impl/__init__.py +0 -3
  215. schemathesis/runner/impl/core.py +0 -755
  216. schemathesis/runner/impl/solo.py +0 -85
  217. schemathesis/runner/impl/threadpool.py +0 -367
  218. schemathesis/runner/serialization.py +0 -189
  219. schemathesis/serializers.py +0 -233
  220. schemathesis/service/__init__.py +0 -3
  221. schemathesis/service/client.py +0 -46
  222. schemathesis/service/constants.py +0 -12
  223. schemathesis/service/events.py +0 -39
  224. schemathesis/service/handler.py +0 -39
  225. schemathesis/service/models.py +0 -7
  226. schemathesis/service/serialization.py +0 -153
  227. schemathesis/service/worker.py +0 -40
  228. schemathesis/specs/graphql/loaders.py +0 -215
  229. schemathesis/specs/openapi/constants.py +0 -7
  230. schemathesis/specs/openapi/expressions/context.py +0 -12
  231. schemathesis/specs/openapi/expressions/pointers.py +0 -29
  232. schemathesis/specs/openapi/filters.py +0 -44
  233. schemathesis/specs/openapi/links.py +0 -302
  234. schemathesis/specs/openapi/loaders.py +0 -453
  235. schemathesis/specs/openapi/parameters.py +0 -413
  236. schemathesis/specs/openapi/security.py +0 -129
  237. schemathesis/specs/openapi/validation.py +0 -24
  238. schemathesis/stateful.py +0 -349
  239. schemathesis/targets.py +0 -32
  240. schemathesis/types.py +0 -38
  241. schemathesis/utils.py +0 -436
  242. schemathesis-3.13.0.dist-info/METADATA +0 -202
  243. schemathesis-3.13.0.dist-info/RECORD +0 -91
  244. schemathesis-3.13.0.dist-info/entry_points.txt +0 -6
  245. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
@@ -0,0 +1,602 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Any, Callable
5
+
6
+ import click
7
+ from click.utils import LazyFile
8
+
9
+ from schemathesis.checks import CHECKS, load_all_checks
10
+ from schemathesis.cli.commands.run import executor, validation
11
+ from schemathesis.cli.commands.run.filters import with_filters
12
+ from schemathesis.cli.constants import MAX_WORKERS, MIN_WORKERS
13
+ from schemathesis.cli.core import ensure_color
14
+ from schemathesis.cli.ext.groups import group, grouped_option
15
+ from schemathesis.cli.ext.options import (
16
+ CsvChoice,
17
+ CsvEnumChoice,
18
+ CustomHelpMessageChoice,
19
+ RegistryChoice,
20
+ )
21
+ from schemathesis.config import (
22
+ DEFAULT_REPORT_DIRECTORY,
23
+ HealthCheck,
24
+ ReportFormat,
25
+ SchemathesisConfig,
26
+ SchemathesisWarning,
27
+ WarningsConfig,
28
+ )
29
+ from schemathesis.core import HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER
30
+ from schemathesis.core.transport import DEFAULT_RESPONSE_TIMEOUT
31
+ from schemathesis.generation import GenerationMode
32
+ from schemathesis.generation.metrics import METRICS, MetricFunction
33
+
34
+ load_all_checks()
35
+
36
+ COLOR_OPTIONS_INVALID_USAGE_MESSAGE = "Can't use `--no-color` and `--force-color` simultaneously"
37
+
38
+ DEFAULT_PHASES = ["examples", "coverage", "fuzzing", "stateful"]
39
+
40
+
41
+ @click.argument( # type: ignore[misc]
42
+ "location",
43
+ type=str,
44
+ callback=validation.validate_schema_location,
45
+ )
46
+ @group("Options")
47
+ @grouped_option(
48
+ "--url",
49
+ "-u",
50
+ "base_url",
51
+ help="API base URL (required for file-based schemas)",
52
+ metavar="URL",
53
+ type=str,
54
+ callback=validation.validate_base_url,
55
+ envvar="SCHEMATHESIS_BASE_URL",
56
+ )
57
+ @grouped_option(
58
+ "--workers",
59
+ "-w",
60
+ "workers",
61
+ help="Number of concurrent workers for testing. Auto-adjusts if 'auto' is specified",
62
+ type=CustomHelpMessageChoice(
63
+ ["auto", *list(map(str, range(MIN_WORKERS, MAX_WORKERS + 1)))],
64
+ choices_repr=f"[auto, {MIN_WORKERS}-{MAX_WORKERS}]",
65
+ ),
66
+ default=None,
67
+ show_default=True,
68
+ callback=validation.convert_workers,
69
+ metavar="",
70
+ )
71
+ @grouped_option(
72
+ "--phases",
73
+ help="A comma-separated list of test phases to run",
74
+ type=CsvChoice(DEFAULT_PHASES),
75
+ default=",".join(DEFAULT_PHASES),
76
+ metavar="",
77
+ )
78
+ @grouped_option(
79
+ "--suppress-health-check",
80
+ help="A comma-separated list of Schemathesis health checks to disable",
81
+ type=CsvEnumChoice(HealthCheck),
82
+ metavar="",
83
+ )
84
+ @grouped_option(
85
+ "--wait-for-schema",
86
+ help="Maximum duration, in seconds, to wait for the API schema to become available. Disabled by default",
87
+ type=click.FloatRange(1.0),
88
+ default=None,
89
+ envvar="SCHEMATHESIS_WAIT_FOR_SCHEMA",
90
+ )
91
+ @grouped_option(
92
+ "--warnings",
93
+ help="Control warning display: 'off' to disable all, or comma-separated list of warning types to enable",
94
+ type=str,
95
+ default=None,
96
+ callback=validation.validate_warnings,
97
+ metavar="WARNINGS",
98
+ )
99
+ @group("API validation options")
100
+ @grouped_option(
101
+ "--checks",
102
+ "-c",
103
+ "included_check_names",
104
+ multiple=True,
105
+ help="Comma-separated list of checks to run against API responses",
106
+ type=RegistryChoice(CHECKS, with_all=True),
107
+ default=None,
108
+ callback=validation.reduce_list,
109
+ show_default=True,
110
+ metavar="",
111
+ )
112
+ @grouped_option(
113
+ "--exclude-checks",
114
+ "excluded_check_names",
115
+ multiple=True,
116
+ help="Comma-separated list of checks to skip during testing",
117
+ type=RegistryChoice(CHECKS, with_all=True),
118
+ default=None,
119
+ callback=validation.reduce_list,
120
+ show_default=True,
121
+ metavar="",
122
+ )
123
+ @grouped_option(
124
+ "--max-failures",
125
+ "max_failures",
126
+ type=click.IntRange(min=1),
127
+ help="Terminate the test suite after reaching a specified number of failures or errors",
128
+ show_default=True,
129
+ )
130
+ @grouped_option(
131
+ "--continue-on-failure",
132
+ "continue_on_failure",
133
+ help="Continue executing all test cases within a scenario, even after encountering failures",
134
+ is_flag=True,
135
+ default=False,
136
+ metavar="",
137
+ )
138
+ @grouped_option(
139
+ "--max-response-time",
140
+ help="Maximum allowed API response time in seconds",
141
+ type=click.FloatRange(min=0.0, min_open=True),
142
+ metavar="SECONDS",
143
+ )
144
+ @group(
145
+ "Filtering options",
146
+ description=(
147
+ "Filter operations by path, method, name, tag, or operation-id using:\n\n"
148
+ "--include-TYPE VALUE Match operations with exact VALUE\n"
149
+ "--include-TYPE-regex PATTERN Match operations using regular expression\n"
150
+ "--exclude-TYPE VALUE Exclude operations with exact VALUE\n"
151
+ "--exclude-TYPE-regex PATTERN Exclude operations using regular expression"
152
+ ),
153
+ )
154
+ @with_filters
155
+ @grouped_option(
156
+ "--include-by",
157
+ "include_by",
158
+ type=str,
159
+ metavar="EXPR",
160
+ callback=validation.validate_filter_expression,
161
+ help="Include using custom expression",
162
+ )
163
+ @grouped_option(
164
+ "--exclude-by",
165
+ "exclude_by",
166
+ type=str,
167
+ callback=validation.validate_filter_expression,
168
+ metavar="EXPR",
169
+ help="Exclude using custom expression",
170
+ )
171
+ @grouped_option(
172
+ "--exclude-deprecated",
173
+ help="Skip deprecated operations",
174
+ is_flag=True,
175
+ is_eager=True,
176
+ default=None,
177
+ show_default=True,
178
+ )
179
+ @group("Network requests options")
180
+ @grouped_option(
181
+ "--header",
182
+ "-H",
183
+ "headers",
184
+ help=r"Add a custom HTTP header to all API requests",
185
+ metavar="NAME:VALUE",
186
+ multiple=True,
187
+ type=str,
188
+ callback=validation.validate_headers,
189
+ )
190
+ @grouped_option(
191
+ "--auth",
192
+ "-a",
193
+ help="Authenticate all API requests with basic authentication",
194
+ metavar="USER:PASS",
195
+ type=str,
196
+ callback=validation.validate_auth,
197
+ )
198
+ @grouped_option(
199
+ "--proxy",
200
+ "request_proxy",
201
+ help="Set the proxy for all network requests",
202
+ metavar="URL",
203
+ type=str,
204
+ )
205
+ @grouped_option(
206
+ "--tls-verify",
207
+ "request_tls_verify",
208
+ help="Path to CA bundle for TLS verification, or 'false' to disable",
209
+ type=str,
210
+ default=None,
211
+ show_default=True,
212
+ callback=validation.convert_boolean_string,
213
+ )
214
+ @grouped_option(
215
+ "--rate-limit",
216
+ help="Specify a rate limit for test requests in '<limit>/<duration>' format. "
217
+ "Example - `100/m` for 100 requests per minute",
218
+ type=str,
219
+ callback=validation.validate_rate_limit,
220
+ )
221
+ @grouped_option(
222
+ "--max-redirects",
223
+ help="Maximum number of redirects to follow for each request",
224
+ type=click.IntRange(min=0),
225
+ show_default=True,
226
+ )
227
+ @grouped_option(
228
+ "--request-timeout",
229
+ help="Timeout limit, in seconds, for each network request during tests",
230
+ type=click.FloatRange(min=0.0, min_open=True),
231
+ default=DEFAULT_RESPONSE_TIMEOUT,
232
+ )
233
+ @grouped_option(
234
+ "--request-cert",
235
+ help="File path of unencrypted client certificate for authentication. "
236
+ "The certificate can be bundled with a private key (e.g. PEM) or the private "
237
+ "key can be provided with the --request-cert-key argument",
238
+ type=click.Path(exists=True),
239
+ default=None,
240
+ show_default=False,
241
+ )
242
+ @grouped_option(
243
+ "--request-cert-key",
244
+ help="Specify the file path of the private key for the client certificate",
245
+ type=click.Path(exists=True),
246
+ default=None,
247
+ show_default=False,
248
+ callback=validation.validate_request_cert_key,
249
+ )
250
+ @group("Output options")
251
+ @grouped_option(
252
+ "--report",
253
+ "report_formats",
254
+ help="Generate test reports in formats specified as a comma-separated list",
255
+ type=CsvEnumChoice(ReportFormat),
256
+ is_eager=True,
257
+ metavar="FORMAT",
258
+ )
259
+ @grouped_option(
260
+ "--report-dir",
261
+ "report_directory",
262
+ help="Directory to store all report files",
263
+ type=click.Path(file_okay=False, dir_okay=True),
264
+ default=DEFAULT_REPORT_DIRECTORY,
265
+ show_default=True,
266
+ )
267
+ @grouped_option(
268
+ "--report-junit-path",
269
+ help="Custom path for JUnit XML report",
270
+ type=click.File("w", encoding="utf-8"),
271
+ is_eager=True,
272
+ )
273
+ @grouped_option(
274
+ "--report-vcr-path",
275
+ help="Custom path for VCR cassette",
276
+ type=click.File("w", encoding="utf-8"),
277
+ is_eager=True,
278
+ )
279
+ @grouped_option(
280
+ "--report-har-path",
281
+ help="Custom path for HAR file",
282
+ type=click.File("w", encoding="utf-8"),
283
+ is_eager=True,
284
+ )
285
+ @grouped_option(
286
+ "--report-preserve-bytes",
287
+ help="Retain exact byte sequence of payloads in cassettes, encoded as base64",
288
+ type=bool,
289
+ is_flag=True,
290
+ default=None,
291
+ callback=validation.validate_preserve_bytes,
292
+ )
293
+ @grouped_option(
294
+ "--output-sanitize",
295
+ type=str,
296
+ default=None,
297
+ show_default=True,
298
+ help="Enable or disable automatic output sanitization to obscure sensitive data",
299
+ metavar="BOOLEAN",
300
+ callback=validation.convert_boolean_string,
301
+ )
302
+ @grouped_option(
303
+ "--output-truncate",
304
+ help="Truncate schemas and responses in error messages",
305
+ type=str,
306
+ default=None,
307
+ show_default=True,
308
+ metavar="BOOLEAN",
309
+ callback=validation.convert_boolean_string,
310
+ )
311
+ @group("Data generation options")
312
+ @grouped_option(
313
+ "--mode",
314
+ "-m",
315
+ "generation_modes",
316
+ help="Test data generation mode",
317
+ type=click.Choice([item.value for item in GenerationMode] + ["all"]),
318
+ default="all",
319
+ callback=validation.convert_generation_mode,
320
+ show_default=True,
321
+ metavar="",
322
+ )
323
+ @grouped_option(
324
+ "--max-examples",
325
+ "-n",
326
+ "generation_max_examples",
327
+ help="Maximum number of test cases per API operation",
328
+ type=click.IntRange(1),
329
+ )
330
+ @grouped_option(
331
+ "--seed",
332
+ "generation_seed",
333
+ help="Random seed for reproducible test runs",
334
+ type=int,
335
+ )
336
+ @grouped_option(
337
+ "--no-shrink",
338
+ "generation_no_shrink",
339
+ help="Disable test case shrinking. Makes test failures harder to debug but improves performance",
340
+ is_flag=True,
341
+ default=None,
342
+ )
343
+ @grouped_option(
344
+ "--generation-deterministic",
345
+ help="Enables deterministic mode, which eliminates random variation between tests",
346
+ is_flag=True,
347
+ is_eager=True,
348
+ default=None,
349
+ show_default=True,
350
+ )
351
+ @grouped_option(
352
+ "--generation-allow-x00",
353
+ help="Whether to allow the generation of 'NULL' bytes within strings",
354
+ type=str,
355
+ default=None,
356
+ show_default=True,
357
+ metavar="BOOLEAN",
358
+ callback=validation.convert_boolean_string,
359
+ )
360
+ @grouped_option(
361
+ "--generation-codec",
362
+ help="The codec used for generating strings",
363
+ type=str,
364
+ default=None,
365
+ callback=validation.validate_generation_codec,
366
+ )
367
+ @grouped_option(
368
+ "--generation-maximize",
369
+ "generation_maximize",
370
+ multiple=True,
371
+ help="Guide input generation to values more likely to expose bugs via targeted property-based testing",
372
+ type=RegistryChoice(METRICS),
373
+ default=None,
374
+ callback=validation.convert_maximize,
375
+ show_default=True,
376
+ metavar="METRIC",
377
+ )
378
+ @grouped_option(
379
+ "--generation-with-security-parameters",
380
+ help="Whether to generate security parameters",
381
+ type=str,
382
+ default=None,
383
+ show_default=True,
384
+ callback=validation.convert_boolean_string,
385
+ metavar="BOOLEAN",
386
+ )
387
+ @grouped_option(
388
+ "--generation-graphql-allow-null",
389
+ help="Whether to use `null` values for optional arguments in GraphQL queries",
390
+ type=str,
391
+ default=None,
392
+ show_default=True,
393
+ callback=validation.convert_boolean_string,
394
+ metavar="BOOLEAN",
395
+ )
396
+ @grouped_option(
397
+ "--generation-database",
398
+ help="Storage for examples discovered by Schemathesis. "
399
+ f"Use 'none' to disable, '{HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER}' for temporary storage, "
400
+ f"or specify a file path for persistent storage",
401
+ type=str,
402
+ callback=validation.validate_hypothesis_database,
403
+ )
404
+ @grouped_option(
405
+ "--generation-unique-inputs",
406
+ "generation_unique_inputs",
407
+ help="Force the generation of unique test cases",
408
+ is_flag=True,
409
+ default=None,
410
+ show_default=True,
411
+ metavar="BOOLEAN",
412
+ )
413
+ @group("Global options")
414
+ @grouped_option("--no-color", help="Disable ANSI color escape codes", type=bool, is_flag=True)
415
+ @grouped_option("--force-color", help="Explicitly tells to enable ANSI color escape codes", type=bool, is_flag=True)
416
+ @click.pass_context # type: ignore[misc]
417
+ def run(
418
+ ctx: click.Context,
419
+ *,
420
+ location: str,
421
+ auth: tuple[str, str] | None,
422
+ headers: dict[str, str],
423
+ included_check_names: list[str] | None,
424
+ excluded_check_names: list[str] | None,
425
+ max_response_time: float | None = None,
426
+ phases: list[str] = DEFAULT_PHASES,
427
+ max_failures: int | None = None,
428
+ continue_on_failure: bool | None = None,
429
+ include_path: tuple[str, ...],
430
+ include_path_regex: str | None,
431
+ include_method: tuple[str, ...],
432
+ include_method_regex: str | None,
433
+ include_name: tuple[str, ...],
434
+ include_name_regex: str | None,
435
+ include_tag: tuple[str, ...],
436
+ include_tag_regex: str | None,
437
+ include_operation_id: tuple[str, ...],
438
+ include_operation_id_regex: str | None,
439
+ exclude_path: tuple[str, ...],
440
+ exclude_path_regex: str | None,
441
+ exclude_method: tuple[str, ...],
442
+ exclude_method_regex: str | None,
443
+ exclude_name: tuple[str, ...],
444
+ exclude_name_regex: str | None,
445
+ exclude_tag: tuple[str, ...],
446
+ exclude_tag_regex: str | None,
447
+ exclude_operation_id: tuple[str, ...],
448
+ exclude_operation_id_regex: str | None,
449
+ include_by: Callable | None = None,
450
+ exclude_by: Callable | None = None,
451
+ exclude_deprecated: bool | None = None,
452
+ workers: int | None = None,
453
+ base_url: str | None,
454
+ wait_for_schema: float | None = None,
455
+ suppress_health_check: list[HealthCheck] | None,
456
+ warnings: bool | list[SchemathesisWarning] | None,
457
+ rate_limit: str | None = None,
458
+ max_redirects: int | None = None,
459
+ request_timeout: int | None = None,
460
+ request_tls_verify: bool | None = None,
461
+ request_cert: str | None = None,
462
+ request_cert_key: str | None = None,
463
+ request_proxy: str | None = None,
464
+ report_formats: list[ReportFormat] | None,
465
+ report_directory: Path | str = DEFAULT_REPORT_DIRECTORY,
466
+ report_junit_path: LazyFile | None = None,
467
+ report_vcr_path: LazyFile | None = None,
468
+ report_har_path: LazyFile | None = None,
469
+ report_preserve_bytes: bool | None = None,
470
+ output_sanitize: bool | None = None,
471
+ output_truncate: bool | None = None,
472
+ generation_modes: list[GenerationMode],
473
+ generation_seed: int | None = None,
474
+ generation_max_examples: int | None = None,
475
+ generation_maximize: list[MetricFunction] | None,
476
+ generation_deterministic: bool | None = None,
477
+ generation_database: str | None = None,
478
+ generation_unique_inputs: bool | None = None,
479
+ generation_allow_x00: bool | None = None,
480
+ generation_graphql_allow_null: bool | None = None,
481
+ generation_with_security_parameters: bool | None = None,
482
+ generation_codec: str | None = None,
483
+ generation_no_shrink: bool | None = None,
484
+ force_color: bool = False,
485
+ no_color: bool = False,
486
+ **__kwargs: Any,
487
+ ) -> None:
488
+ """Generate and run property-based tests against your API.
489
+
490
+ \b
491
+ LOCATION can be:
492
+ - Local file: ./openapi.json, ./schema.yaml, ./schema.graphql
493
+ - OpenAPI URL: https://api.example.com/openapi.json
494
+ - GraphQL URL: https://api.example.com/graphql/
495
+ """ # noqa: D301
496
+ if no_color and force_color:
497
+ raise click.UsageError(COLOR_OPTIONS_INVALID_USAGE_MESSAGE)
498
+
499
+ config: SchemathesisConfig = ctx.obj.config
500
+
501
+ # First, set the right color
502
+ color: bool | None
503
+ if force_color:
504
+ color = True
505
+ elif no_color:
506
+ color = False
507
+ else:
508
+ color = config.color
509
+ ensure_color(ctx, color)
510
+
511
+ validation.validate_auth_overlap(auth, headers)
512
+
513
+ # Then override the global config from CLI options
514
+ config.update(
515
+ color=color,
516
+ suppress_health_check=suppress_health_check,
517
+ seed=generation_seed,
518
+ wait_for_schema=wait_for_schema,
519
+ max_failures=max_failures,
520
+ )
521
+ config.output.sanitization.update(enabled=output_sanitize)
522
+ config.output.truncation.update(enabled=output_truncate)
523
+ config.reports.update(
524
+ formats=report_formats,
525
+ junit_path=report_junit_path.name if report_junit_path else None,
526
+ vcr_path=report_vcr_path.name if report_vcr_path else None,
527
+ har_path=report_har_path.name if report_har_path else None,
528
+ directory=Path(report_directory),
529
+ preserve_bytes=report_preserve_bytes,
530
+ )
531
+ # Other CLI options work as an override for all defined projects
532
+ config.projects.override.update(
533
+ base_url=base_url,
534
+ headers=headers if headers else None,
535
+ basic_auth=auth,
536
+ workers=workers,
537
+ continue_on_failure=continue_on_failure,
538
+ rate_limit=rate_limit,
539
+ max_redirects=max_redirects,
540
+ request_timeout=request_timeout,
541
+ tls_verify=request_tls_verify,
542
+ request_cert=request_cert,
543
+ request_cert_key=request_cert_key,
544
+ proxy=request_proxy,
545
+ warnings=WarningsConfig.from_value([w.value for w in warnings] if isinstance(warnings, list) else warnings)
546
+ if warnings is not None
547
+ else None,
548
+ )
549
+ # These are filters for what API operations should be tested
550
+ filter_set = {
551
+ "include_path": include_path,
552
+ "include_method": include_method,
553
+ "include_name": include_name,
554
+ "include_tag": include_tag,
555
+ "include_operation_id": include_operation_id,
556
+ "include_path_regex": include_path_regex,
557
+ "include_method_regex": include_method_regex,
558
+ "include_name_regex": include_name_regex,
559
+ "include_tag_regex": include_tag_regex,
560
+ "include_operation_id_regex": include_operation_id_regex,
561
+ "exclude_path": exclude_path,
562
+ "exclude_method": exclude_method,
563
+ "exclude_name": exclude_name,
564
+ "exclude_tag": exclude_tag,
565
+ "exclude_operation_id": exclude_operation_id,
566
+ "exclude_path_regex": exclude_path_regex,
567
+ "exclude_method_regex": exclude_method_regex,
568
+ "exclude_name_regex": exclude_name_regex,
569
+ "exclude_tag_regex": exclude_tag_regex,
570
+ "exclude_operation_id_regex": exclude_operation_id_regex,
571
+ "include_by": include_by,
572
+ "exclude_by": exclude_by,
573
+ "exclude_deprecated": exclude_deprecated,
574
+ }
575
+ config.projects.override.phases.update(phases=phases)
576
+ config.projects.override.checks.update(
577
+ included_check_names=included_check_names,
578
+ excluded_check_names=excluded_check_names,
579
+ max_response_time=max_response_time,
580
+ )
581
+ config.projects.override.generation.update(
582
+ modes=generation_modes,
583
+ max_examples=generation_max_examples,
584
+ no_shrink=generation_no_shrink,
585
+ maximize=generation_maximize,
586
+ deterministic=generation_deterministic,
587
+ database=generation_database,
588
+ unique_inputs=generation_unique_inputs,
589
+ allow_x00=generation_allow_x00,
590
+ graphql_allow_null=generation_graphql_allow_null,
591
+ with_security_parameters=generation_with_security_parameters,
592
+ codec=generation_codec,
593
+ )
594
+
595
+ executor.execute(
596
+ location=location,
597
+ filter_set=filter_set,
598
+ # We don't the project yet, so pass the default config
599
+ config=config.projects.get_default(),
600
+ args=ctx.args,
601
+ params=ctx.params,
602
+ )