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.
Files changed (251) 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 -1219
  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 +682 -257
  125. schemathesis/specs/graphql/__init__.py +0 -1
  126. schemathesis/specs/graphql/nodes.py +26 -2
  127. schemathesis/specs/graphql/scalars.py +77 -12
  128. schemathesis/specs/graphql/schemas.py +367 -148
  129. schemathesis/specs/graphql/validation.py +33 -0
  130. schemathesis/specs/openapi/__init__.py +9 -1
  131. schemathesis/specs/openapi/_hypothesis.py +555 -318
  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 +748 -82
  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 +93 -73
  154. schemathesis/specs/openapi/negative/mutations.py +294 -103
  155. schemathesis/specs/openapi/negative/utils.py +0 -9
  156. schemathesis/specs/openapi/patterns.py +458 -0
  157. schemathesis/specs/openapi/references.py +60 -81
  158. schemathesis/specs/openapi/schemas.py +647 -666
  159. schemathesis/specs/openapi/serialization.py +53 -30
  160. schemathesis/specs/openapi/stateful/__init__.py +403 -68
  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.15.4.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.15.4.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
  188. schemathesis/_compat.py +0 -57
  189. schemathesis/_hypothesis.py +0 -123
  190. schemathesis/auth.py +0 -214
  191. schemathesis/cli/callbacks.py +0 -240
  192. schemathesis/cli/cassettes.py +0 -351
  193. schemathesis/cli/context.py +0 -38
  194. schemathesis/cli/debug.py +0 -21
  195. schemathesis/cli/handlers.py +0 -11
  196. schemathesis/cli/junitxml.py +0 -41
  197. schemathesis/cli/options.py +0 -70
  198. schemathesis/cli/output/__init__.py +0 -1
  199. schemathesis/cli/output/default.py +0 -521
  200. schemathesis/cli/output/short.py +0 -40
  201. schemathesis/constants.py +0 -88
  202. schemathesis/exceptions.py +0 -257
  203. schemathesis/extra/_aiohttp.py +0 -27
  204. schemathesis/extra/_flask.py +0 -10
  205. schemathesis/extra/_server.py +0 -16
  206. schemathesis/extra/pytest_plugin.py +0 -251
  207. schemathesis/failures.py +0 -145
  208. schemathesis/fixups/__init__.py +0 -29
  209. schemathesis/fixups/fast_api.py +0 -30
  210. schemathesis/graphql.py +0 -5
  211. schemathesis/internal.py +0 -6
  212. schemathesis/lazy.py +0 -301
  213. schemathesis/models.py +0 -1113
  214. schemathesis/parameters.py +0 -91
  215. schemathesis/runner/__init__.py +0 -470
  216. schemathesis/runner/events.py +0 -242
  217. schemathesis/runner/impl/__init__.py +0 -3
  218. schemathesis/runner/impl/core.py +0 -791
  219. schemathesis/runner/impl/solo.py +0 -85
  220. schemathesis/runner/impl/threadpool.py +0 -367
  221. schemathesis/runner/serialization.py +0 -206
  222. schemathesis/serializers.py +0 -253
  223. schemathesis/service/__init__.py +0 -18
  224. schemathesis/service/auth.py +0 -10
  225. schemathesis/service/client.py +0 -62
  226. schemathesis/service/constants.py +0 -25
  227. schemathesis/service/events.py +0 -39
  228. schemathesis/service/handler.py +0 -46
  229. schemathesis/service/hosts.py +0 -74
  230. schemathesis/service/metadata.py +0 -42
  231. schemathesis/service/models.py +0 -21
  232. schemathesis/service/serialization.py +0 -184
  233. schemathesis/service/worker.py +0 -39
  234. schemathesis/specs/graphql/loaders.py +0 -215
  235. schemathesis/specs/openapi/constants.py +0 -7
  236. schemathesis/specs/openapi/expressions/context.py +0 -12
  237. schemathesis/specs/openapi/expressions/pointers.py +0 -29
  238. schemathesis/specs/openapi/filters.py +0 -44
  239. schemathesis/specs/openapi/links.py +0 -303
  240. schemathesis/specs/openapi/loaders.py +0 -453
  241. schemathesis/specs/openapi/parameters.py +0 -430
  242. schemathesis/specs/openapi/security.py +0 -129
  243. schemathesis/specs/openapi/validation.py +0 -24
  244. schemathesis/stateful.py +0 -358
  245. schemathesis/targets.py +0 -32
  246. schemathesis/types.py +0 -38
  247. schemathesis/utils.py +0 -475
  248. schemathesis-3.15.4.dist-info/METADATA +0 -202
  249. schemathesis-3.15.4.dist-info/RECORD +0 -99
  250. schemathesis-3.15.4.dist-info/entry_points.txt +0 -7
  251. /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
+ )