#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_annotation(doc_path); } MATCHER_P2(HasAnnotationAt, doc_path, schema_path, "") { return arg.has_annotation(doc_path, schema_path); } MATCHER_P3(AnnotationAt, doc_path, schema_path, matcher, "") { auto const * anno = arg.annotation(doc_path, schema_path); 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(""_jptr, "/format"_jptr, "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, AnnotationAt(""_jptr, "/minimum"_jptr, "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, AnnotationAt(""_jptr, "/not/minimum"_jptr, "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, AnnotationAt(""_jptr, "/disallow"_jptr, "type (string) is one of [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, AnnotationAt(""_jptr, "/oneOf/0/minimum"_jptr, "-1 < 10")); EXPECT_THAT(result, AnnotationAt(""_jptr, "/oneOf/1/maximum"_jptr, "-1 <= 0")); } int main(int argc, char ** argv) { testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); }