#include #include #include #include #include #include #include #include #include #include #include #include "matchers.h" using enum jvalidate::schema::Version; using jvalidate::Status; using jvalidate::constraint::ExtensionConstraint; using testing::Not; struct IsKeyOfConstraint : jvalidate::extension::ConstraintBase { IsKeyOfConstraint(std::string_view ptr) : ptr(ptr) { EXPECT_M(ptr.find('/') != std::string_view::npos, "IsKeyOfConstraint requires a value-relative-pointer, not a key-relative-pointer"); } jvalidate::detail::RelativePointer ptr; }; template class Visitor : public jvalidate::extension::Visitor, IsKeyOfConstraint> { public: Visitor(A const & root_document) : root_document_(root_document) {} template Status visit(IsKeyOfConstraint const & cons, A2 const & document, auto const & validator) const { validator.annotate(cons.ptr); auto const & object = std::get<1>(cons.ptr.inspect(validator.where_, root_document_)).as_object(); if (object.find(document.as_string()) != object.end()) { return Status::Accept; } return Status::Reject; } private: A root_document_; }; auto validate(Json::Value const & schema_doc, Json::Value const & instance_doc, jvalidate::schema::Version version = Draft2020_12) { using A = jvalidate::adapter::AdapterFor; jvalidate::ConstraintFactory factory{{"is_key_of", [](auto const & context) { return ExtensionConstraint::make( context.schema.as_string()); }}}; jvalidate::Schema const schema(schema_doc, version, factory); jvalidate::ValidationResult result; (void)jvalidate::Validator(schema, Visitor(A(instance_doc))).validate(instance_doc, &result); return result; } TEST(ExtensionConstraint, CanReportSuccess) { auto schema = R"({ "properties": { "nodes": { "type": "object" }, "edges": { "items": { "properties": { "destination": { "is_key_of": "3/nodes", "type": "string" }, "source": { "is_key_of": "3/nodes", "type": "string" } }, "type": "object" }, "type": "array" } }, "type": "object" })"_json; auto instance = R"({ "nodes": { "A": {}, "B": {} }, "edges": [ { "source": "A", "destination": "B" } ] })"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, Valid()); } TEST(ExtensionConstraint, CanReportFailure) { auto schema = R"({ "properties": { "nodes": { "type": "object" }, "edges": { "items": { "properties": { "destination": { "is_key_of": "3/nodes", "type": "string" }, "source": { "is_key_of": "3/nodes", "type": "string" } }, "type": "object" }, "type": "array" } }, "type": "object" })"_json; auto instance = R"({ "nodes": { "A": {}, "B": {} }, "edges": [ { "source": "A", "destination": "C" } ] })"_json; jvalidate::ValidationResult result = validate(schema, instance); EXPECT_THAT(result, Not(Valid())); } int main(int argc, char ** argv) { testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); }