This is an automated email from the ASF dual-hosted git repository.
maplefu pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 05dbad82 Add test cases for JSON.SET and JSON.GET (#1828)
05dbad82 is described below
commit 05dbad82ca4814170ecac26b7f39ca3f229732a6
Author: Twice <[email protected]>
AuthorDate: Mon Oct 16 13:48:57 2023 +0900
Add test cases for JSON.SET and JSON.GET (#1828)
Co-authored-by: mwish <[email protected]>
---
CMakeLists.txt | 16 ++--
cmake/gtest.cmake | 2 +-
src/types/json.h | 11 ++-
src/types/redis_json.cc | 12 +--
tests/cppunit/types/json_test.cc | 121 +++++++++++++++++++++++++++++++
tests/gocase/unit/type/json/json_test.go | 58 +++++++++++++++
6 files changed, 204 insertions(+), 16 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cbbf8472..b0b69e0d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -129,9 +129,9 @@ include(cmake/zlib.cmake)
include(cmake/zstd.cmake)
include(cmake/tbb.cmake)
if(ENABLE_SPEEDB)
-include(cmake/speedb.cmake)
+ include(cmake/speedb.cmake)
else()
-include(cmake/rocksdb.cmake)
+ include(cmake/rocksdb.cmake)
endif()
include(cmake/libevent.cmake)
include(cmake/fmt.cmake)
@@ -140,9 +140,9 @@ include(cmake/xxhash.cmake)
include(cmake/span.cmake)
if (ENABLE_LUAJIT)
-include(cmake/luajit.cmake)
+ include(cmake/luajit.cmake)
else()
-include(cmake/lua.cmake)
+ include(cmake/lua.cmake)
endif()
find_package(Threads REQUIRED)
@@ -156,12 +156,12 @@ list(APPEND EXTERNAL_LIBS zstd)
list(APPEND EXTERNAL_LIBS zlib_with_headers)
list(APPEND EXTERNAL_LIBS fmt)
if (ENABLE_LUAJIT)
-list(APPEND EXTERNAL_LIBS luajit)
+ list(APPEND EXTERNAL_LIBS luajit)
else()
-list(APPEND EXTERNAL_LIBS lua)
+ list(APPEND EXTERNAL_LIBS lua)
endif()
if (ENABLE_OPENSSL)
-list(APPEND EXTERNAL_LIBS OpenSSL::SSL)
+ list(APPEND EXTERNAL_LIBS OpenSSL::SSL)
endif()
list(APPEND EXTERNAL_LIBS tbb)
list(APPEND EXTERNAL_LIBS jsoncons)
@@ -258,4 +258,4 @@ file(GLOB_RECURSE TESTS_SRCS tests/cppunit/*.cc)
add_executable(unittest ${TESTS_SRCS})
target_include_directories(unittest PRIVATE tests/cppunit)
-target_link_libraries(unittest PRIVATE kvrocks_objs gtest_main
${EXTERNAL_LIBS})
+target_link_libraries(unittest PRIVATE kvrocks_objs gtest_main gmock
${EXTERNAL_LIBS})
diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake
index 6e097750..9a916114 100644
--- a/cmake/gtest.cmake
+++ b/cmake/gtest.cmake
@@ -25,6 +25,6 @@ FetchContent_DeclareGitHubWithMirror(gtest
)
FetchContent_MakeAvailableWithArgs(gtest
- BUILD_GMOCK=OFF
+ BUILD_GMOCK=ON
INSTALL_GTEST=OFF
)
diff --git a/src/types/json.h b/src/types/json.h
index f1fd141e..5fbf02e2 100644
--- a/src/types/json.h
+++ b/src/types/json.h
@@ -42,7 +42,16 @@ struct JsonValue {
return JsonValue(std::move(val));
}
- std::string Dump() const { return value.to_string(); }
+ std::string Dump() const {
+ std::string res;
+ Dump(&res);
+ return res;
+ }
+
+ void Dump(std::string *buffer) const {
+ jsoncons::compact_json_string_encoder encoder{*buffer};
+ value.dump(encoder);
+ }
Status Set(std::string_view path, JsonValue &&new_value) {
try {
diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc
index 65895f4e..a978023f 100644
--- a/src/types/redis_json.cc
+++ b/src/types/redis_json.cc
@@ -35,7 +35,7 @@ rocksdb::Status Json::write(Slice ns_key, JsonMetadata
*metadata, const JsonValu
std::string val;
metadata->Encode(&val);
- val.append(json_val.Dump());
+ json_val.Dump(&val);
batch->Put(metadata_cf_handle_, ns_key, val);
@@ -67,14 +67,14 @@ rocksdb::Status Json::Set(const std::string &user_key,
const std::string &path,
if (metadata.format != JsonStorageFormat::JSON)
return rocksdb::Status::NotSupported("JSON storage format not supported");
- auto origin_res = JsonValue::FromString(rest.ToStringView());
- if (!origin_res) return rocksdb::Status::Corruption(origin_res.Msg());
- auto origin = *std::move(origin_res);
-
auto new_res = JsonValue::FromString(value);
if (!new_res) return rocksdb::Status::InvalidArgument(new_res.Msg());
auto new_val = *std::move(new_res);
+ auto origin_res = JsonValue::FromString(rest.ToStringView());
+ if (!origin_res) return rocksdb::Status::Corruption(origin_res.Msg());
+ auto origin = *std::move(origin_res);
+
auto set_res = origin.Set(path, std::move(new_val));
if (!set_res) return rocksdb::Status::InvalidArgument(set_res.Msg());
@@ -107,7 +107,7 @@ rocksdb::Status Json::Get(const std::string &user_key,
const std::vector<std::st
res = *std::move(get_res);
} else {
for (const auto &path : paths) {
- auto get_res = json_val.Get(paths[0]);
+ auto get_res = json_val.Get(path);
if (!get_res) return rocksdb::Status::InvalidArgument(get_res.Msg());
res.value.insert_or_assign(path, std::move(get_res->value));
}
diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc
new file mode 100644
index 00000000..22233355
--- /dev/null
+++ b/tests/cppunit/types/json_test.cc
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <types/redis_json.h>
+
+#include "test_base.h"
+
+class RedisJsonTest : public TestBase {
+ protected:
+ explicit RedisJsonTest() : json_(std::make_unique<redis::Json>(storage_,
"json_ns")) {}
+ ~RedisJsonTest() override = default;
+
+ void SetUp() override { key_ = "test_json_key"; }
+ void TearDown() override {}
+
+ std::unique_ptr<redis::Json> json_;
+ JsonValue json_val_;
+};
+
+using ::testing::MatchesRegex;
+
+TEST_F(RedisJsonTest, Set) {
+ ASSERT_THAT(json_->Set(key_, "$[0]", "1").ToString(),
MatchesRegex(".*created at the root"));
+ ASSERT_THAT(json_->Set(key_, "$.a", "1").ToString(), MatchesRegex(".*created
at the root"));
+
+ ASSERT_THAT(json_->Set(key_, "$", "invalid").ToString(),
MatchesRegex(".*syntax_error.*"));
+ ASSERT_THAT(json_->Set(key_, "$", "{").ToString(),
MatchesRegex(".*Unexpected end of file.*"));
+
+ ASSERT_TRUE(json_->Set(key_, "$", " \t{\n } ").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), "{}");
+
+ ASSERT_TRUE(json_->Set(key_, "$", "1").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), "1");
+
+ ASSERT_TRUE(json_->Set(key_, "$", "[1, 2, 3]").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), "[1,2,3]");
+
+ ASSERT_TRUE(json_->Set(key_, "$[1]", "233").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), "[1,233,3]");
+
+ ASSERT_TRUE(json_->Set(key_, "$", "[[1,2],[3,4],[5,6]]").ok());
+ ASSERT_TRUE(json_->Set(key_, "$[*][1]", R"("x")").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([[1,"x"],[3,"x"],[5,"x"]])");
+
+ ASSERT_TRUE(json_->Set(key_, "$", R"({"x":1,"y":2, "z":3})").ok());
+ ASSERT_TRUE(json_->Set(key_, "$.x", "[1,2,3]").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"({"x":[1,2,3],"y":2,"z":3})");
+
+ ASSERT_TRUE(json_->Set(key_, "$.y", R"({"a":"xxx","x":2})").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"({"x":[1,2,3],"y":{"a":"xxx","x":2},"z":3})");
+
+ ASSERT_TRUE(json_->Set(key_, "$..x", "true").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"({"x":true,"y":{"a":"xxx","x":true},"z":3})");
+
+ ASSERT_THAT(json_->Set(key_, "...", "1").ToString(),
MatchesRegex("Invalid.*"));
+ ASSERT_THAT(json_->Set(key_, "[", "1").ToString(),
MatchesRegex("Invalid.*"));
+
+ ASSERT_TRUE(json_->Set(key_, "$", "[[1,2],[[5,6],4]] ").ok());
+ ASSERT_TRUE(json_->Set(key_, "$..[0]", "{}").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([{},[{},4]])");
+
+ ASSERT_TRUE(json_->Del(key_).ok());
+ ASSERT_TRUE(json_->Set(key_, "$", "[{ }, [ ]]").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), "[{},[]]");
+ ASSERT_THAT(json_->Set(key_, "$[1]", "invalid").ToString(),
MatchesRegex(".*syntax_error.*"));
+ ASSERT_TRUE(json_->Del(key_).ok());
+}
+
+TEST_F(RedisJsonTest, Get) {
+ ASSERT_TRUE(json_->Set(key_, "$", R"({"x":[1,2,{"z":3}],"y":[]})").ok());
+ ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"({"x":[1,2,{"z":3}],"y":[]})");
+ ASSERT_TRUE(json_->Get(key_, {"$"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([{"x":[1,2,{"z":3}],"y":[]}])");
+ ASSERT_TRUE(json_->Get(key_, {"$.y"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([[]])");
+ ASSERT_TRUE(json_->Get(key_, {"$.y[0]"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([])");
+ ASSERT_TRUE(json_->Get(key_, {"$.z"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([])");
+ ASSERT_THAT(json_->Get(key_, {"[[["}, &json_val_).ToString(),
MatchesRegex("Invalid.*"));
+
+ ASSERT_TRUE(json_->Set(key_, "$", R"([[[1,2],[3]],[4,5]])").ok());
+ ASSERT_TRUE(json_->Get(key_, {"$..[0]"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"([[[1,2],[3]],[1,2],1,3,4])");
+ ASSERT_TRUE(json_->Get(key_, {"$[0][1][0]", "$[1][1]"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(), R"({"$[0][1][0]":[3],"$[1][1]":[5]})");
+
+ ASSERT_TRUE(json_->Set(key_, "$",
R"({"x":{"y":1},"y":[2,{"z":3}],"z":{"a":{"x":4}}})").ok());
+ ASSERT_TRUE(json_->Get(key_, {"$..x", "$..y", "$..z"}, &json_val_).ok());
+ ASSERT_EQ(json_val_.Dump(),
R"({"$..x":[{"y":1},4],"$..y":[[2,{"z":3}],1],"$..z":[{"a":{"x":4}},3]})");
+}
diff --git a/tests/gocase/unit/type/json/json_test.go
b/tests/gocase/unit/type/json/json_test.go
new file mode 100644
index 00000000..0e5f6032
--- /dev/null
+++ b/tests/gocase/unit/type/json/json_test.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package json
+
+import (
+ "context"
+ "testing"
+
+ "github.com/apache/kvrocks/tests/gocase/util"
+ "github.com/stretchr/testify/require"
+)
+
+func TestJson(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("JSON.SET and JSON.GET basics", func(t *testing.T) {
+ require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", ` {"x":1,
"y":2} `).Err())
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(),
`{"x":1,"y":2}`)
+
+ require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$.y",
`233`).Err())
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(),
`{"x":1,"y":233}`)
+
+ require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `[[1],
[2]]`).Err())
+ require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$[*][0]",
"3").Err())
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(),
`[[3],[3]]`)
+
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(),
`[[[3],[3]]]`)
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$[0]").Val(),
`[[3]]`)
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$[0][0]").Val(),
`[3]`)
+
+ require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$",
`{"x":1,"y":{"x":{"y":2},"y":3}}`).Err())
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(),
`{"x":1,"y":{"x":{"y":2},"y":3}}`)
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(),
`[{"x":1,"y":{"x":{"y":2},"y":3}}]`)
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$..x").Val(),
`[1,{"y":2}]`)
+ require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$..x",
"$..y").Val(), `{"$..x":[1,{"y":2}],"$..y":[{"x":{"y":2},"y":3},3,2]}`)
+ })
+}