#include #include #include #include #include #include #include #include #include #include #include #include #include #include using enum jvalidate::schema::Version; using testing::Not; auto operator""_jptr(char const * data, size_t len) { return jvalidate::detail::Pointer(std::string_view{data, len}); } Json::Value operator""_json(char const * data, size_t len) { Json::Value value; Json::CharReaderBuilder builder; std::unique_ptr reader(builder.newCharReader()); std::string error; if (not reader->parse(data, data + len, &value, &error)) { throw std::runtime_error(error); } return value; } auto validate(Json::Value const & schema_doc, Json::Value const & instance_doc, jvalidate::schema::Version version = Draft2020_12) { jvalidate::Schema const schema(schema_doc, version); jvalidate::ValidationResult result; (void)jvalidate::Validator(schema).validate(instance_doc, &result); return result; } MATCHER_P(HasAnnotationsFor, doc_path, "") { return arg.has(doc_path); } MATCHER_P2(HasAnnotationAt, doc_path, schema_path, "") { return arg.has(doc_path, schema_path); } MATCHER_P2(AnnotationAt, key, matcher, "") { auto const * anno = arg.annotation({}, {}, key); if (not anno) { return false; } return testing::ExplainMatchResult(matcher, *anno, result_listener); } MATCHER_P4(AnnotationAt, doc_path, schema_path, key, matcher, "") { auto const * anno = arg.annotation(doc_path, schema_path, key); if (not anno) { return false; } return testing::ExplainMatchResult(matcher, *anno, result_listener); } MATCHER_P2(ErrorAt, key, matcher, "") { auto const * anno = arg.error({}, {}, key); if (not anno) { return false; } return testing::ExplainMatchResult(matcher, *anno, result_listener); } MATCHER_P4(ErrorAt, doc_path, schema_path, key, matcher, "") { auto const * anno = arg.error(doc_path, schema_path, key); if (not anno) { return false; } return testing::ExplainMatchResult(matcher, *anno, result_listener); } TEST(Annotation, AttachesFormattingAnnotation) { auto const schema = R"({ "format": "uri" })"_json; auto const instance = R"("http://json-schema.org")"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, AnnotationAt("format", "uri")); } TEST(Annotation, AnnotatesErrors) { auto const schema = R"({ "minimum": 5 })"_json; auto const instance = R"(4)"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, ErrorAt("minimum", "4 < 5")); } TEST(Annotation, DoesNotAnnotatesValid) { auto const schema = R"({ "minimum": 5 })"_json; auto const instance = R"(6)"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, Not(HasAnnotationsFor(""_jptr))); } TEST(Annotation, NotSchemaFlipsAnnotationRule) { auto const schema = R"({ "not": { "minimum": 5 } })"_json; auto const instance = R"(6)"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, ErrorAt(""_jptr, "/not"_jptr, "minimum", "6 >= 5")); } TEST(Annotation, PathFollowsSchemaNotConstraintModel) { auto const schema = R"({ "$comment": "disallow is implemented in the form of NotConstraint[TypeConstraint]", "disallow": "string" })"_json; auto const instance = R"("hello")"_json; jvalidate::ValidationResult result = validate(schema, instance, Draft03); EXPECT_THAT(result, ErrorAt("disallow", "string is in types [string]")); } TEST(Annotation, SomeConstraintsAnnotateBothValidAndInvalid) { auto const schema = R"({ "$comment": "accepts any number <= 0 or >= 10", "oneOf": [ { "minimum": 10 }, { "maximum": 0 } ] })"_json; auto const instance = R"(-1)"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, Not(HasAnnotationAt(""_jptr, "/oneOf"_jptr))); EXPECT_THAT(result, ErrorAt(""_jptr, "/oneOf/0"_jptr, "minimum", "-1 < 10")); EXPECT_THAT(result, ErrorAt(""_jptr, "/oneOf/1"_jptr, "maximum", "-1 <= 0")); } TEST(Annotation, AttachesAlwaysFalseSensibly) { auto const schema = R"({ "properties": { "A": false } })"_json; auto const instance = R"({ "A": null })"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, ErrorAt("/A"_jptr, "/properties"_jptr, "", "always false")); } int main(int argc, char ** argv) { testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); }