osb/source/test/formatted_json_test.cpp
2023-06-20 14:33:09 +10:00

372 lines
12 KiB
C++

#include "StarFormattedJson.hpp"
#include "StarJsonPath.hpp"
#include "gtest/gtest.h"
using namespace Star;
TEST(FormattedJsonTest, JsonInterop) {
Json array1 = JsonArray{1, 2, 3, 4};
Json array2 = JsonArray{4, 3, 2, 1};
FormattedJson farray1 = array1;
FormattedJson farray2 = array2;
EXPECT_EQ(farray1.toJson(), array1);
EXPECT_EQ(farray2.toJson(), array2);
EXPECT_NE(farray1.toJson(), array2);
EXPECT_NE(farray2.toJson(), array1);
}
TEST(FormattedJsonTest, Parsing) {
FormattedJson json1 = FormattedJson::parse(R"JSON(
{
"foo": "bar",
"hello" : "world",
"abc" :123
// Comment
,"wat": {
"thing": [
49,
27]
}
}
)JSON");
FormattedJson::ElementList expectedElements = {
WhitespaceElement{"\n "},
ObjectKeyElement{"foo"},
ColonElement{},
WhitespaceElement{" "},
ValueElement{Json{"bar"}},
CommaElement{},
WhitespaceElement{"\n "},
ObjectKeyElement{"hello"},
WhitespaceElement{" "},
ColonElement{},
WhitespaceElement{" "},
ValueElement{Json{"world"}},
CommaElement{},
WhitespaceElement{"\n "},
ObjectKeyElement{"abc"},
WhitespaceElement{" "},
ColonElement{},
ValueElement{Json{123}},
WhitespaceElement{"\n // Comment\n "},
CommaElement{},
ObjectKeyElement{"wat"},
ColonElement{},
WhitespaceElement{" "},
ValueElement{Json{JsonObject{{"thing", JsonArray{49, 27}}}}},
WhitespaceElement{"\n "}
};
EXPECT_EQ(json1.elements(), expectedElements);
EXPECT_EQ(json1.get("foo"), FormattedJson{"bar"});
EXPECT_EQ(json1.get("abc"), FormattedJson{123});
EXPECT_EQ(json1.get("wat").get("thing").get(1), FormattedJson{27});
EXPECT_NE(json1.get("wat").get("thing").get(0), FormattedJson{66});
ASSERT_THROW(FormattedJson::parse(" "), JsonParsingException);
ASSERT_THROW(FormattedJson::parse("/* */"), JsonParsingException);
ASSERT_THROW(FormattedJson::parse("x"), JsonParsingException);
ASSERT_THROW(FormattedJson::parseJson("123"), JsonParsingException);
ASSERT_THROW(FormattedJson::parseJson("\"foo\""), JsonParsingException);
EXPECT_TRUE(FormattedJson::parse("123").isType(Json::Type::Int));
EXPECT_TRUE(FormattedJson::parse("\"foo\"").isType(Json::Type::String));
}
List<String> keyOrder(FormattedJson const& json) {
List<String> keys;
for (JsonElement const& elem : json.elements()) {
if (elem.is<ObjectKeyElement>())
keys.append(elem.get<ObjectKeyElement>().key);
}
return keys;
}
TEST(FormattedJsonTest, ObjectInsertion) {
FormattedJson json = FormattedJson::ofType(Json::Type::Object);
List<String> expectedKeys;
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.set("foo", Json{"bar"});
expectedKeys.append("foo");
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.set("baz", Json{"..."});
expectedKeys.append("baz");
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.prepend("hello", Json{"world"});
expectedKeys.insertAt(0, "hello");
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.insertBefore("lala", Json{"alal"}, "foo");
expectedKeys.insertAt(1, "lala");
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.insertAfter("lorem", Json{"ipsum"}, "foo");
expectedKeys.insertAt(3, "lorem");
EXPECT_EQ(keyOrder(json), expectedKeys);
json = json.append("dolor", Json{"sit amet"});
expectedKeys.append("dolor");
EXPECT_EQ(keyOrder(json), expectedKeys);
// If the key already exists, the key order doesn't change.
json = json.set("foo", Json{123}).append("hello", Json{123}).insertAfter("dolor", Json{123}, "baz");
EXPECT_EQ(keyOrder(json), expectedKeys);
FormattedJson::ElementList expectedElements = {ObjectKeyElement{"hello"},
ColonElement{},
ValueElement{Json{123}},
CommaElement{},
ObjectKeyElement{"lala"},
ColonElement{},
ValueElement{Json{"alal"}},
CommaElement{},
ObjectKeyElement{"foo"},
ColonElement{},
ValueElement{Json{123}},
CommaElement{},
ObjectKeyElement{"lorem"},
ColonElement{},
ValueElement{Json{"ipsum"}},
CommaElement{},
ObjectKeyElement{"baz"},
ColonElement{},
ValueElement{Json{"..."}},
CommaElement{},
ObjectKeyElement{"dolor"},
ColonElement{},
ValueElement{Json{123}}};
EXPECT_EQ(json.elements(), expectedElements);
FormattedJson emptyObject = FormattedJson::ofType(Json::Type::Object);
ASSERT_THROW(emptyObject.insertBefore("foo", Json{}, "bar"), JsonException);
ASSERT_THROW(emptyObject.insertAfter("foo", Json{}, "bar"), JsonException);
ASSERT_THROW(FormattedJson::ofType(Json::Type::Array).set("foo", Json{}), JsonException);
}
TEST(FormattedJsonTest, ObjectInsertionWithWhitespace) {
FormattedJson json = FormattedJson::parse(" { \"foo\": 123 } ");
json = json.append("hello", Json{"world"});
json = json.prepend("lorem", Json{"ipsum"});
EXPECT_EQ(json.repr(),
R"JSON({ "lorem": "ipsum", "foo": 123, "hello": "world" })JSON");
}
TEST(FormattedJsonTest, ArrayInsertion) {
FormattedJson json = FormattedJson::parse(R"JSON([
12,
34
])JSON");
json = json.insert(1, Json{23});
json = json.append(Json{45});
json = json.set(0, Json{"01"});
json = json.insert(0, Json{0});
char const* expected = R"JSON([
0,
"01",
23,
34,
45
])JSON";
EXPECT_EQ(json.repr(), expected);
FormattedJson emptyArray = FormattedJson::ofType(Json::Type::Array);
EXPECT_EQ(emptyArray.insert(0, Json{}).size(), 1u);
ASSERT_THROW(emptyArray.insert(1, Json{}), JsonException);
ASSERT_THROW(FormattedJson::ofType(Json::Type::Object).insert(0, Json{}), JsonException);
}
TEST(FormattedJsonTest, ObjectErase) {
FormattedJson json = FormattedJson::parse(R"JSON({
"zzz": 123,
"mmm": 456,
"aaa": 789
})JSON");
json = json.eraseKey("mmm");
char const* expected = R"JSON({
"zzz": 123,
"aaa": 789
})JSON";
EXPECT_EQ(json.repr(), expected);
FormattedJson jsonNoZ = json.eraseKey("zzz");
expected = R"JSON({
"aaa": 789
})JSON";
EXPECT_EQ(jsonNoZ.repr(), expected);
FormattedJson jsonNoA = json.eraseKey("aaa");
expected = R"JSON({
"zzz": 123
})JSON";
EXPECT_EQ(jsonNoA.repr(), expected);
ASSERT_EQ(json.eraseKey("bbb"), json);
ASSERT_THROW(FormattedJson::ofType(Json::Type::Array).eraseKey("foo"), JsonException);
}
TEST(FormattedJsonTest, ArrayErase) {
FormattedJson json = FormattedJson::parse("[123, 456, 789]");
EXPECT_EQ(json.eraseIndex(0).repr(), "[456, 789]");
EXPECT_EQ(json.eraseIndex(1).repr(), "[123, 789]");
EXPECT_EQ(json.eraseIndex(2).repr(), "[123, 456]");
EXPECT_EQ(json.eraseIndex(0).eraseIndex(0).repr(), "[789]");
EXPECT_EQ(json.eraseIndex(0).eraseIndex(0).eraseIndex(0).repr(), "[]");
ASSERT_THROW(FormattedJson::ofType(Json::Type::Object).eraseIndex(0), JsonException);
}
TEST(FormattedJsonTest, CommentPreservation) {
FormattedJson json = FormattedJson::parse(R"JSON({
// This is a comment
"hello": 1,
"world": 2
})JSON");
json = json.insertBefore("goodbye", Json{1}, "world");
json = json.eraseKey("hello");
char const* expected = R"JSON({
// This is a comment
"goodbye": 1,
"world": 2
})JSON";
EXPECT_EQ(json.repr(), expected);
}
TEST(FormattedJsonTest, StylePreservation) {
FormattedJson json = FormattedJson::parse(R"JSON({
"hello" : 1234
})JSON");
json = json.append("world", Json{5678});
char const* expected = R"JSON({
"hello" : 1234,
"world" : 5678
})JSON";
EXPECT_EQ(json.repr(), expected);
}
TEST(FormattedJsonTest, Queries) {
FormattedJson json0 = FormattedJson::parse("[]");
FormattedJson json1 = FormattedJson::parse("{\"a\":1}");
FormattedJson json2 = FormattedJson::parse("[1,2]");
FormattedJson json3 = FormattedJson::parse(R"({"a":1,"b":2,"c":3})");
EXPECT_EQ(json0.size(), 0u);
EXPECT_EQ(json1.size(), 1u);
EXPECT_EQ(json2.size(), 2u);
EXPECT_EQ(json3.size(), 3u);
EXPECT_TRUE(json1.contains("a"));
EXPECT_FALSE(json1.contains("b"));
EXPECT_TRUE(json3.contains("c"));
EXPECT_TRUE(json3.contains("b"));
ASSERT_THROW(FormattedJson::parse("123").size(), JsonException);
ASSERT_THROW(json2.contains("1"), JsonException);
}
TEST(FormattedJsonTest, Types) {
FormattedJson jsonNull = Json{};
FormattedJson jsonBool = Json{true};
FormattedJson jsonInt = Json{1};
FormattedJson jsonFloat = Json{2.0};
FormattedJson jsonString = Json{"foo"};
FormattedJson jsonArray = Json::ofType(Json::Type::Array);
FormattedJson jsonObject = Json::ofType(Json::Type::Object);
EXPECT_EQ(jsonNull.type(), Json::Type::Null);
EXPECT_TRUE(jsonNull.isType(Json::Type::Null));
EXPECT_EQ(jsonNull.typeName(), "null");
EXPECT_EQ(jsonBool.type(), Json::Type::Bool);
EXPECT_TRUE(jsonBool.isType(Json::Type::Bool));
EXPECT_EQ(jsonBool.typeName(), "bool");
EXPECT_EQ(jsonInt.type(), Json::Type::Int);
EXPECT_TRUE(jsonInt.isType(Json::Type::Int));
EXPECT_EQ(jsonInt.typeName(), "int");
EXPECT_EQ(jsonFloat.type(), Json::Type::Float);
EXPECT_TRUE(jsonFloat.isType(Json::Type::Float));
EXPECT_EQ(jsonFloat.typeName(), "float");
EXPECT_EQ(jsonString.type(), Json::Type::String);
EXPECT_TRUE(jsonString.isType(Json::Type::String));
EXPECT_EQ(jsonString.typeName(), "string");
EXPECT_EQ(jsonArray.type(), Json::Type::Array);
EXPECT_TRUE(jsonArray.isType(Json::Type::Array));
EXPECT_EQ(jsonArray.typeName(), "array");
EXPECT_EQ(jsonObject.type(), Json::Type::Object);
EXPECT_TRUE(jsonObject.isType(Json::Type::Object));
EXPECT_EQ(jsonObject.typeName(), "object");
}
TEST(FormattedJsonTest, JsonPath) {
FormattedJson json = FormattedJson::parse(R"JSON({
"foo": {
"bar": [
12,
{
"hello": "world"
},
45
]
},
"baz": [{"a":1}, {"a":2}, {"a":3}]
})JSON");
FormattedJson expectedHelloWorld = Json{JsonObject{{"hello", "world"}}};
EXPECT_EQ(JsonPath::Pointer("/foo/bar/1").get(json), expectedHelloWorld);
EXPECT_EQ(JsonPath::QueryPath("foo.bar[1]").get(json), expectedHelloWorld);
EXPECT_EQ(JsonPath::Pointer("/baz/0/a").get(json), FormattedJson{Json{1}});
EXPECT_EQ(JsonPath::QueryPath("baz[0].a").get(json), FormattedJson{Json{1}});
json = JsonPath::Pointer("/baz/0/a").set(json, FormattedJson{Json{0}});
json = JsonPath::QueryPath("baz[1].a").set(json, FormattedJson{Json{4}});
json = JsonPath::Pointer("/baz/1").add(json, FormattedJson::parse("{\"b\":1}"));
json = JsonPath::QueryPath("baz[1]").add(json, FormattedJson::parse("{\"c\":0.5}"));
EXPECT_EQ(json.get("baz").repr(),
R"([{"a":0},{"c":0.5},{"b":1}, {"a":4}, {"a":3}])");
json = JsonPath::Pointer("/plz").set(json, FormattedJson{JsonArray{}});
json = JsonPath::Pointer("/plz/-").set(json, FormattedJson{Json{"thx"}});
json = JsonPath::Pointer("/plz/-").add(json, FormattedJson{Json{"bye"}});
FormattedJson expectedThxBye = Json{JsonArray{"thx", "bye"}};
EXPECT_EQ(json.get("plz"), expectedThxBye);
// Set and add are almost the same, but:
// Set 0 => replaces the first array element
json = JsonPath::Pointer("/plz/0").set(json, FormattedJson{Json{"kthx"}});
// Add 0 => inserts a new element at the beginning
json = JsonPath::Pointer("/plz/0").add(json, FormattedJson{Json{"kbye"}});
FormattedJson expectedKByeKThxBye = Json{JsonArray{"kbye", "kthx", "bye"}};
EXPECT_EQ(json.get("plz"), expectedKByeKThxBye);
json = JsonPath::Pointer("/foo/bar/1").remove(json);
FormattedJson expectedBar = Json{JsonArray{12, 45}};
EXPECT_EQ(JsonPath::Pointer("/foo/bar").get(json), expectedBar);
json = JsonPath::QueryPath("foo.bar[1]").remove(json);
EXPECT_EQ(JsonPath::QueryPath("foo.bar").get(json).toJson(), JsonArray{12});
}
TEST(FormattedJsonTest, NumberFormatPreservation) {
EXPECT_EQ(FormattedJson::parse("1.0").repr(), "1.0");
EXPECT_EQ(FormattedJson::parse("1").repr(), "1");
EXPECT_EQ(FormattedJson::parse("-0").repr(), "-0");
EXPECT_EQ(FormattedJson::parse("0").repr(), "0");
EXPECT_EQ(FormattedJson::parseJson("[-0.0,1.0,-0]").repr(), "[-0.0,1.0,-0]");
EXPECT_EQ(FormattedJson::parseJson("[1,0]").repr(), "[1,0]");
}