annotation_test.cxx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #include <string_view>
  2. #include <gmock/gmock.h>
  3. #include <gtest/gtest.h>
  4. #include <jvalidate/adapter.h>
  5. #include <jvalidate/adapters/jsoncpp.h>
  6. #include <jvalidate/detail/pointer.h>
  7. #include <jvalidate/enum.h>
  8. #include <jvalidate/schema.h>
  9. #include <jvalidate/status.h>
  10. #include <jvalidate/uri.h>
  11. #include <jvalidate/validation_result.h>
  12. #include <jvalidate/validator.h>
  13. #include <json/reader.h>
  14. #include <json/value.h>
  15. using enum jvalidate::schema::Version;
  16. using testing::Not;
  17. auto operator""_jptr(char const * data, size_t len) {
  18. return jvalidate::detail::Pointer(std::string_view{data, len});
  19. }
  20. Json::Value operator""_json(char const * data, size_t len) {
  21. Json::Value value;
  22. Json::CharReaderBuilder builder;
  23. std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  24. std::string error;
  25. if (not reader->parse(data, data + len, &value, &error)) {
  26. throw std::runtime_error(error);
  27. }
  28. return value;
  29. }
  30. auto validate(Json::Value const & schema_doc, Json::Value const & instance_doc,
  31. jvalidate::schema::Version version = Draft2020_12) {
  32. jvalidate::Schema const schema(schema_doc, version);
  33. jvalidate::ValidationResult result;
  34. (void)jvalidate::Validator(schema).validate(instance_doc, &result);
  35. return result;
  36. }
  37. MATCHER_P(HasAnnotationsFor, doc_path, "") { return arg.has(doc_path); }
  38. MATCHER_P2(HasAnnotationAt, doc_path, schema_path, "") { return arg.has(doc_path, schema_path); }
  39. MATCHER_P2(AnnotationAt, key, matcher, "") {
  40. auto const * anno = arg.annotation({}, {}, key);
  41. if (not anno) {
  42. return false;
  43. }
  44. return testing::ExplainMatchResult(matcher, *anno, result_listener);
  45. }
  46. MATCHER_P4(AnnotationAt, doc_path, schema_path, key, matcher, "") {
  47. auto const * anno = arg.annotation(doc_path, schema_path, key);
  48. if (not anno) {
  49. return false;
  50. }
  51. return testing::ExplainMatchResult(matcher, *anno, result_listener);
  52. }
  53. MATCHER_P2(ErrorAt, key, matcher, "") {
  54. auto const * anno = arg.error({}, {}, key);
  55. if (not anno) {
  56. return false;
  57. }
  58. return testing::ExplainMatchResult(matcher, *anno, result_listener);
  59. }
  60. MATCHER_P4(ErrorAt, doc_path, schema_path, key, matcher, "") {
  61. auto const * anno = arg.error(doc_path, schema_path, key);
  62. if (not anno) {
  63. return false;
  64. }
  65. return testing::ExplainMatchResult(matcher, *anno, result_listener);
  66. }
  67. TEST(Annotation, AttachesFormattingAnnotation) {
  68. auto const schema = R"({
  69. "format": "uri"
  70. })"_json;
  71. auto const instance = R"("http://json-schema.org")"_json;
  72. jvalidate::ValidationResult result = validate(schema, instance);
  73. EXPECT_THAT(result, AnnotationAt("format", "uri"));
  74. }
  75. TEST(Annotation, AnnotatesErrors) {
  76. auto const schema = R"({
  77. "minimum": 5
  78. })"_json;
  79. auto const instance = R"(4)"_json;
  80. jvalidate::ValidationResult result = validate(schema, instance);
  81. EXPECT_THAT(result, ErrorAt("minimum", "4 < 5"));
  82. }
  83. TEST(Annotation, DoesNotAnnotatesValid) {
  84. auto const schema = R"({
  85. "minimum": 5
  86. })"_json;
  87. auto const instance = R"(6)"_json;
  88. jvalidate::ValidationResult result = validate(schema, instance);
  89. EXPECT_THAT(result, Not(HasAnnotationsFor(""_jptr)));
  90. }
  91. TEST(Annotation, NotSchemaFlipsAnnotationRule) {
  92. auto const schema = R"({
  93. "not": { "minimum": 5 }
  94. })"_json;
  95. auto const instance = R"(6)"_json;
  96. jvalidate::ValidationResult result = validate(schema, instance);
  97. EXPECT_THAT(result, ErrorAt(""_jptr, "/not"_jptr, "minimum", "6 >= 5"));
  98. }
  99. TEST(Annotation, PathFollowsSchemaNotConstraintModel) {
  100. auto const schema = R"({
  101. "$comment": "disallow is implemented in the form of NotConstraint[TypeConstraint]",
  102. "disallow": "string"
  103. })"_json;
  104. auto const instance = R"("hello")"_json;
  105. jvalidate::ValidationResult result = validate(schema, instance, Draft03);
  106. EXPECT_THAT(result, ErrorAt("disallow", "string is in types [string]"));
  107. }
  108. TEST(Annotation, SomeConstraintsAnnotateBothValidAndInvalid) {
  109. auto const schema = R"({
  110. "$comment": "accepts any number <= 0 or >= 10",
  111. "oneOf": [
  112. { "minimum": 10 },
  113. { "maximum": 0 }
  114. ]
  115. })"_json;
  116. auto const instance = R"(-1)"_json;
  117. jvalidate::ValidationResult result = validate(schema, instance);
  118. EXPECT_THAT(result, Not(HasAnnotationAt(""_jptr, "/oneOf"_jptr)));
  119. EXPECT_THAT(result, ErrorAt(""_jptr, "/oneOf/0"_jptr, "minimum", "-1 < 10"));
  120. EXPECT_THAT(result, ErrorAt(""_jptr, "/oneOf/1"_jptr, "maximum", "-1 <= 0"));
  121. }
  122. TEST(Annotation, AttachesAlwaysFalseSensibly) {
  123. auto const schema = R"({
  124. "properties": {
  125. "A": false
  126. }
  127. })"_json;
  128. auto const instance = R"({
  129. "A": null
  130. })"_json;
  131. jvalidate::ValidationResult result = validate(schema, instance);
  132. EXPECT_THAT(result, ErrorAt("/A"_jptr, "/properties"_jptr, "", "always false"));
  133. }
  134. int main(int argc, char ** argv) {
  135. testing::InitGoogleMock(&argc, argv);
  136. return RUN_ALL_TESTS();
  137. }