schemathesis 4.3.8__py3-none-any.whl → 4.3.10__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/core/jsonschema/keywords.py +40 -38
- schemathesis/generation/coverage.py +32 -5
- schemathesis/generation/hypothesis/builder.py +0 -1
- schemathesis/specs/openapi/stateful/dependencies/naming.py +34 -11
- schemathesis/specs/openapi/stateful/dependencies/schemas.py +2 -2
- {schemathesis-4.3.8.dist-info → schemathesis-4.3.10.dist-info}/METADATA +1 -1
- {schemathesis-4.3.8.dist-info → schemathesis-4.3.10.dist-info}/RECORD +10 -10
- {schemathesis-4.3.8.dist-info → schemathesis-4.3.10.dist-info}/WHEEL +0 -0
- {schemathesis-4.3.8.dist-info → schemathesis-4.3.10.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.3.8.dist-info → schemathesis-4.3.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
ALL_KEYWORDS =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
ALL_KEYWORDS = frozenset(
|
|
2
|
+
{
|
|
3
|
+
"additionalItems",
|
|
4
|
+
"additionalProperties",
|
|
5
|
+
"allOf",
|
|
6
|
+
"anyOf",
|
|
7
|
+
"const",
|
|
8
|
+
"contains",
|
|
9
|
+
"contentEncoding",
|
|
10
|
+
"contentMediaType",
|
|
11
|
+
"dependencies",
|
|
12
|
+
"enum",
|
|
13
|
+
"else",
|
|
14
|
+
"exclusiveMaximum",
|
|
15
|
+
"exclusiveMinimum",
|
|
16
|
+
"format",
|
|
17
|
+
"if",
|
|
18
|
+
"items",
|
|
19
|
+
"maxItems",
|
|
20
|
+
"maxLength",
|
|
21
|
+
"maxProperties",
|
|
22
|
+
"maximum",
|
|
23
|
+
"minItems",
|
|
24
|
+
"minLength",
|
|
25
|
+
"minProperties",
|
|
26
|
+
"minimum",
|
|
27
|
+
"multipleOf",
|
|
28
|
+
"not",
|
|
29
|
+
"oneOf",
|
|
30
|
+
"pattern",
|
|
31
|
+
"patternProperties",
|
|
32
|
+
"properties",
|
|
33
|
+
"propertyNames",
|
|
34
|
+
"$ref",
|
|
35
|
+
"required",
|
|
36
|
+
"then",
|
|
37
|
+
"type",
|
|
38
|
+
"uniqueItems",
|
|
39
|
+
}
|
|
40
|
+
)
|
|
@@ -7,6 +7,10 @@ from dataclasses import dataclass
|
|
|
7
7
|
from functools import lru_cache, partial
|
|
8
8
|
from itertools import combinations
|
|
9
9
|
|
|
10
|
+
from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY
|
|
11
|
+
from schemathesis.core.jsonschema.keywords import ALL_KEYWORDS
|
|
12
|
+
from schemathesis.generation.hypothesis.strategies import combine
|
|
13
|
+
|
|
10
14
|
try:
|
|
11
15
|
from json.encoder import _make_iterencode # type: ignore[attr-defined]
|
|
12
16
|
except ImportError:
|
|
@@ -72,6 +76,14 @@ STRATEGIES_FOR_TYPE = {
|
|
|
72
76
|
"array": ARRAY_STRATEGY,
|
|
73
77
|
"object": OBJECT_STRATEGY,
|
|
74
78
|
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_strategy_for_type(ty: str | list[str]) -> st.SearchStrategy:
|
|
82
|
+
if isinstance(ty, str):
|
|
83
|
+
return STRATEGIES_FOR_TYPE[ty]
|
|
84
|
+
return combine([STRATEGIES_FOR_TYPE[t] for t in ty if t in STRATEGIES_FOR_TYPE])
|
|
85
|
+
|
|
86
|
+
|
|
75
87
|
FORMAT_STRATEGIES = {**BUILT_IN_STRING_FORMATS, **get_default_format_strategies(), **STRING_FORMATS}
|
|
76
88
|
|
|
77
89
|
UNKNOWN_PROPERTY_KEY = "x-schemathesis-unknown-property"
|
|
@@ -267,11 +279,11 @@ class CoverageContext:
|
|
|
267
279
|
if isinstance(schema, bool):
|
|
268
280
|
return 0
|
|
269
281
|
keys = sorted([k for k in schema if not k.startswith("x-") and k not in ["description", "example", "examples"]])
|
|
270
|
-
if keys == ["type"]
|
|
271
|
-
return cached_draw(
|
|
282
|
+
if keys == ["type"]:
|
|
283
|
+
return cached_draw(get_strategy_for_type(schema["type"]))
|
|
272
284
|
if keys == ["format", "type"]:
|
|
273
285
|
if schema["type"] != "string":
|
|
274
|
-
return cached_draw(
|
|
286
|
+
return cached_draw(get_strategy_for_type(schema["type"]))
|
|
275
287
|
elif schema["format"] in FORMAT_STRATEGIES:
|
|
276
288
|
return cached_draw(FORMAT_STRATEGIES[schema["format"]])
|
|
277
289
|
if (keys == ["maxLength", "minLength", "type"] or keys == ["maxLength", "type"]) and schema["type"] == "string":
|
|
@@ -346,6 +358,19 @@ class CoverageContext:
|
|
|
346
358
|
if isinstance(schema, dict) and "examples" in schema:
|
|
347
359
|
# Examples may contain binary data which will fail the canonicalisation process in `hypothesis-jsonschema`
|
|
348
360
|
schema = {key: value for key, value in schema.items() if key != "examples"}
|
|
361
|
+
# Prevent some hard to satisfy schemas
|
|
362
|
+
if isinstance(schema, dict) and schema.get("additionalProperties") is False and "required" in schema:
|
|
363
|
+
# Set required properties to any value to simplify generation
|
|
364
|
+
schema = dict(schema)
|
|
365
|
+
properties = schema.setdefault("properties", {})
|
|
366
|
+
for key in schema["required"]:
|
|
367
|
+
properties.setdefault(key, {})
|
|
368
|
+
|
|
369
|
+
# Add bundled schemas if any
|
|
370
|
+
if isinstance(schema, dict) and BUNDLE_STORAGE_KEY in self.root_schema:
|
|
371
|
+
schema = dict(schema)
|
|
372
|
+
schema[BUNDLE_STORAGE_KEY] = self.root_schema[BUNDLE_STORAGE_KEY]
|
|
373
|
+
|
|
349
374
|
return self.generate_from(from_schema(schema, custom_formats=self.custom_formats))
|
|
350
375
|
|
|
351
376
|
|
|
@@ -490,12 +515,14 @@ def cover_schema_iter(
|
|
|
490
515
|
# Can't resolve a reference - at this point, we can't generate anything useful as `$ref` is in the current schema root
|
|
491
516
|
return
|
|
492
517
|
|
|
493
|
-
if schema
|
|
518
|
+
if schema is True:
|
|
494
519
|
types = ["null", "boolean", "string", "number", "array", "object"]
|
|
495
520
|
schema = {}
|
|
496
521
|
elif schema is False:
|
|
497
522
|
types = []
|
|
498
523
|
schema = {"not": {}}
|
|
524
|
+
elif not any(k in ALL_KEYWORDS for k in schema):
|
|
525
|
+
types = ["null", "boolean", "string", "number", "array", "object"]
|
|
499
526
|
else:
|
|
500
527
|
types = schema.get("type", [])
|
|
501
528
|
push_examples_to_properties(schema)
|
|
@@ -1305,7 +1332,7 @@ def _negative_type(
|
|
|
1305
1332
|
}.get(ctx.location)
|
|
1306
1333
|
|
|
1307
1334
|
if "number" in types:
|
|
1308
|
-
|
|
1335
|
+
strategies.pop("integer", None)
|
|
1309
1336
|
if "integer" in types:
|
|
1310
1337
|
strategies["number"] = FLOAT_STRATEGY.filter(_is_non_integer_float)
|
|
1311
1338
|
if ctx.location == ParameterLocation.QUERY:
|
|
@@ -529,7 +529,6 @@ def _iter_coverage_cases(
|
|
|
529
529
|
|
|
530
530
|
instant = Instant()
|
|
531
531
|
responses = list(operation.responses.iter_examples())
|
|
532
|
-
# NOTE: The HEAD method is excluded
|
|
533
532
|
custom_formats = _build_custom_formats(generation_config)
|
|
534
533
|
|
|
535
534
|
seen_negative = coverage.HashSet()
|
|
@@ -2,16 +2,35 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def from_parameter(parameter: str, path: str) -> str | None:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Named like "user_id" -> look for "User" resource
|
|
10
|
-
elif parameter.endswith("_id"):
|
|
11
|
-
return to_pascal_case(parameter[:-3])
|
|
12
|
-
# Just "id" -> infer from path context
|
|
13
|
-
elif parameter == "id":
|
|
5
|
+
parameter = parameter.strip()
|
|
6
|
+
lower = parameter.lower()
|
|
7
|
+
|
|
8
|
+
if lower == "id":
|
|
14
9
|
return from_path(path, parameter_name=parameter)
|
|
10
|
+
|
|
11
|
+
# Capital-sensitive
|
|
12
|
+
capital_suffixes = ("Id", "Uuid", "Guid")
|
|
13
|
+
for suffix in capital_suffixes:
|
|
14
|
+
if parameter.endswith(suffix):
|
|
15
|
+
prefix = parameter[: -len(suffix)]
|
|
16
|
+
if len(prefix) >= 2:
|
|
17
|
+
return to_pascal_case(prefix)
|
|
18
|
+
|
|
19
|
+
# Snake_case (case-insensitive is fine here)
|
|
20
|
+
snake_suffixes = ("_guid", "_uuid", "_id", "-guid", "-uuid", "-id")
|
|
21
|
+
for suffix in snake_suffixes:
|
|
22
|
+
if lower.endswith(suffix):
|
|
23
|
+
prefix = parameter[: -len(suffix)]
|
|
24
|
+
if len(prefix) >= 2:
|
|
25
|
+
return to_pascal_case(prefix)
|
|
26
|
+
|
|
27
|
+
# Special cases that need exact match
|
|
28
|
+
# Twilio-style, capital S
|
|
29
|
+
if parameter.endswith("Sid"):
|
|
30
|
+
prefix = parameter[:-3]
|
|
31
|
+
if len(prefix) >= 2:
|
|
32
|
+
return to_pascal_case(prefix)
|
|
33
|
+
|
|
15
34
|
return None
|
|
16
35
|
|
|
17
36
|
|
|
@@ -335,8 +354,12 @@ def to_plural(word: str) -> str:
|
|
|
335
354
|
|
|
336
355
|
|
|
337
356
|
def to_pascal_case(text: str) -> str:
|
|
338
|
-
|
|
339
|
-
|
|
357
|
+
# snake_case/kebab-case - split and capitalize each word
|
|
358
|
+
if "_" in text or "-" in text:
|
|
359
|
+
parts = text.replace("-", "_").split("_")
|
|
360
|
+
return "".join(word.capitalize() for word in parts if word)
|
|
361
|
+
# camelCase - just uppercase first letter, preserve the rest
|
|
362
|
+
return text[0].upper() + text[1:] if text else text
|
|
340
363
|
|
|
341
364
|
|
|
342
365
|
def to_snake_case(text: str) -> str:
|
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Mapping
|
|
|
5
5
|
|
|
6
6
|
from schemathesis.core.jsonschema import ALL_KEYWORDS
|
|
7
7
|
from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY, bundle
|
|
8
|
-
from schemathesis.core.jsonschema.types import JsonSchema, JsonSchemaObject
|
|
8
|
+
from schemathesis.core.jsonschema.types import JsonSchema, JsonSchemaObject, get_type
|
|
9
9
|
from schemathesis.core.transforms import encode_pointer
|
|
10
10
|
from schemathesis.specs.openapi.adapter.parameters import resource_name_from_ref
|
|
11
11
|
from schemathesis.specs.openapi.adapter.references import maybe_resolve
|
|
@@ -425,7 +425,7 @@ def _detect_externally_tagged_pattern(schema: Mapping[str, Any], path: str) -> s
|
|
|
425
425
|
if name.lower() not in possible_names:
|
|
426
426
|
continue
|
|
427
427
|
|
|
428
|
-
if isinstance(subschema, dict):
|
|
428
|
+
if isinstance(subschema, dict) and "object" in get_type(subschema):
|
|
429
429
|
return name
|
|
430
430
|
|
|
431
431
|
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.10
|
|
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
|
|
@@ -70,7 +70,7 @@ schemathesis/core/validation.py,sha256=b0USkKzkWvdz3jOW1JXYc_TfYshfKZeP7xAUnMqcN
|
|
|
70
70
|
schemathesis/core/version.py,sha256=dOBUWrY3-uA2NQXJp9z7EtZgkR6jYeLg8sMhQCL1mcI,205
|
|
71
71
|
schemathesis/core/jsonschema/__init__.py,sha256=gBZGsXIpK2EFfcp8x0b69dqzWAm2OeZHepKImkkLvoE,320
|
|
72
72
|
schemathesis/core/jsonschema/bundler.py,sha256=a8cQMJ7EQe3eWvS3haS2ZpxOUwzA4PY4OLI0Uyx045A,8472
|
|
73
|
-
schemathesis/core/jsonschema/keywords.py,sha256=
|
|
73
|
+
schemathesis/core/jsonschema/keywords.py,sha256=ssys1lgo4T_-B0VjWTAp91cga1wxwufxlb3-MI75ioY,796
|
|
74
74
|
schemathesis/core/jsonschema/references.py,sha256=c2Q4IKWUbwENNtkbFaqf8r3LLZu6GFE5YLnYQlg5tPg,6069
|
|
75
75
|
schemathesis/core/jsonschema/types.py,sha256=C7f9g8yKFuoxC5_0YNIh8QAyGU0-tj8pzTMfMDjjjVM,1248
|
|
76
76
|
schemathesis/core/output/__init__.py,sha256=SiHqONFskXl73AtP5dV29L14nZoKo7B-IeG52KZB32M,1446
|
|
@@ -93,13 +93,13 @@ schemathesis/engine/phases/unit/_executor.py,sha256=YDibV3lkC2UMHLvh1FSmnlaQ-SJS
|
|
|
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=
|
|
96
|
+
schemathesis/generation/coverage.py,sha256=uUUND4zF17hqs9L1VtNz41KtxcW-BB9JDsrNBA-w2-w,60813
|
|
97
97
|
schemathesis/generation/meta.py,sha256=tXhUZBEdpQMn68uMx1SW8Vv59Uf6Wl6yzs-VB9lu_8o,2589
|
|
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=wOCjNAScpiN-wJ4EM0QnJ_5o9nczW0WILFNfIR1qeKQ,38480
|
|
103
103
|
schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
|
|
104
104
|
schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
|
|
105
105
|
schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
|
|
@@ -167,10 +167,10 @@ schemathesis/specs/openapi/stateful/links.py,sha256=SSA66mU50FFBz7e6sA37CfL-Vt0O
|
|
|
167
167
|
schemathesis/specs/openapi/stateful/dependencies/__init__.py,sha256=0JM-FrY6Awv6gl-qDHaaK7pXbt_GKutBKPyIaph8apA,7842
|
|
168
168
|
schemathesis/specs/openapi/stateful/dependencies/inputs.py,sha256=PSactImp4OqsYMHUl2gB2pgvUlZCCKJRJKeaalclFzU,11511
|
|
169
169
|
schemathesis/specs/openapi/stateful/dependencies/models.py,sha256=Kl482Hwq2M8lYAdqGmf_8Yje3voSj1WLDUIujRUDWDQ,12286
|
|
170
|
-
schemathesis/specs/openapi/stateful/dependencies/naming.py,sha256=
|
|
170
|
+
schemathesis/specs/openapi/stateful/dependencies/naming.py,sha256=NnXEFY1W3i18jEEYGgC_8oLoE7YOxdXgcMYtZvLj10w,12920
|
|
171
171
|
schemathesis/specs/openapi/stateful/dependencies/outputs.py,sha256=zvVUfQWNIuhMkKDpz5hsVGkkvkefLt1EswpJAnHajOw,1186
|
|
172
172
|
schemathesis/specs/openapi/stateful/dependencies/resources.py,sha256=p58XoADpMKFAun0Bx_rul-kiUlfA9PXjxHJ97dT2tBE,11202
|
|
173
|
-
schemathesis/specs/openapi/stateful/dependencies/schemas.py,sha256=
|
|
173
|
+
schemathesis/specs/openapi/stateful/dependencies/schemas.py,sha256=RaG1BJH4D7-o5Qs2rIRQvS8ERntMUEs2I5jXUFaKMRo,14147
|
|
174
174
|
schemathesis/specs/openapi/types/__init__.py,sha256=VPsWtLJle__Kodw_QqtQ3OuvBzBcCIKsTOrXy3eA7OU,66
|
|
175
175
|
schemathesis/specs/openapi/types/v3.py,sha256=Vondr9Amk6JKCIM6i6RGcmTUjFfPgOOqzBXqerccLpo,1468
|
|
176
176
|
schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
|
|
@@ -179,8 +179,8 @@ schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEz
|
|
|
179
179
|
schemathesis/transport/requests.py,sha256=wriRI9fprTplE_qEZLEz1TerX6GwkE3pwr6ZnU2o6vQ,10648
|
|
180
180
|
schemathesis/transport/serialization.py,sha256=GwO6OAVTmL1JyKw7HiZ256tjV4CbrRbhQN0ep1uaZwI,11157
|
|
181
181
|
schemathesis/transport/wsgi.py,sha256=kQtasFre6pjdJWRKwLA_Qb-RyQHCFNpaey9ubzlFWKI,5907
|
|
182
|
-
schemathesis-4.3.
|
|
183
|
-
schemathesis-4.3.
|
|
184
|
-
schemathesis-4.3.
|
|
185
|
-
schemathesis-4.3.
|
|
186
|
-
schemathesis-4.3.
|
|
182
|
+
schemathesis-4.3.10.dist-info/METADATA,sha256=BrfLLUJxe-ve_0xCRM77F5ksA27XYwnQaS_tf7pawQM,8541
|
|
183
|
+
schemathesis-4.3.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
184
|
+
schemathesis-4.3.10.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
|
|
185
|
+
schemathesis-4.3.10.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
|
|
186
|
+
schemathesis-4.3.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|