#include #include #include #include #include #include #include #include #include #include #include #include "matchers.h" using enum jvalidate::schema::Version; using jvalidate::Status; using enum jvalidate::adapter::Type; using jvalidate::adapter::JsonCppAdapter; using testing::Eq; namespace jvalidate { class ValidationVisitorTest : public testing::Test { protected: template auto visit(jvalidate::detail::Pointer ptr, auto const & cons, JSON & json, jvalidate::ValidationResult * result = nullptr, bool annotate_everything = false) { JsonCppAdapter const adapter(json); ValidationVisitor visitor(node_, adapter, cfg_, regex_, format_, extension_, result); if (annotate_everything) { visitor.tracking_ = StoreResults::ForAnything; } visitor.schema_path_ = ptr; return visitor.visit(cons, adapter); } template auto visit(jvalidate::Not auto const & cons, JSON & json, jvalidate::ValidationResult * result = nullptr, bool annotate_everything = false) { return visit({}, cons, json, result, annotate_everything); } void config(jvalidate::ValidationConfig cfg) { cfg_ = cfg; } void format(FormatValidator::UserDefinedFormats && fmts) { format_ = {std::move(fmts), jvalidate::StdRegexEngine::is_regex}; } private: jvalidate::schema::Node node_; jvalidate::StdRegexEngine regex_; jvalidate::FormatValidator format_{jvalidate::StdRegexEngine::is_regex}; jvalidate::ValidationConfig cfg_; jvalidate::detail::StubExtensionVisitor extension_; }; TEST_F(ValidationVisitorTest, StubExtensionIsNoop) { constraint::ExtensionConstraint cons; EXPECT_THAT(visit(cons, {}), Eq(Status::Noop)); } TEST_F(ValidationVisitorTest, StubExtensionAnnotates) { constraint::ExtensionConstraint cons; ValidationResult result; visit("/extension"_jptr, cons, {}, &result); EXPECT_THAT(result, AnnotationAt("extension", "unsupported extension")); } TEST_F(ValidationVisitorTest, TypeMatchesType) { constraint::TypeConstraint cons{{Integer}}; EXPECT_THAT(visit(cons, "5"_json), Eq(Status::Accept)); } TEST_F(ValidationVisitorTest, TypeNumberMatchesInteger) { constraint::TypeConstraint cons{{Number}}; EXPECT_THAT(visit(cons, "5"_json), Eq(Status::Accept)); } TEST_F(ValidationVisitorTest, TypeIntegerMatchesWholeDecimal) { constraint::TypeConstraint cons{{Integer}}; // JsonCppAdapter follows the convention of implicitly treating whole doubles // that fit in int64s as integer type, but the specification allows for much // wider integers than that as far as TypeConstraint is concerned. EXPECT_THAT(visit(cons, "10000000000000000000.0"_json), Eq(Status::Accept)); } TEST_F(ValidationVisitorTest, TypeIntegerDoesNotMatchFractional) { constraint::TypeConstraint cons{{Integer}}; EXPECT_THAT(visit(cons, "5.2"_json), Eq(Status::Reject)); } TEST_F(ValidationVisitorTest, ConstConstraintCanPerformStrictOrLooseEquality) { constraint::ConstConstraint cons{JsonCppAdapter("\"true\""_json).freeze()}; config({.strict_equality = false}); EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Accept)); config({.strict_equality = true}); EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Reject)); } TEST_F(ValidationVisitorTest, EnumConstraintCanPerformStrictOrLooseEquality) { constraint::EnumConstraint cons; cons.enumeration.push_back(JsonCppAdapter("\"true\""_json).freeze()); config({.strict_equality = false}); EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Accept)); config({.strict_equality = true}); EXPECT_THAT(visit(cons, "true"_json), Eq(Status::Reject)); } TEST_F(ValidationVisitorTest, EnumConstraintAnnotatesMatchingIndex) { constraint::EnumConstraint cons; cons.enumeration.push_back(JsonCppAdapter("\"true\""_json).freeze()); cons.enumeration.push_back(JsonCppAdapter("true"_json).freeze()); ValidationResult result; visit("/enum"_jptr, cons, "true"_json, &result, true); EXPECT_THAT(result, ErrorAt("enum", "1")); } TEST_F(ValidationVisitorTest, UnknownFormatIsAccept) { constraint::FormatConstraint cons{"bogus", Draft2020_12, true}; config({.validate_format = true}); ValidationResult result; EXPECT_THAT(visit("/format"_jptr, cons, "\"Hello\""_json, &result), Eq(Status::Accept)); } TEST_F(ValidationVisitorTest, UnimplementedFormatIsError) { constraint::FormatConstraint cons{"bogus", Draft2020_12, true}; config({.validate_format = true}); format({{"bogus", nullptr}}); ValidationResult result; EXPECT_THAT(visit("/format"_jptr, cons, "\"Hello\""_json, &result), Eq(Status::Reject)); EXPECT_THAT(result, ErrorAt("format", "unimplemented format 'bogus'")); } } #if !defined(JVALIDATE_MONOTEST) int main(int argc, char ** argv) { testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); } #endif