validation_visitor_test.cxx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #include <jvalidate/validation_visitor.h>
  2. #include <gmock/gmock.h>
  3. #include <gtest/gtest.h>
  4. #include <json/value.h>
  5. #include <jvalidate/adapters/jsoncpp.h>
  6. #include <jvalidate/constraint/general_constraint.h>
  7. #include <jvalidate/detail/pointer.h>
  8. #include <jvalidate/enum.h>
  9. #include <jvalidate/regex.h>
  10. #include <jvalidate/schema.h>
  11. #include <jvalidate/validator.h>
  12. #include "matchers.h"
  13. using enum jvalidate::schema::Version;
  14. using jvalidate::Status;
  15. using enum jvalidate::adapter::Type;
  16. using jvalidate::adapter::JsonCppAdapter;
  17. using testing::Eq;
  18. namespace jvalidate {
  19. class ValidationVisitorTest : public testing::Test {
  20. protected:
  21. template <typename JSON = Json::Value const>
  22. auto visit(jvalidate::detail::Pointer ptr, auto const & cons, JSON & json,
  23. jvalidate::ValidationResult * result = nullptr, bool annotate_everything = false) {
  24. JsonCppAdapter const adapter(json);
  25. ValidationVisitor visitor(node_, adapter, cfg_, regex_, format_, extension_, result);
  26. if (annotate_everything) {
  27. visitor.tracking_ = StoreResults::ForAnything;
  28. }
  29. visitor.schema_path_ = ptr;
  30. return visitor.visit(cons, adapter);
  31. }
  32. template <typename JSON = Json::Value const>
  33. auto visit(jvalidate::Not<jvalidate::detail::Pointer> auto const & cons, JSON & json,
  34. jvalidate::ValidationResult * result = nullptr, bool annotate_everything = false) {
  35. return visit({}, cons, json, result, annotate_everything);
  36. }
  37. void config(jvalidate::ValidationConfig cfg) { cfg_ = cfg; }
  38. void format(FormatValidator::UserDefinedFormats && fmts) {
  39. format_ = {std::move(fmts), jvalidate::StdRegexEngine::is_regex};
  40. }
  41. private:
  42. jvalidate::schema::Node node_;
  43. jvalidate::StdRegexEngine regex_;
  44. jvalidate::FormatValidator format_{jvalidate::StdRegexEngine::is_regex};
  45. jvalidate::ValidationConfig cfg_;
  46. jvalidate::detail::StubExtensionVisitor extension_;
  47. };
  48. TEST_F(ValidationVisitorTest, StubExtensionIsNoop) {
  49. constraint::ExtensionConstraint cons;
  50. EXPECT_THAT(visit(cons, {}), Eq(Status::Noop));
  51. }
  52. TEST_F(ValidationVisitorTest, StubExtensionAnnotates) {
  53. constraint::ExtensionConstraint cons;
  54. ValidationResult result;
  55. visit("/extension"_jptr, cons, {}, &result);
  56. EXPECT_THAT(result, AnnotationAt("extension", "unsupported extension"));
  57. }
  58. TEST_F(ValidationVisitorTest, TypeMatchesType) {
  59. constraint::TypeConstraint cons{{Integer}};
  60. EXPECT_THAT(visit(cons, "5"_json), Eq(Status::Accept));
  61. }
  62. TEST_F(ValidationVisitorTest, TypeNumberMatchesInteger) {
  63. constraint::TypeConstraint cons{{Number}};
  64. EXPECT_THAT(visit(cons, "5"_json), Eq(Status::Accept));
  65. }
  66. TEST_F(ValidationVisitorTest, TypeIntegerMatchesWholeDecimal) {
  67. constraint::TypeConstraint cons{{Integer}};
  68. // JsonCppAdapter follows the convention of implicitly treating whole doubles
  69. // that fit in int64s as integer type, but the specification allows for much
  70. // wider integers than that as far as TypeConstraint is concerned.
  71. EXPECT_THAT(visit(cons, "10000000000000000000.0"_json), Eq(Status::Accept));
  72. }
  73. TEST_F(ValidationVisitorTest, TypeIntegerDoesNotMatchFractional) {
  74. constraint::TypeConstraint cons{{Integer}};
  75. EXPECT_THAT(visit(cons, "5.2"_json), Eq(Status::Reject));
  76. }
  77. TEST_F(ValidationVisitorTest, ConstConstraintCanPerformStrictOrLooseEquality) {
  78. constraint::ConstConstraint cons{JsonCppAdapter("\"true\""_json).freeze()};
  79. config({.strict_equality = false});
  80. EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Accept));
  81. config({.strict_equality = true});
  82. EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Reject));
  83. }
  84. TEST_F(ValidationVisitorTest, EnumConstraintCanPerformStrictOrLooseEquality) {
  85. constraint::EnumConstraint cons;
  86. cons.enumeration.push_back(JsonCppAdapter("\"true\""_json).freeze());
  87. config({.strict_equality = false});
  88. EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Accept));
  89. config({.strict_equality = true});
  90. EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Reject));
  91. }
  92. TEST_F(ValidationVisitorTest, EnumConstraintAnnotatesMatchingIndex) {
  93. constraint::EnumConstraint cons;
  94. cons.enumeration.push_back(JsonCppAdapter("\"true\""_json).freeze());
  95. cons.enumeration.push_back(JsonCppAdapter("true"_json).freeze());
  96. ValidationResult result;
  97. visit("/enum"_jptr, cons, "true"_json, &result, true);
  98. EXPECT_THAT(result, ErrorAt("enum", "1"));
  99. }
  100. TEST_F(ValidationVisitorTest, UnknownFormatIsAccept) {
  101. constraint::FormatConstraint cons{"bogus", Draft2020_12, true};
  102. config({.validate_format = true});
  103. ValidationResult result;
  104. EXPECT_THAT(visit("/format"_jptr, cons, "\"Hello\""_json, &result), Eq(Status::Accept));
  105. }
  106. TEST_F(ValidationVisitorTest, UnimplementedFormatIsError) {
  107. constraint::FormatConstraint cons{"bogus", Draft2020_12, true};
  108. config({.validate_format = true});
  109. format({{"bogus", nullptr}});
  110. ValidationResult result;
  111. EXPECT_THAT(visit("/format"_jptr, cons, "\"Hello\""_json, &result), Eq(Status::Reject));
  112. EXPECT_THAT(result, ErrorAt("format", "unimplemented format 'bogus'"));
  113. }
  114. }
  115. #if !defined(JVALIDATE_MONOTEST)
  116. int main(int argc, char ** argv) {
  117. testing::InitGoogleMock(&argc, argv);
  118. return RUN_ALL_TESTS();
  119. }
  120. #endif