#include "zeroerr/assert.h" #include "zeroerr/unittest.h" #include "common/Json.h" #include "common/Value.h" #include #include #include using eve::Value; TEST_CASE("value.json_round_trip_preserves_all_supported_kinds") { Value::Array array; array.emplace_back(); array.emplace_back(true); array.emplace_back(std::int64_t{-7}); array.emplace_back(1.25); array.emplace_back("line\n中🚀"); Value value(Value::Object{{"z", "last"}, {"a", Value(std::move(array))}}); auto encoded = value.toJson(); REQUIRE(encoded.ok()); CHECK_EQ(encoded.value(), R"({"a":[null,true,-7,1.25,"line\n中🚀"],"z":"last"})"); auto parsed = Value::fromJson(encoded.value()); REQUIRE(parsed.ok()); CHECK(parsed.value() == value); } TEST_CASE("value.object_order_and_numeric_kinds_are_deterministic") { auto parsed = Value::fromJson(R"({"z":9223372036854775807,"a":1.0,"negativeZero":-0.0})"); REQUIRE(parsed.ok()); const Value::Object* object = parsed.value().getIf(); REQUIRE(object != nullptr); CHECK(object->find("z")->second.getIf() != nullptr); CHECK(object->find("a")->second.getIf() != nullptr); const double* negativeZero = object->find("negativeZero")->second.getIf(); REQUIRE(negativeZero != nullptr); CHECK(std::signbit(*negativeZero)); auto encoded = parsed.value().toJson(); REQUIRE(encoded.ok()); CHECK_EQ(encoded.value(), R"({"a":1.0,"negativeZero":-0.0,"z":9223372036854775807})"); } TEST_CASE("value_uses_the_common_json_parser_and_serializer") { std::string error; eve::json::Document document = eve::json::Document::parse(R"({"large":9223372036854775807,"fraction":1.0})", &error); REQUIRE(document.valid()); CHECK(document.root().get("large").isIntegerLiteral()); CHECK(document.root().get("large").isInt64()); CHECK_EQ(document.root().get("large").asInt64(), std::int64_t{9223372036854775807}); CHECK(!eve::json::Document::parse(R"({"x":1,"x":2})").valid()); CHECK(!eve::json::Document::parse("Infinity").valid()); Value value = Value::fromJson(R"({"z":2,"a":1})").expect("value parse"); auto commonJson = eve::json::stringify(value); REQUIRE(commonJson.ok()); CHECK_EQ(commonJson.value(), R"({"a":1,"z":2})"); auto valueJson = value.toJson(); REQUIRE(valueJson.ok()); CHECK_EQ(valueJson.value(), commonJson.value()); } TEST_CASE("value.rejects_duplicate_keys_invalid_json_and_non_finite_numbers") { CHECK(!Value::fromJson(R"({"x":1,"x":2})").ok()); CHECK(!Value::fromJson(R"([1,])").ok()); CHECK(!Value::fromJson("1e9999").ok()); CHECK(!Value::fromJson("NaN").ok()); auto nan = Value(std::numeric_limits::quiet_NaN()).toJson(); CHECK(!nan.ok()); CHECK_EQ(static_cast(nan.code()), static_cast(eve::StatusCode::Failed)); REQUIRE(nan.error() != nullptr); CHECK_EQ(static_cast(nan.error()->code()), static_cast(eve::DiagnosticCode::SerializationError)); auto infinity = Value(std::numeric_limits::infinity()).toJson(); CHECK(!infinity.ok()); CHECK_EQ(static_cast(infinity.error()->code()), static_cast(eve::DiagnosticCode::SerializationError)); } TEST_CASE("presentation_value_is_the_common_value_without_a_second_storage") { static_assert(std::is_same_v); eve::Value value(eve::Value::Object{{"count", 4}}); auto encoded = value.toJson(); REQUIRE(encoded.ok()); auto parsed = eve::Value::fromJson(encoded.value()); REQUIRE(parsed.ok()); CHECK(parsed.value() == value); }