schemathesis 4.0.16__py3-none-any.whl → 4.0.17__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -597,12 +597,49 @@ def cover_schema_iter(
597
597
  with _ignore_unfixable():
598
598
  canonical = canonicalish(schema)
599
599
  yield from cover_schema_iter(nctx, canonical, seen)
600
- elif key == "anyOf" or key == "oneOf":
600
+ elif key == "anyOf":
601
601
  nctx = ctx.with_negative()
602
- # NOTE: Other sub-schemas are not filtered out
602
+ validators = [jsonschema.validators.validator_for(sub_schema)(sub_schema) for sub_schema in value]
603
603
  for idx, sub_schema in enumerate(value):
604
604
  with nctx.at(idx):
605
- yield from cover_schema_iter(nctx, sub_schema, seen)
605
+ for value in cover_schema_iter(nctx, sub_schema, seen):
606
+ # Negative value for this schema could be a positive value for another one
607
+ if is_valid_for_others(value.value, idx, validators):
608
+ continue
609
+ yield value
610
+ elif key == "oneOf":
611
+ nctx = ctx.with_negative()
612
+ validators = [jsonschema.validators.validator_for(sub_schema)(sub_schema) for sub_schema in value]
613
+ for idx, sub_schema in enumerate(value):
614
+ with nctx.at(idx):
615
+ for value in cover_schema_iter(nctx, sub_schema, seen):
616
+ if is_invalid_for_oneOf(value.value, idx, validators):
617
+ yield value
618
+
619
+
620
+ def is_valid_for_others(value: Any, idx: int, validators: list[jsonschema.Validator]) -> bool:
621
+ for vidx, validator in enumerate(validators):
622
+ if idx == vidx:
623
+ # This one is being negated
624
+ continue
625
+ if validator.is_valid(value):
626
+ return True
627
+ return False
628
+
629
+
630
+ def is_invalid_for_oneOf(value: Any, idx: int, validators: list[jsonschema.Validator]) -> bool:
631
+ valid_count = 0
632
+ for vidx, validator in enumerate(validators):
633
+ if idx == vidx:
634
+ # This one is being negated
635
+ continue
636
+ if validator.is_valid(value):
637
+ valid_count += 1
638
+ # Should circuit - no need to validate more, it is already invalid
639
+ if valid_count > 1:
640
+ return True
641
+ # No matching at all - we successfully generated invalid value
642
+ return valid_count == 0
606
643
 
607
644
 
608
645
  def _get_properties(schema: dict | bool) -> dict | bool:
@@ -4,6 +4,7 @@ from dataclasses import dataclass
4
4
  from functools import lru_cache
5
5
  from typing import TYPE_CHECKING, Any, Callable, Iterator
6
6
 
7
+ import jsonschema
7
8
  from hypothesis import strategies as st
8
9
  from hypothesis.stateful import Bundle, Rule, precondition, rule
9
10
 
@@ -14,6 +15,7 @@ from schemathesis.engine.recorder import ScenarioRecorder
14
15
  from schemathesis.generation import GenerationMode
15
16
  from schemathesis.generation.case import Case
16
17
  from schemathesis.generation.hypothesis import strategies
18
+ from schemathesis.generation.meta import ComponentInfo, ComponentKind
17
19
  from schemathesis.generation.stateful import STATEFUL_TESTS_LABEL
18
20
  from schemathesis.generation.stateful.state_machine import APIStateMachine, StepInput, StepOutput, _normalize_name
19
21
  from schemathesis.schemas import APIOperation
@@ -281,6 +283,15 @@ def into_step_input(
281
283
  case.body = {**case.body, **new}
282
284
  else:
283
285
  case.body = new
286
+ if case.meta and case.meta.generation.mode == GenerationMode.NEGATIVE:
287
+ # It is possible that the new body is now valid and the whole test case could be valid too
288
+ for alternative in case.operation.body:
289
+ if alternative.media_type == case.media_type:
290
+ schema = alternative.as_json_schema(case.operation)
291
+ if jsonschema.validators.validator_for(schema)(schema).is_valid(new):
292
+ case.meta.components[ComponentKind.BODY] = ComponentInfo(mode=GenerationMode.POSITIVE)
293
+ if all(info.mode == GenerationMode.POSITIVE for info in case.meta.components.values()):
294
+ case.meta.generation.mode = GenerationMode.POSITIVE
284
295
  return StepInput(case=case, transition=transition)
285
296
 
286
297
  return inner(output=_output)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.0.16
3
+ Version: 4.0.17
4
4
  Summary: Property-based testing framework for Open API and GraphQL based apps
5
5
  Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
6
6
  Project-URL: Changelog, https://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
@@ -85,7 +85,7 @@ schemathesis/engine/phases/unit/_executor.py,sha256=9MmZoKSBVSPk0LWwN3PZ3iaO9nzp
85
85
  schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
86
86
  schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
87
87
  schemathesis/generation/case.py,sha256=zwAwFQ-Fp7SOxCXYOQyAdwAtNwVJe63PdLpvqackFQY,12296
88
- schemathesis/generation/coverage.py,sha256=jpl1GpAGZXyoO2aZLUZlIE0h_ygdHMnvUK14oBg51k4,53896
88
+ schemathesis/generation/coverage.py,sha256=2GZIyVfaZ8wAcF1GQohm9pftwNIVeeu5uWhYp8qHKlA,55606
89
89
  schemathesis/generation/meta.py,sha256=adkoMuCfzSjHJ9ZDocQn0GnVldSCkLL3eVR5A_jafwM,2552
90
90
  schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz5-kt4,2836
91
91
  schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
@@ -148,7 +148,7 @@ schemathesis/specs/openapi/negative/__init__.py,sha256=1sajF22SrE4pUK7-C6PiseZ9P
148
148
  schemathesis/specs/openapi/negative/mutations.py,sha256=xDSUVnGWjuuIcvmW_mJGChf-G-nXst-JBX1okQAzon4,19865
149
149
  schemathesis/specs/openapi/negative/types.py,sha256=a7buCcVxNBG6ILBM3A7oNTAX0lyDseEtZndBuej8MbI,174
150
150
  schemathesis/specs/openapi/negative/utils.py,sha256=ozcOIuASufLqZSgnKUACjX-EOZrrkuNdXX0SDnLoGYA,168
151
- schemathesis/specs/openapi/stateful/__init__.py,sha256=FSitLbJxjBYU814cqjI_QCkdyoIc-9xfT_1HQcYwsXw,15064
151
+ schemathesis/specs/openapi/stateful/__init__.py,sha256=t5O84nKXIk_NhFihuSip3EWMLBo4_AG95W_YVbTKPl4,15985
152
152
  schemathesis/specs/openapi/stateful/control.py,sha256=QaXLSbwQWtai5lxvvVtQV3BLJ8n5ePqSKB00XFxp-MA,3695
153
153
  schemathesis/specs/openapi/stateful/links.py,sha256=h5q40jUbcIk5DS_Tih1cvFJxS_QxxG0_9ZQnTs1A_zo,8806
154
154
  schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
@@ -157,8 +157,8 @@ schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEz
157
157
  schemathesis/transport/requests.py,sha256=46aplzhSmBupegPNMawma-iJWCegWkEd6mzdWLTpgM4,10742
158
158
  schemathesis/transport/serialization.py,sha256=igUXKZ_VJ9gV7P0TUc5PDQBJXl_s0kK9T3ljGWWvo6E,10339
159
159
  schemathesis/transport/wsgi.py,sha256=KoAfvu6RJtzyj24VGB8e-Iaa9smpgXJ3VsM8EgAz2tc,6152
160
- schemathesis-4.0.16.dist-info/METADATA,sha256=Cy4KK06pQyWGPssr3PsSFtofmnMdsSgSNNjy3LHoxuw,8472
161
- schemathesis-4.0.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
- schemathesis-4.0.16.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
- schemathesis-4.0.16.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
- schemathesis-4.0.16.dist-info/RECORD,,
160
+ schemathesis-4.0.17.dist-info/METADATA,sha256=W-ng4zywYAQxqVAdxHO-m6DAHhBnE5jGSgQIhx04uTY,8472
161
+ schemathesis-4.0.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
+ schemathesis-4.0.17.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
+ schemathesis-4.0.17.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
+ schemathesis-4.0.17.dist-info/RECORD,,