def test_merge_semantics(data, s1, s2): assume(canonicalish(s1) != FALSEY and canonicalish(s2) != FALSEY) combined = merged([s1, s2]) assume(combined is not None) assert combined == merged([s2, s1]) # union is commutative assume(combined != FALSEY) _merge_semantics_helper(data, s1, s2, combined)
def test_canonicalises_to_equivalent_fixpoint(schema_strategy, data): """Check that an object drawn from an arbitrary schema is valid.""" schema = data.draw(schema_strategy, label="schema") cc = canonicalish(schema) assert cc == canonicalish(cc) instance = data.draw(JSON_STRATEGY | from_schema(cc), label="instance") assert is_valid(instance, schema) == is_valid(instance, cc) jsonschema.validators.validator_for(schema).check_schema(schema)
def test_can_almost_always_merge_numeric_schemas(data, s1, s2): assume(canonicalish(s1) != FALSEY and canonicalish(s2) != FALSEY) combined = merged([s1, s2]) if combined is None: # The ONLY case in which we can't merge numeric schemas is when # they both contain multipleOf keys with distinct non-integer values. mul1, mul2 = s1["multipleOf"], s2["multipleOf"] assert isinstance(mul1, float) or isinstance(mul2, float) assert mul1 != mul2 elif combined != FALSEY: _merge_semantics_helper(data, s1, s2, combined)
def test_dependencies_canonicalises_to_fixpoint(): """Check that an object drawn from an arbitrary schema is valid.""" cc = canonicalish({ "required": [""], "properties": { "": {} }, "dependencies": { "": [""] } }) assert cc == canonicalish(cc)
def test_merge_semantics(data, s1, s2): assume(canonicalish(s1) != FALSEY and canonicalish(s2) != FALSEY) combined = merged([s1, s2]) assume(combined is not None) assume(combined != FALSEY) note(combined) ic = data.draw(from_schema(combined), label="combined") i1 = data.draw(from_schema(s1), label="s1") i2 = data.draw(from_schema(s2), label="s2") assert is_valid(ic, s1) and is_valid(ic, s2) assert is_valid(i1, s2) == is_valid(i1, combined) assert is_valid(i2, s1) == is_valid(i2, combined)
def test_canonicalises_to_equivalent_fixpoint(schema_strategy, data): """Check that an object drawn from an arbitrary schema is valid.""" schema = data.draw(schema_strategy, label="schema") cc = canonicalish(schema) assert cc == canonicalish(cc) try: strat = from_schema(cc) except InvalidArgument: # e.g. array of unique {type: integers}, with too few allowed integers assume(False) instance = data.draw(JSON_STRATEGY | strat, label="instance") assert is_valid(instance, schema) == is_valid(instance, cc) jsonschema.validators.validator_for(schema).check_schema(schema)
def _canonicalises_to_equivalent_fixpoint(data): # This function isn't executed by pytest, only by FuzzBuzz - we want to parametrize # over schemas for differnt types there, but have to supply *all* args here. schema = data.draw(json_schemata(), label="schema") cc = canonicalish(schema) assert cc == canonicalish(cc) try: strat = from_schema(cc) except InvalidArgument: # e.g. array of unique {type: integers}, with too few allowed integers assume(False) instance = data.draw(JSON_STRATEGY | strat, label="instance") assert is_valid(instance, schema) == is_valid(instance, cc) jsonschema.validators.validator_for(schema).check_schema(schema)
def test_generated_data_matches_schema(schema_strategy, data): """Check that an object drawn from an arbitrary schema is valid.""" schema = data.draw(schema_strategy) note(schema) try: value = data.draw(from_schema(schema), "value from schema") except InvalidArgument: reject() jsonschema.validate(value, schema) # This checks that our canonicalisation is semantically equivalent. jsonschema.validate(value, canonicalish(schema))
def test_validators_use_proper_draft(): # See GH-66 schema = { "$schema": "http://json-schema.org/draft-04/schema#", "not": { "allOf": [ {"exclusiveMinimum": True, "minimum": 0}, {"exclusiveMaximum": True, "maximum": 10}, ] }, } cc = canonicalish(schema) jsonschema.validators.validator_for(cc).check_schema(cc)
def test_reference_resolver_issue_65_regression(): schema = { "allOf": [{"$ref": "#/definitions/ref"}, {"required": ["foo"]}], "properties": {"foo": {}}, "definitions": {"ref": {"maxProperties": 1}}, "type": "object", } res = resolve_all_refs(schema) can = canonicalish(res) assert "$ref" not in res assert "$ref" not in can for s in (schema, res, can): with pytest.raises(jsonschema.ValidationError): jsonschema.validate({}, s)
def test_generated_data_matches_schema(schema_strategy, data): """Check that an object drawn from an arbitrary schema is valid.""" schema = data.draw(schema_strategy) note(schema) try: value = data.draw(from_schema(schema), "value from schema") except InvalidArgument: reject() try: jsonschema.validate(value, schema) # This checks that our canonicalisation is semantically equivalent. jsonschema.validate(value, canonicalish(schema)) except jsonschema.ValidationError as err: if "'uniqueItems': True" in str(err): pytest.xfail("https://github.com/Julian/jsonschema/issues/686") raise
def negate_constraints(context: MutationContext, draw: Draw, schema: Schema) -> MutationResult: """Negate schema constrains while keeping the original type.""" if canonicalish(schema) == {}: return MutationResult.FAILURE copied = schema.copy() schema.clear() is_negated = False def is_mutation_candidate(k: str) -> bool: # Should we negate this key? return not (k in ("type", "properties", "items", "minItems") or (k == "additionalProperties" and context.is_header_location)) enabled_keywords = draw(st.shared(FeatureStrategy(), key="keywords")) # type: ignore candidates = [] mutation_candidates = [key for key in copied if is_mutation_candidate(key)] if mutation_candidates: # There should be at least one mutated keyword candidate = draw( st.sampled_from( [key for key in copied if is_mutation_candidate(key)])) candidates.append(candidate) # If the chosen candidate has dependency, then the dependency should also be present in the final schema if candidate in DEPENDENCIES: candidates.append(DEPENDENCIES[candidate]) for key, value in copied.items(): if is_mutation_candidate(key): if key in candidates or enabled_keywords.is_enabled(key): is_negated = True negated = schema.setdefault("not", {}) negated[key] = value if key in DEPENDENCIES: # If this keyword has a dependency, then it should be also negated dependency = DEPENDENCIES[key] if dependency not in negated: negated[dependency] = copied[ dependency] # Assuming the schema is valid else: schema[key] = value if is_negated: return MutationResult.SUCCESS return MutationResult.FAILURE
def test_prevent_unsatisfiable_schema(schema, new_type): prevent_unsatisfiable_schema(schema, new_type) assert canonicalish(schema) != FALSEY
def test_canonicalise_is_only_valid_for_schemas(): with pytest.raises(InvalidArgument): canonicalish("not a schema")
def test_self_merge_eq_canonicalish(schema): m = merged([schema, schema]) assert m == canonicalish(schema)
def test_no_unsatisfiable_schemas(data): schema = {"type": "object", "required": ["foo"]} mutated_schema = data.draw( mutated(schema, location="body", media_type="application/json")) assert canonicalish(mutated_schema) != FALSEY
def test_canonicalises_to_expected(schema, expected): assert canonicalish(schema) == expected, (schema, canonicalish(schema), expected)
def test_canonicalises_to_empty(schema): assert canonicalish(schema) == {"not": {}}, (schema, canonicalish(schema))