extension_test.cxx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #include <jvalidate/extension.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/extension_constraint.h>
  7. #include <jvalidate/detail/expect.h>
  8. #include <jvalidate/detail/relative_pointer.h>
  9. #include <jvalidate/forward.h>
  10. #include <jvalidate/status.h>
  11. #include <jvalidate/validator.h>
  12. #include "matchers.h"
  13. using enum jvalidate::schema::Version;
  14. using jvalidate::Status;
  15. using jvalidate::constraint::ExtensionConstraint;
  16. using testing::Not;
  17. struct IsKeyOfConstraint : jvalidate::extension::ConstraintBase<IsKeyOfConstraint> {
  18. IsKeyOfConstraint(std::string_view ptr) : ptr(ptr) {
  19. EXPECT_M(ptr.find('/') != std::string_view::npos,
  20. "IsKeyOfConstraint requires a value-relative-pointer, not a key-relative-pointer");
  21. }
  22. jvalidate::detail::RelativePointer ptr;
  23. };
  24. class Visitor : public jvalidate::extension::Visitor<Visitor, IsKeyOfConstraint> {
  25. public:
  26. template <jvalidate::Adapter A2>
  27. Status visit(IsKeyOfConstraint const & cons, A2 const & document, auto const & validator) const {
  28. validator.annotate(cons.ptr);
  29. auto const & object =
  30. std::get<1>(cons.ptr.inspect(validator.where_, *validator.root_)).as_object();
  31. if (object.find(document.as_string()) != object.end()) {
  32. return Status::Accept;
  33. }
  34. return Status::Reject;
  35. }
  36. };
  37. auto validate(Json::Value const & schema_doc, Json::Value const & instance_doc,
  38. jvalidate::schema::Version version = Draft2020_12) {
  39. using A = jvalidate::adapter::AdapterFor<Json::Value const>;
  40. jvalidate::ConstraintFactory<A> factory{{"is_key_of", [](auto const & context) {
  41. return ExtensionConstraint::make<IsKeyOfConstraint>(
  42. context.schema.as_string());
  43. }}};
  44. jvalidate::Schema const schema(schema_doc, version, factory);
  45. jvalidate::ValidationResult result;
  46. (void)jvalidate::Validator(schema, Visitor()).validate(instance_doc, &result);
  47. return result;
  48. }
  49. TEST(ExtensionConstraint, CanReportSuccess) {
  50. auto schema = R"({
  51. "properties": {
  52. "nodes": {
  53. "type": "object"
  54. },
  55. "edges": {
  56. "items": {
  57. "properties": {
  58. "destination": {
  59. "is_key_of": "3/nodes",
  60. "type": "string"
  61. },
  62. "source": {
  63. "is_key_of": "3/nodes",
  64. "type": "string"
  65. }
  66. },
  67. "type": "object"
  68. },
  69. "type": "array"
  70. }
  71. },
  72. "type": "object"
  73. })"_json;
  74. auto instance = R"({
  75. "nodes": {
  76. "A": {},
  77. "B": {}
  78. },
  79. "edges": [
  80. { "source": "A", "destination": "B" }
  81. ]
  82. })"_json;
  83. jvalidate::ValidationResult result = validate(schema, instance);
  84. EXPECT_THAT(result, Valid());
  85. }
  86. TEST(ExtensionConstraint, CanReportFailure) {
  87. auto schema = R"({
  88. "properties": {
  89. "nodes": {
  90. "type": "object"
  91. },
  92. "edges": {
  93. "items": {
  94. "properties": {
  95. "destination": {
  96. "is_key_of": "3/nodes",
  97. "type": "string"
  98. },
  99. "source": {
  100. "is_key_of": "3/nodes",
  101. "type": "string"
  102. }
  103. },
  104. "type": "object"
  105. },
  106. "type": "array"
  107. }
  108. },
  109. "type": "object"
  110. })"_json;
  111. auto instance = R"({
  112. "nodes": {
  113. "A": {},
  114. "B": {}
  115. },
  116. "edges": [
  117. { "source": "A", "destination": "C" }
  118. ]
  119. })"_json;
  120. jvalidate::ValidationResult result = validate(schema, instance);
  121. EXPECT_THAT(result, Not(Valid()));
  122. }
  123. int main(int argc, char ** argv) {
  124. testing::InitGoogleMock(&argc, argv);
  125. return RUN_ALL_TESTS();
  126. }