]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test/d4n: this commit squashes the following commits related to tests for d4n.
authorSamarah <samarah.uriarte@ibm.com>
Fri, 28 Jul 2023 13:44:03 +0000 (09:44 -0400)
committerPritha Srivastava <prsrivas@redhat.com>
Tue, 2 Apr 2024 15:54:51 +0000 (21:24 +0530)
test/redis: Add temporary `boost::redis` unit test program for experimentation
test/rgw: Add/update unit test programs for D4N classes + RedisDriver; switch to `boost::redis` usage
test/d4n: Begin `cpp_redis` removal from D4N filter test program; needs
to be fixed
test/d4n: Temporarily remove D4N filter test program until D4N workflows
are fully implemented

Signed-off-by: Samarah <samarah.uriarte@ibm.com>
src/test/rgw/CMakeLists.txt
src/test/rgw/run-d4n-unit-tests.sh
src/test/rgw/test_boost_redis.cc [new file with mode: 0644]
src/test/rgw/test_d4n_directory.cc
src/test/rgw/test_d4n_filter.cc [deleted file]
src/test/rgw/test_d4n_policy.cc [new file with mode: 0644]
src/test/rgw/test_redis_driver.cc [new file with mode: 0644]

index dec7ea1c149c4648fed6705e29b110cdfd5b1220..3367314f4820be091bdf7003f99d496ecf160028 100644 (file)
@@ -37,16 +37,49 @@ target_link_libraries(ceph_test_rgw_d4n_directory PRIVATE
   )
   target_link_libraries(ceph_test_rgw_d4n_directory PRIVATE spawn)
 install(TARGETS ceph_test_rgw_d4n_directory DESTINATION ${CMAKE_INSTALL_BINDIR})
-endif()
   
-if(WITH_RADOSGW_D4N)
-add_executable(ceph_test_rgw_d4n_filter
-  test_d4n_filter.cc
-  ) 
-target_include_directories(ceph_test_rgw_d4n_filter
+add_executable(ceph_test_rgw_d4n_policy
+  test_d4n_policy.cc
+  )
+target_include_directories(ceph_test_rgw_d4n_policy
+  PUBLIC "${CMAKE_SOURCE_DIR}/src/dmclock/support/src"
+  SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/")
+target_link_libraries(ceph_test_rgw_d4n_policy PRIVATE
+  rgw_common
+  librados
+  ceph-common
+  ${rgw_libs}
+  ${UNITTEST_LIBS}
+  ${EXTRALIBS}
+  )
+  target_link_libraries(ceph_test_rgw_d4n_policy PRIVATE spawn)
+install(TARGETS ceph_test_rgw_d4n_policy DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+add_executable(ceph_test_rgw_redis_driver
+  test_redis_driver.cc
+  )
+target_include_directories(ceph_test_rgw_redis_driver
+  PUBLIC "${CMAKE_SOURCE_DIR}/src/dmclock/support/src"
+  SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/")
+target_link_libraries(ceph_test_rgw_redis_driver PRIVATE
+  rgw_common
+  librados
+  ceph-common
+  ${rgw_libs}
+  ${UNITTEST_LIBS}
+  ${EXTRALIBS}
+  )
+  target_link_libraries(ceph_test_rgw_redis_driver PRIVATE spawn)
+install(TARGETS ceph_test_rgw_redis_driver DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# remove later -Sam
+add_executable(test_boost_redis
+  test_boost_redis.cc
+  )
+target_include_directories(test_boost_redis
   PUBLIC "${CMAKE_SOURCE_DIR}/src/dmclock/support/src"
-  SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/store/dbstore/common")
-target_link_libraries(ceph_test_rgw_d4n_filter PRIVATE
+  SYSTEM PRIVATE "${CMAKE_SOURCE_DIR}/src/rgw/driver/d4n")
+target_link_libraries(test_boost_redis PRIVATE
   rgw_common
   librados
   ceph-common
@@ -54,8 +87,8 @@ target_link_libraries(ceph_test_rgw_d4n_filter PRIVATE
   ${UNITTEST_LIBS}
   ${EXTRALIBS}
   )
-  target_link_libraries(ceph_test_rgw_d4n_filter PRIVATE spawn)
-install(TARGETS ceph_test_rgw_d4n_filter DESTINATION ${CMAKE_INSTALL_BINDIR})
+  target_link_libraries(test_boost_redis PRIVATE spawn)
+install(TARGETS test_boost_redis DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
 #unittest_rgw_bencode
index 3490b8e2bcd53e758a734c93fb427822dcd399e2..6c4c4ff9110d88d51d90300ea11b9553055bfe5a 100755 (executable)
@@ -4,17 +4,21 @@ if [ $? -eq 0 ];
 then 
        echo "Redis process found; flushing!"
        redis-cli FLUSHALL
-fi
+else
 redis-server --daemonize yes
-echo "-----------Redis Server Started-----------"
+fi
+
 ../../../build/bin/ceph_test_rgw_d4n_directory
 printf "\n-----------Directory Test Executed-----------\n"
+
 redis-cli FLUSHALL
-echo "-----------Redis Server Flushed-----------"
-../../../build/bin/ceph_test_rgw_d4n_filter
-printf "\n-----------Filter Test Executed-----------\n"
+../../../build/bin/ceph_test_rgw_d4n_policy
+printf "\n-----------Policy Test Executed-----------\n"
+
 redis-cli FLUSHALL
-echo "-----------Redis Server Flushed-----------"
+../../../build/bin/ceph_test_rgw_redis_driver
+printf "\n-----------RedisDriver Test Executed-----------\n"
+
 REDIS_PID=$(lsof -i4TCP:6379 -sTCP:LISTEN -t)
 kill $REDIS_PID
 echo "-----------Redis Server Stopped-----------"
diff --git a/src/test/rgw/test_boost_redis.cc b/src/test/rgw/test_boost_redis.cc
new file mode 100644 (file)
index 0000000..8ec5f01
--- /dev/null
@@ -0,0 +1,163 @@
+#include <thread>
+#include <gtest/gtest.h>
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/redis/connection.hpp>
+
+namespace net = boost::asio;
+using boost::redis::config;
+using boost::redis::connection;
+using boost::redis::request;
+using boost::redis::response;
+
+class ConnectionFixture: public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+      conn = new connection{io};
+
+      /* Run context */
+      using Executor = net::io_context::executor_type;
+      using Work = net::executor_work_guard<Executor>;
+      work = new std::optional<Work>(io.get_executor());
+      worker = new std::thread([&] { io.run(); });
+      
+      config cfg;
+      cfg.addr.host = "127.0.0.1";
+      cfg.addr.port = "6379";
+
+      conn->async_run(cfg, {}, net::detached);
+    }
+
+    virtual void TearDown() {
+      io.stop();
+
+      delete conn;
+      delete work;
+      delete worker;
+    }
+
+    net::io_context io;
+    connection* conn;
+
+    using Executor = net::io_context::executor_type;
+    using Work = net::executor_work_guard<Executor>;
+    std::optional<Work>* work;
+    std::thread* worker;
+};
+
+TEST_F(ConnectionFixture, Ping) {
+  ASSERT_NE(conn, nullptr);
+
+  request req;
+  req.push("PING", "Hello world");
+
+  response<std::string> resp;
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), "Hello world");
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+TEST_F(ConnectionFixture, HMSet) {
+  request req;
+  response<std::string> resp;
+
+  req.push("HMSET", "key", "data", "value");
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), "OK");
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+TEST_F(ConnectionFixture, HGETALL) {
+  request req;
+  response<std::string,
+           std::map<std::string, std::string> > resp;
+
+  req.push("HMSET", "key", "data", "value");
+  req.push("HGETALL", "key");
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), "OK");
+    EXPECT_EQ(std::get<1>(resp).value().size(), 1);
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+TEST_F(ConnectionFixture, HMGET) {
+  request req;
+  response<std::string,
+           std::vector<std::string> > resp;
+
+  req.push("HMSET", "key", "data", "value");
+  req.push("HMGET", "key", "data");
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), "OK");
+    EXPECT_EQ(std::get<1>(resp).value()[0], "value");
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+TEST_F(ConnectionFixture, HSET) {
+  request req;
+  response<int> resp;
+
+  req.push_range("HSET", "key", std::map<std::string, std::string>{{"field", "value"}});
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), 1);
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+TEST_F(ConnectionFixture, FLUSHALL) {
+  request req;
+  response<std::string, boost::redis::ignore_t> resp;
+
+  req.push("HMSET", "key", "data", "value");
+  req.push("FLUSHALL");
+
+  conn->async_exec(req, resp, [&](auto ec, auto) {
+    ASSERT_EQ((bool)ec, 0);
+    EXPECT_EQ(std::get<0>(resp).value(), "OK");
+
+    conn->cancel();
+  });
+
+  *work = std::nullopt;
+  worker->join();
+}
+
+int main(int argc, char *argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index c69d2d4ecb1a1bb9c8c1cbb0bc88f661af2ce769..806417416daa71f84a256d1c36461cffc2f3d067 100644 (file)
-#include "../rgw/driver/d4n/d4n_directory.h" // Fix -Sam
-#include "rgw_process_env.h"
-#include <cpp_redis/cpp_redis>
-#include <iostream>
-#include <string>
-#include "gtest/gtest.h"
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/redis/connection.hpp>
 
-using namespace std;
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "rgw_auth_registry.h"
+#include "driver/d4n/d4n_directory.h"
+
+namespace net = boost::asio;
+using boost::redis::config;
+using boost::redis::connection;
+using boost::redis::request;
+using boost::redis::response;
+
+class Environment* env;
+
+class Environment : public ::testing::Environment {
+  public:
+    Environment() {}
+
+    virtual ~Environment() {}
+
+    void SetUp() override {
+      std::vector<const char*> args;
+      std::string conf_file_list;
+      std::string cluster = "";
+      CephInitParameters iparams = ceph_argparse_early_args(
+       args, CEPH_ENTITY_TYPE_CLIENT,
+       &cluster, &conf_file_list);
+
+      cct = common_preinit(iparams, CODE_ENVIRONMENT_UTILITY, {}); 
+      dpp = new DoutPrefix(cct->get(), dout_subsys, "D4N Object Directory Test: ");
+      
+      redisHost = cct->_conf->rgw_d4n_host + ":" + std::to_string(cct->_conf->rgw_d4n_port); 
+    }
+    
+    void TearDown() override {
+      delete dpp;
+    }
 
-string portStr;
-string hostStr;
-string redisHost = "";
-string oid = "testName";
-string bucketName = "testBucket";
-int blockSize = 123;
+    std::string redisHost;
+    CephContext* cct;
+    DoutPrefixProvider* dpp;
+};
 
-class DirectoryFixture: public ::testing::Test {
+class ObjectDirectoryFixture: public ::testing::Test {
   protected:
     virtual void SetUp() {
-      blockDir = new rgw::d4n::BlockDirectory(hostStr, stoi(portStr));
-      cacheBlock = new rgw::d4n::CacheBlock();
+      dir = new rgw::d4n::ObjectDirectory{io};
+      obj = new rgw::d4n::CacheObj{
+       .objName = "testName",
+       .bucketName = "testBucket",
+       .creationTime = 0,
+       .dirty = false,
+       .hostsList = { env->redisHost }
+      };
+
+      conn = new connection{boost::asio::make_strand(io)};
 
-      cacheBlock->hostsList.push_back(redisHost);
-      cacheBlock->size = blockSize; 
-      cacheBlock->cacheObj.bucketName = bucketName;
-      cacheBlock->cacheObj.objName = oid;
+      ASSERT_NE(obj, nullptr);
+      ASSERT_NE(dir, nullptr);
+      ASSERT_NE(conn, nullptr);
+
+      dir->init(env->cct, env->dpp);
+
+      /* Run fixture's connection */
+      config cfg;
+      cfg.addr.host = env->cct->_conf->rgw_d4n_host;
+      cfg.addr.port = std::to_string(env->cct->_conf->rgw_d4n_port);
+
+      conn->async_run(cfg, {}, net::detached);
     } 
 
     virtual void TearDown() {
-      delete blockDir;
-      blockDir = nullptr;
+      delete conn;
+      delete obj;
+      delete dir;
+    }
+
+    rgw::d4n::CacheObj* obj;
+    rgw::d4n::ObjectDirectory* dir;
+
+    net::io_context io;
+    connection* conn;
 
-      delete cacheBlock;
-      cacheBlock = nullptr;
+    std::vector<std::string> vals{"testName", "testBucket", "0", "0", env->redisHost};
+    std::vector<std::string> fields{"objName", "bucketName", "creationTime", "dirty", "objHosts"};
+};
+
+class BlockDirectoryFixture: public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+      dir = new rgw::d4n::BlockDirectory{io};
+      block = new rgw::d4n::CacheBlock{
+        .cacheObj = {
+         .objName = "testName",
+         .bucketName = "testBucket",
+         .creationTime = 0,
+         .dirty = false,
+         .hostsList = { env->redisHost }
+       },
+        .blockID = 0,
+       .version = "",
+       .size = 0,
+       .hostsList = { env->redisHost }
+      };
+
+      conn = new connection{boost::asio::make_strand(io)};
+
+      ASSERT_NE(block, nullptr);
+      ASSERT_NE(dir, nullptr);
+      ASSERT_NE(conn, nullptr);
+
+      dir->init(env->cct, env->dpp);
+
+      /* Run fixture's connection */
+      config cfg;
+      cfg.addr.host = env->cct->_conf->rgw_d4n_host;
+      cfg.addr.port = std::to_string(env->cct->_conf->rgw_d4n_port);
+
+      conn->async_run(cfg, {}, net::detached);
+    } 
+
+    virtual void TearDown() {
+      delete conn;
+      delete block;
+      delete dir;
     }
 
-    rgw::d4n::BlockDirectory* blockDir;
-    rgw::d4n::CacheBlock* cacheBlock;
+    rgw::d4n::CacheBlock* block;
+    rgw::d4n::BlockDirectory* dir;
+
+    net::io_context io;
+    connection* conn;
+
+    std::vector<std::string> vals{"0", "", "0", "0", env->redisHost, 
+                                   "testName", "testBucket", "0", "0", env->redisHost};
+    std::vector<std::string> fields{"blockID", "version", "size", "globalWeight", "blockHosts", 
+                                    "objName", "bucketName", "creationTime", "dirty", "objHosts"};
 };
 
-/* Successful initialization */
-TEST_F(DirectoryFixture, DirectoryInit) {
-  ASSERT_NE(blockDir, nullptr);
-  ASSERT_NE(cacheBlock, nullptr);
-  ASSERT_NE(redisHost.length(), (long unsigned int)0);
+TEST_F(ObjectDirectoryFixture, SetYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(obj, optional_yield{io, yield}));
+    dir->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push_range("HMGET", "testBucket_testName", fields);
+    req.push("FLUSHALL");
+
+    response< std::vector<std::string>,
+             boost::redis::ignore_t > resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), vals);
+    conn->cancel();
+  });
+
+  io.run();
 }
 
-/* Successful set_value Call and Redis Check */
-TEST_F(DirectoryFixture, SetValueTest) {
-  cpp_redis::client client;
-  int key_exist = -1;
-  string key;
-  string hosts;
-  string size;
-  string bucketName;
-  string objName;
-  std::vector<std::string> fields;
-  int setReturn = blockDir->set_value(cacheBlock);
-
-  ASSERT_EQ(setReturn, 0);
-
-  fields.push_back("key");
-  fields.push_back("hosts");
-  fields.push_back("size");
-  fields.push_back("bucketName");
-  fields.push_back("objName");
-
-  client.connect(hostStr, stoi(portStr), nullptr, 0, 5, 1000);
-  ASSERT_EQ((bool)client.is_connected(), (bool)1);
-
-  client.hmget("rgw-object:" + oid + ":block-directory", fields, [&key, &hosts, &size, &bucketName, &objName, &key_exist](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      key_exist = 0;
-      key = arr[0].as_string();
-      hosts = arr[1].as_string();
-      size = arr[2].as_string();
-      bucketName = arr[3].as_string();
-      objName = arr[4].as_string();
+TEST_F(ObjectDirectoryFixture, GetYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(obj, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "testBucket_testName", "objName", "newoid");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
     }
+
+    ASSERT_EQ(0, dir->get(obj, optional_yield{io, yield}));
+    EXPECT_EQ(obj->objName, "newoid");
+    dir->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("FLUSHALL");
+      response<boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+    }
+
+    conn->cancel();
   });
 
-  client.sync_commit();
+  io.run();
+}
+
+TEST_F(ObjectDirectoryFixture, CopyYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(obj, optional_yield{io, yield}));
+    ASSERT_EQ(0, dir->copy(obj, "copyTestName", "copyBucketName", optional_yield{io, yield}));
+    dir->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("EXISTS", "copyBucketName_copyTestName");
+    req.push_range("HMGET", "copyBucketName_copyTestName", fields);
+    req.push("FLUSHALL");
+
+    response<int, std::vector<std::string>, 
+            boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), 1);
+
+    auto copyVals = vals;
+    copyVals[0] = "copyTestName";
+    copyVals[1] = "copyBucketName";
+    EXPECT_EQ(std::get<1>(resp).value(), copyVals);
 
-  EXPECT_EQ(key_exist, 0);
-  EXPECT_EQ(key, "rgw-object:" + oid + ":block-directory");
-  EXPECT_EQ(hosts, redisHost);
-  EXPECT_EQ(size, to_string(blockSize));
-  EXPECT_EQ(bucketName, bucketName);
-  EXPECT_EQ(objName, oid);
+    conn->cancel();
+  });
 
-  client.flushall();
+  io.run();
 }
 
-/* Successful get_value Calls and Redis Check */
-TEST_F(DirectoryFixture, GetValueTest) {
-  cpp_redis::client client;
-  int key_exist = -1;
-  string key;
-  string hosts;
-  string size;
-  string bucketName;
-  string objName;
-  std::vector<std::string> fields;
-  int setReturn = blockDir->set_value(cacheBlock);
-
-  ASSERT_EQ(setReturn, 0);
-
-  fields.push_back("key");
-  fields.push_back("hosts");
-  fields.push_back("size");
-  fields.push_back("bucketName");
-  fields.push_back("objName");
-
-  client.connect(hostStr, stoi(portStr), nullptr, 0, 5, 1000);
-  ASSERT_EQ((bool)client.is_connected(), (bool)1);
-
-  client.hmget("rgw-object:" + oid + ":block-directory", fields, [&key, &hosts, &size, &bucketName, &objName, &key_exist](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      key_exist = 0;
-      key = arr[0].as_string();
-      hosts = arr[1].as_string();
-      size = arr[2].as_string();
-      bucketName = arr[3].as_string();
-      objName = arr[4].as_string();
+TEST_F(ObjectDirectoryFixture, DelYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(obj, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "testBucket_testName");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
     }
+
+    ASSERT_EQ(0, dir->del(obj, optional_yield{io, yield}));
+    dir->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "testBucket_testName");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(BlockDirectoryFixture, SetYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+    dir->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push_range("HMGET", "testBucket_testName_0", fields);
+    req.push("FLUSHALL");
+
+    response< std::vector<std::string>,
+             boost::redis::ignore_t > resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), vals);
+    conn->cancel();
   });
 
-  client.sync_commit();
+  io.run();
+}
+
+TEST_F(BlockDirectoryFixture, GetYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "testBucket_testName_0", "objName", "newoid");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
 
-  EXPECT_EQ(key_exist, 0);
-  EXPECT_EQ(key, "rgw-object:" + oid + ":block-directory");
-  EXPECT_EQ(hosts, redisHost);
-  EXPECT_EQ(size, to_string(blockSize));
-  EXPECT_EQ(bucketName, bucketName);
-  EXPECT_EQ(objName, oid);
+    ASSERT_EQ(0, dir->get(block, optional_yield{io, yield}));
+    EXPECT_EQ(block->cacheObj.objName, "newoid");
+    dir->shutdown();
 
-  /* Check if object name in directory instance matches redis update */
-  client.hset("rgw-object:" + oid + ":block-directory", "objName", "newoid", [](cpp_redis::reply& reply) {
-    if (!reply.is_null()) {
-      ASSERT_EQ(reply.as_integer(), 0);
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("FLUSHALL");
+      response<boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
     }
+
+    conn->cancel();
   });
 
-  client.sync_commit();
+  io.run();
+}
+
+TEST_F(BlockDirectoryFixture, CopyYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+    ASSERT_EQ(0, dir->copy(block, "copyTestName", "copyBucketName", optional_yield{io, yield}));
+    dir->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("EXISTS", "copyBucketName_copyTestName_0");
+    req.push_range("HMGET", "copyBucketName_copyTestName_0", fields);
+    req.push("FLUSHALL");
+
+    response<int, std::vector<std::string>, 
+            boost::redis::ignore_t> resp;
 
-  int getReturn = blockDir->get_value(cacheBlock);
+    conn->async_exec(req, resp, yield[ec]);
 
-  ASSERT_EQ(getReturn, 0);
-  EXPECT_EQ(cacheBlock->cacheObj.objName, "newoid");
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), 1);
 
-  client.flushall();
+    auto copyVals = vals;
+    copyVals[5] = "copyTestName";
+    copyVals[6] = "copyBucketName";
+    EXPECT_EQ(std::get<1>(resp).value(), copyVals);
+
+    conn->cancel();
+  });
+
+  io.run();
 }
 
-/* Successful del_value Call and Redis Check */
-TEST_F(DirectoryFixture, DelValueTest) {
-  cpp_redis::client client;
-  vector<string> keys;
-  int setReturn = blockDir->set_value(cacheBlock);
+TEST_F(BlockDirectoryFixture, DelYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "testBucket_testName_0");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
+    }
+
+    ASSERT_EQ(0, dir->del(block, optional_yield{io, yield}));
+    dir->shutdown();
 
-  ASSERT_EQ(setReturn, 0);
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "testBucket_testName_0");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
 
-  /* Ensure entry exists in directory before deletion */
-  keys.push_back("rgw-object:" + oid + ":block-directory");
+      conn->async_exec(req, resp, yield[ec]);
 
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      ASSERT_EQ(reply.as_integer(), 1);
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
     }
+
+    conn->cancel();
   });
 
-  int delReturn = blockDir->del_value(cacheBlock);
+  io.run();
+}
+
+TEST_F(BlockDirectoryFixture, UpdateFieldYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+    ASSERT_EQ(0, dir->update_field(block, "objName", "newTestName", optional_yield{io, yield}));
+    ASSERT_EQ(0, dir->update_field(block, "blockHosts", "127.0.0.1:5000", optional_yield{io, yield}));
+    dir->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HMGET", "testBucket_testName_0", "objName", "blockHosts");
+    req.push("FLUSHALL");
+    response< std::vector<std::string>, 
+             boost::redis::ignore_t> resp;
 
-  ASSERT_EQ(delReturn, 0);
+    conn->async_exec(req, resp, yield[ec]);
 
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      ASSERT_EQ(reply.as_integer(), 0);  /* Zero keys exist */
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value()[0], "newTestName");
+    EXPECT_EQ(std::get<0>(resp).value()[1], "127.0.0.1:6379_127.0.0.1:5000");
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(BlockDirectoryFixture, RemoveHostYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    block->hostsList.push_back("127.0.0.1:6000");
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+    ASSERT_EQ(0, dir->remove_host(block, "127.0.0.1:6379", optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HEXISTS", "testBucket_testName_0", "blockHosts");
+      req.push("HGET", "testBucket_testName_0", "blockHosts");
+      response<int, std::string> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
+      EXPECT_EQ(std::get<1>(resp).value(), "127.0.0.1:6000");
     }
+
+    ASSERT_EQ(0, dir->remove_host(block, "127.0.0.1:6000", optional_yield{io, yield}));
+    dir->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "testBucket_testName_0");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    conn->cancel();
   });
 
-  client.flushall();
+  io.run();
 }
 
 int main(int argc, char *argv[]) {
   ::testing::InitGoogleTest(&argc, argv);
 
-  /* Other ports can be passed to the program */
-  if (argc == 1) {
-    portStr = "6379";
-    hostStr = "127.0.0.1";
-  } else if (argc == 3) {
-    hostStr = argv[1];
-    portStr = argv[2];
-  } else {
-    cout << "Incorrect number of arguments." << std::endl;
-    return -1;
-  }
-
-  redisHost = hostStr + ":" + portStr;
+  env = new Environment();
+  ::testing::AddGlobalTestEnvironment(env);
 
   return RUN_ALL_TESTS();
 }
diff --git a/src/test/rgw/test_d4n_filter.cc b/src/test/rgw/test_d4n_filter.cc
deleted file mode 100644 (file)
index b5c382c..0000000
+++ /dev/null
@@ -1,2132 +0,0 @@
-#include "gtest/gtest.h"
-#include "common/ceph_context.h"
-#include <iostream>
-#include <string>
-#include "rgw_process_env.h"
-#include <cpp_redis/cpp_redis>
-#include "driver/dbstore/common/dbstore.h"
-#include "rgw_sal_store.h"
-#include "../../rgw/driver/d4n/rgw_sal_d4n.h" // fix -Sam
-
-#include "rgw_sal.h"
-#include "rgw_auth.h"
-#include "rgw_auth_registry.h"
-#include "driver/rados/rgw_zone.h"
-#include "rgw_sal_config.h"
-
-#include <boost/asio/io_context.hpp>
-
-#define dout_subsys ceph_subsys_rgw
-
-#define METADATA_LENGTH 22
-
-using namespace std;
-
-string portStr;
-string hostStr;
-string redisHost = "";
-
-vector<const char*> args;
-class Environment* env;
-const DoutPrefixProvider* dpp;
-const req_context rctx{dpp, null_yield, nullptr};
-
-class StoreObject : public rgw::sal::StoreObject {
-  friend class D4NFilterFixture;
-  FRIEND_TEST(D4NFilterFixture, StoreGetMetadata);
-};
-
-class Environment : public ::testing::Environment {
-  boost::asio::io_context ioc;
-  public:
-    Environment() {}
-    
-    virtual ~Environment() {}
-
-    void SetUp() override {
-      /* Ensure redis instance is running */
-      try {
-        env_client.connect(hostStr, stoi(portStr), nullptr, 0, 5, 1000);
-      } catch (std::exception &e) {
-        std::cerr << "[          ] ERROR: Redis instance not running." << std::endl;
-      }
-
-      ASSERT_EQ((bool)env_client.is_connected(), (bool)1);
-
-      /* Proceed with environment setup */
-      cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT, 
-                       CODE_ENVIRONMENT_UTILITY, 
-                       CINIT_FLAG_NO_MON_CONFIG);
-
-      dpp = new DoutPrefix(cct->get(), dout_subsys, "d4n test: ");
-      DriverManager::Config cfg;
-
-      cfg.store_name = "dbstore";
-      cfg.filter_name = "d4n";
-      auto config_store_type = g_conf().get_val<std::string>("rgw_config_store");
-      std::unique_ptr<rgw::sal::ConfigStore> cfgstore
-        = DriverManager::create_config_store(dpp, config_store_type);
-      ASSERT_TRUE(cfgstore);
-      rgw::SiteConfig site;
-      auto r = site.load(dpp, null_yield, cfgstore.get());
-      ASSERT_GT(r, 0);
-
-      driver = DriverManager::get_storage(dpp, dpp->get_cct(),
-              cfg,
-              ioc,
-              site,
-              false,
-              false,
-              false,
-              false,
-              false,
-              false, null_yield,
-             false); 
-    
-      ASSERT_NE(driver, nullptr);
-    }
-
-    void TearDown() override {
-      if (env_client.is_connected()) {
-        delete driver;
-        delete dpp;
-        
-       env_client.disconnect();
-      }
-    }
-
-    boost::intrusive_ptr<CephContext> cct;
-    rgw::sal::Driver* driver;
-    cpp_redis::client env_client;
-};
-
-class D4NFilterFixture : public ::testing::Test {
-  protected:
-    rgw::sal::Driver* driver;
-    unique_ptr<rgw::sal::User> testUser = nullptr;
-    unique_ptr<rgw::sal::Bucket> testBucket = nullptr;
-    unique_ptr<rgw::sal::Writer> testWriter = nullptr;
-
-  public:
-    D4NFilterFixture() {}
-    
-    void SetUp() {
-      driver = env->driver;
-    }
-
-    void TearDown() {}
-    
-    int createUser() {
-      rgw_user u("test_tenant", "test_user", "ns");
-
-      testUser = driver->get_user(u);
-      testUser->get_info().user_id = u;
-
-      int ret = testUser->store_user(dpp, null_yield, false);
-
-      return ret;
-    }
-
-    int createBucket() {
-      RGWBucketInfo info;
-      info.bucket.name = "test_bucket";
-
-      testBucket = driver->get_bucket(info);
-
-      rgw::sal::Bucket::CreateParams params;
-      params.zonegroup_id = "test_id";
-      params.placement_rule.storage_class = "test_sc";
-      params.swift_ver_location = "test_location";
-
-      return testBucket->create(dpp, params, null_yield);
-    }
-
-    int putObject(string name) {
-      string object_name = "test_object_" + name;
-      unique_ptr<rgw::sal::Object> obj = testBucket->get_object(rgw_obj_key(object_name));
-      rgw_user owner;
-      rgw_placement_rule ptail_placement_rule;
-      uint64_t olh_epoch = 123;
-      string unique_tag;
-
-      obj->get_obj_attrs(null_yield, dpp);
-
-      testWriter = driver->get_atomic_writer(dpp, 
-                 null_yield,
-                 obj.get(),
-                 owner,
-                 &ptail_placement_rule,
-                 olh_epoch,
-                 unique_tag);
-  
-      size_t accounted_size = 4;
-      string etag("test_etag");
-      ceph::real_time mtime; 
-      ceph::real_time set_mtime;
-
-      buffer::list bl;
-      string tmp = "test_attrs_value_" + name;
-      bl.append("test_attrs_value_" + name);
-      map<string, bufferlist> attrs{{"test_attrs_key_" + name, bl}};
-
-      ceph::real_time delete_at;
-      char if_match;
-      char if_nomatch;
-      string user_data;
-      rgw_zone_set zones_trace;
-      bool canceled;
-      
-      int ret = testWriter->prepare(null_yield);
-         
-      if (!ret) {
-       ret = testWriter->complete(accounted_size, etag,
-                       &mtime, set_mtime,
-                       attrs,
-                       delete_at,
-                       &if_match, &if_nomatch,
-                       &user_data,
-                       &zones_trace, &canceled,
-                       rctx, rgw::sal::FLAG_LOG_OP);
-
-      return ret;
-    }
-
-    void clientSetUp(cpp_redis::client* client) {
-      client->connect(hostStr, stoi(portStr), nullptr, 0, 5, 1000);
-      ASSERT_EQ((bool)client->is_connected(), (bool)1);
-
-      client->flushdb([](cpp_redis::reply& reply) {});
-      client->sync_commit();
-    }
-
-    void clientReset(cpp_redis::client* client) {
-      client->flushdb([](cpp_redis::reply& reply) {});
-      client->sync_commit();
-    }
-};
-
-/* General operation-related tests */
-TEST_F(D4NFilterFixture, CreateUser) {
-  EXPECT_EQ(createUser(), 0);
-  EXPECT_NE(testUser, nullptr);
-}
-
-TEST_F(D4NFilterFixture, CreateBucket) {
-  ASSERT_EQ(createUser(), 0);
-  ASSERT_NE(testUser, nullptr);
-   
-  EXPECT_EQ(createBucket(), 0);
-  EXPECT_NE(testBucket, nullptr);
-}
-
-TEST_F(D4NFilterFixture, PutObject) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_PutObject");
-  clientSetUp(&client); 
-
-  ASSERT_EQ(createUser(), 0);
-  ASSERT_NE(testUser, nullptr);
-   
-  ASSERT_EQ(createBucket(), 0);
-  ASSERT_NE(testBucket, nullptr);
-  
-  EXPECT_EQ(putObject("PutObject"), 0);
-  EXPECT_NE(testWriter, nullptr);
-
-  client.hgetall("rgw-object:test_object_PutObject:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_PutObject:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_PutObject");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, GetObject) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_GetObject");
-  clientSetUp(&client); 
-
-  ASSERT_EQ(createUser(), 0);
-  ASSERT_NE(testUser, nullptr);
-   
-  ASSERT_EQ(createBucket(), 0);
-  ASSERT_NE(testBucket, nullptr);
-  
-  ASSERT_EQ(putObject("GetObject"), 0);
-  ASSERT_NE(testWriter, nullptr);
-
-  unique_ptr<rgw::sal::Object> testObject_GetObject = testBucket->get_object(rgw_obj_key("test_object_GetObject"));
-
-  EXPECT_NE(testObject_GetObject, nullptr);
-  
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_GetObject.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  
-  unique_ptr<rgw::sal::Object::ReadOp> testROp = testObject_GetObject->get_read_op();
-
-  EXPECT_NE(testROp, nullptr);
-  EXPECT_EQ(testROp->prepare(null_yield, dpp), 0);
-
-  client.hgetall("rgw-object:test_object_GetObject:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_GetObject:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_GetObject");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, CopyObjectNone) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_CopyObjectNone");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("CopyObjectNone");
-  unique_ptr<rgw::sal::Object> testObject_CopyObjectNone = testBucket->get_object(rgw_obj_key("test_object_CopyObjectNone"));
-
-  ASSERT_NE(testObject_CopyObjectNone, nullptr);
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_CopyObjectNone.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Update object */
-  RGWEnv rgw_env;
-  req_info info(get_pointer(env->cct), &rgw_env);
-  rgw_zone_id source_zone;
-  rgw_placement_rule dest_placement; 
-  ceph::real_time src_mtime;
-  ceph::real_time mtime;
-  ceph::real_time mod_ptr;
-  ceph::real_time unmod_ptr;
-  char if_match;
-  char if_nomatch;
-  rgw::sal::AttrsMod attrs_mod = rgw::sal::ATTRSMOD_NONE;
-  rgw::sal::Attrs attrs;
-  RGWObjCategory category = RGWObjCategory::Main;
-  uint64_t olh_epoch = 0;
-  ceph::real_time delete_at;
-  string tag;
-  string etag;
-
-  EXPECT_EQ(testObject_CopyObjectNone->copy_object(testUser.get(),
-                             &info, source_zone, testObject_CopyObjectNone.get(),
-                             testBucket.get(), testBucket.get(),
-                              dest_placement, &src_mtime, &mtime,
-                             &mod_ptr, &unmod_ptr, false,
-                             &if_match, &if_nomatch, attrs_mod,
-                             false, attrs, category, olh_epoch,
-                             delete_at, NULL, &tag, &etag,
-                             NULL, NULL, dpp, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_CopyObjectNone:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-  
-  client.hmget("rgw-object:test_object_CopyObjectNone:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_CopyObjectNone");
-    }
-  });
-
-  client.sync_commit();
-}
-
-TEST_F(D4NFilterFixture, CopyObjectReplace) {
-  cpp_redis::client client;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("CopyObjectReplace");
-  unique_ptr<rgw::sal::Object> testObject_CopyObjectReplace = testBucket->get_object(rgw_obj_key("test_object_CopyObjectReplace"));
-
-  ASSERT_NE(testObject_CopyObjectReplace, nullptr);
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_CopyObjectReplace.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Copy to new object */
-  unique_ptr<rgw::sal::Writer> testWriterCopy = nullptr;
-  unique_ptr<rgw::sal::Object> obj = testBucket->get_object(rgw_obj_key("test_object_copy"));
-  rgw_user owner;
-  rgw_placement_rule ptail_placement_rule;
-  uint64_t olh_epoch_copy = 123;
-  string unique_tag;
-
-  obj->get_obj_attrs(null_yield, dpp);
-
-  testWriterCopy = driver->get_atomic_writer(dpp, 
-             null_yield,
-             obj.get(),
-             owner,
-             &ptail_placement_rule,
-             olh_epoch_copy,
-             unique_tag);
-
-  RGWEnv rgw_env;
-  size_t accounted_size = 0;
-  req_info info(get_pointer(env->cct), &rgw_env);
-  rgw_zone_id source_zone;
-  rgw_placement_rule dest_placement; 
-  ceph::real_time src_mtime;
-  ceph::real_time mtime; 
-  ceph::real_time set_mtime;
-  ceph::real_time mod_ptr;
-  ceph::real_time unmod_ptr;
-  rgw::sal::AttrsMod attrs_mod = rgw::sal::ATTRSMOD_REPLACE;
-  char if_match;
-  char if_nomatch;
-  RGWObjCategory category = RGWObjCategory::Main;
-  uint64_t olh_epoch = 0;
-  ceph::real_time delete_at;
-  string tag;
-  string etag("test_etag_copy");
-
-  /* Attribute to replace */
-  buffer::list bl;
-  bl.append("test_attrs_copy_value");
-  rgw::sal::Attrs attrs{{"test_attrs_key_CopyObjectReplace", bl}};
-
-  string user_data;
-  rgw_zone_set zones_trace;
-  bool canceled;
-  
-  ASSERT_EQ(testWriterCopy->complete(accounted_size, etag,
-                  &mtime, set_mtime,
-                  attrs,
-                  delete_at,
-                  &if_match, &if_nomatch,
-                  &user_data,
-                  &zones_trace, &canceled,
-                  rctx, rgw::sal::FLAG_LOG_OP), 0);
-
-  unique_ptr<rgw::sal::Object> testObject_copy = testBucket->get_object(rgw_obj_key("test_object_copy"));
-
-  EXPECT_EQ(testObject_CopyObjectReplace->copy_object(testUser.get(),
-                             &info, source_zone, testObject_copy.get(),
-                             testBucket.get(), testBucket.get(),
-                              dest_placement, &src_mtime, &mtime,
-                             &mod_ptr, &unmod_ptr, false,
-                             &if_match, &if_nomatch, attrs_mod,
-                             false, attrs, category, olh_epoch,
-                             delete_at, NULL, &tag, &etag,
-                             NULL, NULL, dpp, null_yield), 0);
-
-  /* Ensure the original object is still in the cache */
-  vector<string> keys;
-  keys.push_back("rgw-object:test_object_CopyObjectReplace:cache");
-
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 1);
-    }
-  });
-
-  client.sync_commit();
-
-  /* Retrieve original object's redis data for later comparison */
-  std::vector< std::pair<std::string, std::string> > data;
-  
-  client.hgetall("rgw-object:test_object_CopyObjectReplace:cache", [&data](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      for (int i = 0; i < (int)arr.size() - 1; i += 2) {
-       data.push_back({arr[i].as_string(), arr[i + 1].as_string()});
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  /* Check copy */
-  client.hgetall("rgw-object:test_object_copy:cache", [&data](cpp_redis::reply& reply) {
-    bool unexpected = false;
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 4 + METADATA_LENGTH); /* With etag */
-
-      for (int i = 0; i < (int)arr.size() - 1; i += 2) {
-        auto it = std::find_if(data.begin(), data.end(),
-         [&](const auto& pair) { return pair.first == arr[i].as_string(); });
-
-       if (it != data.end()) {
-         if (arr[i].as_string() == "test_attrs_key_CopyObjectReplace")
-           EXPECT_EQ(arr[i + 1].as_string(), "test_attrs_copy_value");
-         else if (arr[i].as_string() != "mtime") { /* mtime will be different */
-           int index = std::distance(data.begin(), it);
-           EXPECT_EQ(arr[i + 1].as_string(), data[index].second);
-         }
-       } else if (arr[i].as_string() == "etag") {
-          EXPECT_EQ(arr[i + 1].as_string(), "test_etag_copy");
-       } else
-         unexpected = true; /* Unexpected field */
-      }
-      
-      EXPECT_EQ(unexpected, false);
-    }
-  });
-
-  client.sync_commit();
-  
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, CopyObjectMerge) {
-  cpp_redis::client client;
-  vector<string> fields;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("CopyObjectMerge");
-  unique_ptr<rgw::sal::Object> testObject_CopyObjectMerge = testBucket->get_object(rgw_obj_key("test_object_CopyObjectMerge"));
-
-  ASSERT_NE(testObject_CopyObjectMerge, nullptr);
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_CopyObjectMerge.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Copy to new object */
-  unique_ptr<rgw::sal::Writer> testWriterCopy = nullptr;
-  string object_name = "test_object_copy";
-  unique_ptr<rgw::sal::Object> obj = testBucket->get_object(rgw_obj_key(object_name));
-  rgw_user owner;
-  rgw_placement_rule ptail_placement_rule;
-  uint64_t olh_epoch_copy = 123;
-  string unique_tag;
-
-  obj->get_obj_attrs(null_yield, dpp);
-
-  testWriterCopy = driver->get_atomic_writer(dpp, 
-             null_yield,
-             obj.get(),
-             owner,
-             &ptail_placement_rule,
-             olh_epoch_copy,
-             unique_tag);
-
-  RGWEnv rgw_env;
-  size_t accounted_size = 4;
-  req_info info(get_pointer(env->cct), &rgw_env);
-  rgw_zone_id source_zone;
-  rgw_placement_rule dest_placement; 
-  ceph::real_time src_mtime;
-  ceph::real_time mtime; 
-  ceph::real_time set_mtime;
-  ceph::real_time mod_ptr;
-  ceph::real_time unmod_ptr;
-  rgw::sal::AttrsMod attrs_mod = rgw::sal::ATTRSMOD_MERGE;
-  char if_match;
-  char if_nomatch;
-  RGWObjCategory category = RGWObjCategory::Main;
-  uint64_t olh_epoch = 0;
-  ceph::real_time delete_at;
-  string tag;
-  string etag("test_etag_copy");
-
-  buffer::list bl;
-  bl.append("bad_value");
-  rgw::sal::Attrs attrs{{"test_attrs_key_CopyObjectMerge", bl}}; /* Existing attr */
-  bl.clear();
-  bl.append("test_attrs_copy_extra_value");
-  attrs.insert({"test_attrs_copy_extra_key", bl}); /* New attr */
-
-  string user_data;
-  rgw_zone_set zones_trace;
-  bool canceled;
-  
-  ASSERT_EQ(testWriterCopy->complete(accounted_size, etag,
-                  &mtime, set_mtime,
-                  attrs,
-                  delete_at,
-                  &if_match, &if_nomatch,
-                  &user_data,
-                  &zones_trace, &canceled,
-                  rctx, rgw::sal::FLAG_LOG_OP), 0);
-
-  unique_ptr<rgw::sal::Object> testObject_copy = testBucket->get_object(rgw_obj_key("test_object_copy"));
-
-  EXPECT_EQ(testObject_CopyObjectMerge->copy_object(testUser.get(),
-                             &info, source_zone, testObject_copy.get(),
-                             testBucket.get(), testBucket.get(),
-                              dest_placement, &src_mtime, &mtime,
-                             &mod_ptr, &unmod_ptr, false,
-                             &if_match, &if_nomatch, attrs_mod,
-                             false, attrs, category, olh_epoch,
-                             delete_at, NULL, &tag, &etag,
-                             NULL, NULL, dpp, null_yield), 0);
-
-  /* Ensure the original object is still in the cache */
-  vector<string> keys;
-  keys.push_back("rgw-object:test_object_CopyObjectMerge:cache");
-
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 1);
-    }
-  });
-
-  client.sync_commit();
-
-  /* Retrieve original object's redis data for later comparison */
-  std::vector< std::pair<std::string, std::string> > data;
-  
-  client.hgetall("rgw-object:test_object_CopyObjectMerge:cache", [&data](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      for (int i = 0; i < (int)arr.size() - 1; i += 2) {
-       data.push_back({arr[i].as_string(), arr[i + 1].as_string()});
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  /* Check copy */
-  client.hgetall("rgw-object:test_object_copy:cache", [&data](cpp_redis::reply& reply) {
-    bool unexpected = false;
-    bool merge = false;
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 6 + METADATA_LENGTH); /* With etag */
-
-      for (int i = 0; i < (int)arr.size() - 1; i += 2) {
-        auto it = std::find_if(data.begin(), data.end(),
-         [&](const auto& pair) { return pair.first == arr[i].as_string(); });
-
-       if (it != data.end()) {
-         if (arr[i].as_string() == "test_attrs_key_CopyObjectMerge")
-           EXPECT_EQ(arr[i + 1].as_string(), "test_attrs_value_CopyObjectMerge");
-         else if (arr[i].as_string() != "mtime") { /* mtime will be different */
-           int index = std::distance(data.begin(), it);
-           EXPECT_EQ(arr[i + 1].as_string(), data[index].second);
-         }
-       } else if (arr[i].as_string() == "etag") {
-          EXPECT_EQ(arr[i + 1].as_string(), "test_etag_copy");
-       } else if (arr[i].as_string() == "test_attrs_copy_extra_key") {
-         merge = true; 
-          EXPECT_EQ(arr[i + 1].as_string(), "test_attrs_copy_extra_value");
-       } else
-         unexpected = true; /* Unexpected field */
-      }
-      
-      EXPECT_EQ(unexpected, false);
-      EXPECT_EQ(merge, true);
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelObject) {
-  cpp_redis::client client;
-  vector<string> keys;
-  keys.push_back("rgw-object:test_object_DelObject:cache");
-  clientSetUp(&client); 
-
-  ASSERT_EQ(createUser(), 0);
-  ASSERT_NE(testUser, nullptr);
-   
-  ASSERT_EQ(createBucket(), 0);
-  ASSERT_NE(testBucket, nullptr);
-  
-  ASSERT_EQ(putObject("DelObject"), 0);
-  ASSERT_NE(testWriter, nullptr);
-
-  /* Check the object exists before delete op */
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 1);
-    }
-  });
-
-  client.sync_commit();
-
-  unique_ptr<rgw::sal::Object> testObject_DelObject = testBucket->get_object(rgw_obj_key("test_object_DelObject"));
-
-  EXPECT_NE(testObject_DelObject, nullptr);
-  
-  unique_ptr<rgw::sal::Object::DeleteOp> testDOp = testObject_DelObject->get_delete_op();
-
-  EXPECT_NE(testDOp, nullptr);
-  EXPECT_EQ(testDOp->delete_obj(dpp, null_yield, true), 0);
-
-  /* Check the object does not exist after delete op */
-  client.exists(keys, [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 0); /* Zero keys exist */
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, CachePolicy) {
-  cpp_redis::client client;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-
-  /* Create multipart object */
-  string object_name = "test_object_CachePolicy";
-  unique_ptr<rgw::sal::Object> obj = testBucket->get_object(rgw_obj_key(object_name));
-  rgw_user owner;
-  rgw_placement_rule ptail_placement_rule;
-  uint64_t olh_epoch = 123;
-  string unique_tag;
-
-  obj->get_obj_attrs(null_yield, dpp);
-
-  testWriter = driver->get_atomic_writer(dpp, 
-             null_yield,
-             obj.get(),
-             owner,
-             &ptail_placement_rule,
-             olh_epoch,
-             unique_tag);
-
-  size_t accounted_size = 15; /* Uploaded as multipart */
-  string etag("test_etag");
-  ceph::real_time mtime; 
-  ceph::real_time set_mtime;
-
-  buffer::list bl;
-  string tmp = "test_attrs_value_CachePolicy";
-  bl.append("test_attrs_value_CachePolicy");
-  map<string, bufferlist> attrs{{"test_attrs_key_CachePolicy", bl}};
-
-  ceph::real_time delete_at;
-  char if_match;
-  char if_nomatch;
-  string user_data;
-  rgw_zone_set zones_trace;
-  bool canceled;
-  
-  ASSERT_EQ(testWriter->complete(accounted_size, etag,
-                  &mtime, set_mtime,
-                  attrs,
-                  delete_at,
-                  &if_match, &if_nomatch,
-                  &user_data,
-                  &zones_trace, &canceled,
-                  null_yield), 0);
-
-
-  unique_ptr<rgw::sal::Object> testObject_CachePolicy = testBucket->get_object(rgw_obj_key("test_object_CachePolicy"));
-
-  ASSERT_NE(testObject_CachePolicy, nullptr);
-
-  /* Copy to new multipart object */
-  unique_ptr<rgw::sal::Writer> testWriterCopy = nullptr;
-  unique_ptr<rgw::sal::Object> obj_copy = testBucket->get_object(rgw_obj_key("test_object_copy"));
-  uint64_t olh_epoch_copy = 123;
-
-  obj_copy->get_obj_attrs(null_yield, dpp);
-
-  testWriterCopy = driver->get_atomic_writer(dpp, 
-             null_yield,
-             obj_copy.get(),
-             owner,
-             &ptail_placement_rule,
-             olh_epoch_copy,
-             unique_tag);
-
-  RGWEnv rgw_env;
-  req_info info(get_pointer(env->cct), &rgw_env);
-  rgw_zone_id source_zone;
-  rgw_placement_rule dest_placement; 
-  ceph::real_time src_mtime;
-  ceph::real_time mod_ptr;
-  ceph::real_time unmod_ptr;
-  rgw::sal::AttrsMod attrs_mod = rgw::sal::ATTRSMOD_REPLACE;
-  RGWObjCategory category = RGWObjCategory::Main;
-  string tag;
-  
-  ASSERT_EQ(testWriterCopy->complete(accounted_size, etag,
-                  &mtime, set_mtime,
-                  attrs,
-                  delete_at,
-                  &if_match, &if_nomatch,
-                  &user_data,
-                  &zones_trace, &canceled,
-                  null_yield), 0);
-
-  unique_ptr<rgw::sal::Object> testObject_copy = testBucket->get_object(rgw_obj_key("test_object_copy"));
-
-  EXPECT_EQ(testObject_CachePolicy->copy_object(testUser.get(),
-                             &info, source_zone, testObject_copy.get(),
-                             testBucket.get(), testBucket.get(),
-                              dest_placement, &src_mtime, &mtime,
-                             &mod_ptr, &unmod_ptr, false,
-                             &if_match, &if_nomatch, attrs_mod,
-                             false, attrs, category, olh_epoch,
-                             delete_at, NULL, &tag, &etag,
-                             NULL, NULL, dpp, null_yield), 0);
-
-  /* Ensure data field doesn't exist for original object */
-  client.hexists("rgw-object:test_object_CachePolicy:cache", "data", [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 0);
-    }
-  });
-
-  client.sync_commit();
-
-  /* Ensure data field doesn't exist for copy */
-  client.hexists("rgw-object:test_object_CachePolicy:cache", "data", [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 0);
-    }
-  });
-
-  client.sync_commit();
-  
-  clientReset(&client);
-}
-
-/* Attribute-related tests */
-TEST_F(D4NFilterFixture, SetObjectAttrs) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_SetObjectAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("SetObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_SetObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_SetObjectAttrs"));
-
-  ASSERT_NE(testObject_SetObjectAttrs, nullptr);
-
-  buffer::list bl;
-  bl.append("test_attrs_value_extra");
-  map<string, bufferlist> test_attrs{{"test_attrs_key_extra", bl}};
-  fields.push_back("test_attrs_key_extra");
-
-  EXPECT_EQ(testObject_SetObjectAttrs->set_obj_attrs(dpp, &test_attrs, NULL, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_SetObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 4 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_SetObjectAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_SetObjectAttrs");
-      EXPECT_EQ(arr[1].as_string(), "test_attrs_value_extra");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, GetObjectAttrs) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_GetObjectAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("GetObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_GetObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_GetObjectAttrs"));
-
-  ASSERT_NE(testObject_GetObjectAttrs, nullptr);
-
-  buffer::list bl;
-  bl.append("test_attrs_value_extra");
-  map<string, bufferlist> test_attrs{{"test_attrs_key_extra", bl}};
-  fields.push_back("test_attrs_key_extra");
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_GetObjectAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_GetObjectAttrs->set_obj_attrs(dpp, &test_attrs, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  EXPECT_EQ(testObject_GetObjectAttrs->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  client.hgetall("rgw-object:test_object_GetObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 4 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_GetObjectAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_GetObjectAttrs");
-      EXPECT_EQ(arr[1].as_string(), "test_attrs_value_extra");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelObjectAttrs) {
-  cpp_redis::client client;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("DelObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_DelObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_DelObjectAttrs"));
-
-  ASSERT_NE(testObject_DelObjectAttrs, nullptr);
-
-  buffer::list bl;
-  bl.append("test_attrs_value_extra");
-  map<string, bufferlist> test_attrs{{"test_attrs_key_extra", bl}};
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_DelObjectAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_DelObjectAttrs->set_obj_attrs(dpp, &test_attrs, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Check that the attributes exist before deletion */ 
-  client.hgetall("rgw-object:test_object_DelObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 4 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  EXPECT_EQ(testObject_DelObjectAttrs->set_obj_attrs(dpp, NULL, &test_attrs, null_yield), 0);
-
-  /* Check that the attribute does not exist after deletion */ 
-  client.hgetall("rgw-object:test_object_DelObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hexists("rgw-object:test_object_DelObjectAttrs:cache", "test_attrs_key_extra", [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 0);
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, SetLongObjectAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_long;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_SetLongObjectAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("SetLongObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_SetLongObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_SetLongObjectAttrs"));
-
-  ASSERT_NE(testObject_SetLongObjectAttrs, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_long.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  EXPECT_EQ(testObject_SetLongObjectAttrs->set_obj_attrs(dpp, &test_attrs_long, NULL, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_SetLongObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_SetLongObjectAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_SetLongObjectAttrs");
-
-      for (int i = 1; i < 11; ++i) {
-       EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, GetLongObjectAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_long;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_GetLongObjectAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("GetLongObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_GetLongObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_GetLongObjectAttrs"));
-
-  ASSERT_NE(testObject_GetLongObjectAttrs, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_long.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_GetLongObjectAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_GetLongObjectAttrs->set_obj_attrs(dpp, &test_attrs_long, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  EXPECT_EQ(testObject_GetLongObjectAttrs->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  client.hgetall("rgw-object:test_object_GetLongObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_GetLongObjectAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_GetLongObjectAttrs");
-
-      for (int i = 1; i < 11; ++i) {
-       EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, ModifyObjectAttr) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_long;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_ModifyObjectAttr");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("ModifyObjectAttr");
-  unique_ptr<rgw::sal::Object> testObject_ModifyObjectAttr = testBucket->get_object(rgw_obj_key("test_object_ModifyObjectAttr"));
-
-  ASSERT_NE(testObject_ModifyObjectAttr, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_long.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_ModifyObjectAttr.get())->get_next();
-
-  ASSERT_EQ(testObject_ModifyObjectAttr->set_obj_attrs(dpp, &test_attrs_long, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  buffer::list bl_tmp;
-  string tmp_value = "new_test_attrs_value_extra_5";
-  bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-
-  EXPECT_EQ(testObject_ModifyObjectAttr->modify_obj_attrs("test_attrs_key_extra_5", bl_tmp, null_yield, dpp), 0);
-
-  client.hgetall("rgw-object:test_object_ModifyObjectAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_ModifyObjectAttr:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_ModifyObjectAttr");
-
-      for (int i = 1; i < 11; ++i) {
-       if (i == 6) {
-          EXPECT_EQ(arr[i].as_string(), "new_test_attrs_value_extra_" + to_string(i - 1));
-        } else {
-          EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-       }
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelLongObjectAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_long;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_DelLongObjectAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("DelLongObjectAttrs");
-  unique_ptr<rgw::sal::Object> testObject_DelLongObjectAttrs = testBucket->get_object(rgw_obj_key("test_object_DelLongObjectAttrs"));
-
-  ASSERT_NE(testObject_DelLongObjectAttrs, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_long.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_DelLongObjectAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_DelLongObjectAttrs->set_obj_attrs(dpp, &test_attrs_long, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-  
-  /* Check that the attributes exist before deletion */
-  client.hgetall("rgw-object:test_object_DelLongObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  EXPECT_EQ(testObject_DelLongObjectAttrs->set_obj_attrs(dpp, NULL, &test_attrs_long, null_yield), 0);
-
-  /* Check that the attributes do not exist after deletion */
-  client.hgetall("rgw-object:test_object_DelLongObjectAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-
-      for (int i = 0; i < (int)arr.size(); ++i) {
-          EXPECT_EQ((int)arr[i].as_string().find("extra"), -1);
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelObjectAttr) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_long;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_DelObjectAttr");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("DelObjectAttr");
-  unique_ptr<rgw::sal::Object> testObject_DelObjectAttr = testBucket->get_object(rgw_obj_key("test_object_DelObjectAttr"));
-
-  ASSERT_NE(testObject_DelObjectAttr, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_long.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-  
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_DelObjectAttr.get())->get_next();
-
-  ASSERT_EQ(testObject_DelObjectAttr->set_obj_attrs(dpp, &test_attrs_long, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-  
-  /* Check that the attribute exists before deletion */
-  client.hgetall("rgw-object:test_object_DelObjectAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  EXPECT_EQ(testObject_DelObjectAttr->delete_obj_attrs(dpp, "test_attrs_key_extra_5", null_yield), 0);
-
-  /* Check that the attribute does not exist after deletion */
-  client.hgetall("rgw-object:test_object_DelObjectAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 20 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hexists("rgw-object:test_object_DelObjectAttr:cache", "test_attrs_key_extra_5", [](cpp_redis::reply& reply) {
-    if (reply.is_integer()) {
-      EXPECT_EQ(reply.as_integer(), 0);
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-/* Edge cases */
-TEST_F(D4NFilterFixture, PrepareCopyObject) {
-  cpp_redis::client client;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_PrepareCopyObject");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("PrepareCopyObject");
-  unique_ptr<rgw::sal::Object> testObject_PrepareCopyObject = testBucket->get_object(rgw_obj_key("test_object_PrepareCopyObject"));
-
-  ASSERT_NE(testObject_PrepareCopyObject, nullptr);
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_PrepareCopyObject.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  unique_ptr<rgw::sal::Object::ReadOp> testROp = testObject_PrepareCopyObject->get_read_op();
-
-  ASSERT_NE(testROp, nullptr);
-  ASSERT_EQ(testROp->prepare(null_yield, dpp), 0);
-
-  /* Update object */
-  RGWEnv rgw_env;
-  req_info info(get_pointer(env->cct), &rgw_env);
-  rgw_zone_id source_zone;
-  rgw_placement_rule dest_placement; 
-  ceph::real_time src_mtime;
-  ceph::real_time mtime;
-  ceph::real_time mod_ptr;
-  ceph::real_time unmod_ptr;
-  char if_match;
-  char if_nomatch;
-  rgw::sal::AttrsMod attrs_mod = rgw::sal::ATTRSMOD_NONE;
-  rgw::sal::Attrs attrs;
-  RGWObjCategory category = RGWObjCategory::Main;
-  uint64_t olh_epoch = 0;
-  ceph::real_time delete_at;
-  string tag;
-  string etag;
-
-  EXPECT_EQ(testObject_PrepareCopyObject->copy_object(testUser.get(),
-                             &info, source_zone, testObject_PrepareCopyObject.get(),
-                             testBucket.get(), testBucket.get(),
-                              dest_placement, &src_mtime, &mtime,
-                             &mod_ptr, &unmod_ptr, false,
-                             &if_match, &if_nomatch, attrs_mod,
-                             false, attrs, category, olh_epoch,
-                             delete_at, NULL, &tag, &etag,
-                             NULL, NULL, dpp, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_PrepareCopyObject:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-  
-  client.hmget("rgw-object:test_object_PrepareCopyObject:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_PrepareCopyObject");
-    }
-  });
-
-  client.sync_commit();
-  
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, SetDelAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_SetDelAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("SetDelAttrs");
-  unique_ptr<rgw::sal::Object> testObject_SetDelAttrs = testBucket->get_object(rgw_obj_key("test_object_SetDelAttrs"));
-
-  ASSERT_NE(testObject_SetDelAttrs, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_SetDelAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_SetDelAttrs->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Attempt to set and delete attrs with the same API call */
-  buffer::list bl;
-  bl.append("test_attrs_value_extra");
-  map<string, bufferlist> test_attrs_new{{"test_attrs_key_extra", bl}};
-  fields.push_back("test_attrs_key_extra");
-  
-  EXPECT_EQ(testObject_SetDelAttrs->set_obj_attrs(dpp, &test_attrs_new, &test_attrs_base, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_SetDelAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 4 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_SetDelAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_SetDelAttrs");
-      EXPECT_EQ(arr[1].as_string(), "test_attrs_value_extra");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, ModifyNonexistentAttr) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_ModifyNonexistentAttr");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("ModifyNonexistentAttr");
-  unique_ptr<rgw::sal::Object> testObject_ModifyNonexistentAttr = testBucket->get_object(rgw_obj_key("test_object_ModifyNonexistentAttr"));
-
-  ASSERT_NE(testObject_ModifyNonexistentAttr, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_ModifyNonexistentAttr.get())->get_next();
-
-  ASSERT_EQ(testObject_ModifyNonexistentAttr->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-  
-  buffer::list bl_tmp;
-  bl_tmp.append("new_test_attrs_value_extra_ModifyNonexistentAttr");
-
-  EXPECT_EQ(testObject_ModifyNonexistentAttr->modify_obj_attrs("test_attrs_key_extra_ModifyNonexistentAttr", bl_tmp, null_yield, dpp), 0);
-
-  fields.push_back("test_attrs_key_extra_ModifyNonexistentAttr");
-
-  client.hgetall("rgw-object:test_object_ModifyNonexistentAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 24 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_ModifyNonexistentAttr:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_ModifyNonexistentAttr");
-
-      for (int i = 1; i < 11; ++i) {
-       EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-      }
-
-      /* New attribute will be created and stored since it was not found in the existing attributes */
-      EXPECT_EQ(arr[11].as_string(), "new_test_attrs_value_extra_ModifyNonexistentAttr");
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, ModifyGetAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_ModifyGetAttrs");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("ModifyGetAttrs");
-  unique_ptr<rgw::sal::Object> testObject_ModifyGetAttrs = testBucket->get_object(rgw_obj_key("test_object_ModifyGetAttrs"));
-
-  ASSERT_NE(testObject_ModifyGetAttrs, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_ModifyGetAttrs.get())->get_next();
-
-  ASSERT_EQ(testObject_ModifyGetAttrs->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Attempt to get immediately after a modification */
-  buffer::list bl_tmp;
-  bl_tmp.append("new_test_attrs_value_extra_5");
-
-  ASSERT_EQ(testObject_ModifyGetAttrs->modify_obj_attrs("test_attrs_key_extra_5", bl_tmp, null_yield, dpp), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  EXPECT_EQ(testObject_ModifyGetAttrs->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  client.hgetall("rgw-object:test_object_ModifyGetAttrs:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_ModifyGetAttrs:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_ModifyGetAttrs");
-
-      for (int i = 1; i < 11; ++i) {
-       if (i == 6) {
-          EXPECT_EQ(arr[i].as_string(), "new_test_attrs_value_extra_5");
-        } else {
-          EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-       }
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelNonexistentAttr) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_DelNonexistentAttr");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("DelNonexistentAttr");
-  unique_ptr<rgw::sal::Object> testObject_DelNonexistentAttr = testBucket->get_object(rgw_obj_key("test_object_DelNonexistentAttr"));
-
-  ASSERT_NE(testObject_DelNonexistentAttr, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-    fields.push_back(tmp_key);
-  }
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_DelNonexistentAttr.get())->get_next();
-
-  ASSERT_EQ(testObject_DelNonexistentAttr->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield), 0);
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-  /* Attempt to delete an attribute that does not exist */
-  ASSERT_EQ(testObject_DelNonexistentAttr->delete_obj_attrs(dpp, "test_attrs_key_extra_12", null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_DelNonexistentAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 22 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  client.hmget("rgw-object:test_object_DelNonexistentAttr:cache", fields, [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ(arr[0].as_string(), "test_attrs_value_DelNonexistentAttr");
-
-      for (int i = 1; i < 11; ++i) {
-       EXPECT_EQ(arr[i].as_string(), "test_attrs_value_extra_" + to_string(i - 1));
-      }
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, DelSetWithNonexisentAttr) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  vector<string> fields;
-  fields.push_back("test_attrs_key_DelSetWithNonexistentAttr");
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("DelSetWithNonexistentAttr");
-  unique_ptr<rgw::sal::Object> testObject_DelSetWithNonexistentAttr = testBucket->get_object(rgw_obj_key("test_object_DelSetWithNonexistentAttr"));
-
-  ASSERT_NE(testObject_DelSetWithNonexistentAttr, nullptr);
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-  }
-
-  ASSERT_EQ(testObject_DelSetWithNonexistentAttr->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield), 0);
-
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_DelSetWithNonexistentAttr.get())->get_next();
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  EXPECT_EQ(testObject_DelSetWithNonexistentAttr->delete_obj_attrs(dpp, "test_attrs_key_extra_5", null_yield), 0);
-  
-  /* Attempt to delete a set of attrs, including one that does not exist */
-  EXPECT_EQ(testObject_DelSetWithNonexistentAttr->set_obj_attrs(dpp, NULL, &test_attrs_base, null_yield), 0);
-
-  client.hgetall("rgw-object:test_object_DelSetWithNonexistentAttr:cache", [](cpp_redis::reply& reply) {
-    auto arr = reply.as_array();
-
-    if (!arr[0].is_null()) {
-      EXPECT_EQ((int)arr.size(), 2 + METADATA_LENGTH);
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-/* Underlying store attribute check */
-TEST_F(D4NFilterFixture, StoreSetAttr) {
-  createUser();
-  createBucket();
-  putObject("StoreSetAttr");
-  unique_ptr<rgw::sal::Object> testObject_StoreSetAttr = testBucket->get_object(rgw_obj_key("test_object_StoreSetAttr"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreSetAttr.get())->get_next();
-
-  EXPECT_NE(nextObject, nullptr);
-
-  /* Set one attribute */
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-  ASSERT_NE(nextObject->get_attrs().empty(), true);
-
-  /* Check the attribute */ 
-  rgw::sal::Attrs driverAttrs = nextObject->get_attrs();
-  pair<string, string> value(driverAttrs.begin()->first, driverAttrs.begin()->second.to_str());
-
-  EXPECT_EQ(value, make_pair(string("test_attrs_key_StoreSetAttr"), string("test_attrs_value_StoreSetAttr")));
-}
-
-TEST_F(D4NFilterFixture, StoreSetAttrs) {
-  createUser();
-  createBucket();
-  putObject("StoreSetAttrs");
-  unique_ptr<rgw::sal::Object> testObject_StoreSetAttrs = testBucket->get_object(rgw_obj_key("test_object_StoreSetAttrs"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreSetAttrs.get())->get_next();
-
-  EXPECT_NE(nextObject, nullptr);
-
-  /* Delete base attribute for easier comparison */
-  testObject_StoreSetAttrs->delete_obj_attrs(dpp, "test_attrs_key_StoreSetAttrs", null_yield);
-
-  /* Set more attributes */
-  map<string, bufferlist> test_attrs_base;
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-  }
-
-  testObject_StoreSetAttrs->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Check the attributes */ 
-  rgw::sal::Attrs driverAttrs = nextObject->get_attrs();
-  rgw::sal::Attrs::iterator attrs;
-  vector< pair<string, string> > values;
-
-  for (attrs = driverAttrs.begin(); attrs != driverAttrs.end(); ++attrs) {
-    values.push_back(make_pair(attrs->first, attrs->second.to_str()));
-  }
-
-  int i = 0;
-
-  for (const auto& pair : values) {
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-
-    EXPECT_EQ(pair, make_pair(tmp_key, tmp_value));
-    ++i;
-  }
-}
-
-TEST_F(D4NFilterFixture, StoreGetAttrs) {
-  cpp_redis::client client;
-  map<string, bufferlist> test_attrs_base;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("StoreGetAttrs");
-  unique_ptr<rgw::sal::Object> testObject_StoreGetAttrs = testBucket->get_object(rgw_obj_key("test_object_StoreGetAttrs"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreGetAttrs.get())->get_next();
-
-  EXPECT_NE(nextObject, nullptr);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Delete base attribute for easier comparison */
-  testObject_StoreGetAttrs->delete_obj_attrs(dpp, "test_attrs_key_StoreGetAttrs", null_yield);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Set more attributes */
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-  }
-
-  testObject_StoreGetAttrs->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield);
-  nextObject->get_obj_attrs(null_yield, dpp, NULL);
-
-  /* Change an attribute through redis */
-  vector< pair<string, string> > value;
-  value.push_back(make_pair("test_attrs_key_extra_5", "new_test_attrs_value_extra_5"));
-
-  client.hmset("rgw-object:test_object_StoreGetAttrs:cache", value, [&](cpp_redis::reply& reply) {
-    if (!reply.is_null()) {
-      EXPECT_EQ(reply.as_string(), "OK");
-    }
-  });
-
-  client.sync_commit();
-
-  /* Artificially adding the data field so getObject will succeed 
-     for the purposes of this test                                */
-  value.clear();
-  value.push_back(make_pair("data", ""));
-
-  client.hmset("rgw-object:test_object_StoreGetAttrs:cache", value, [&](cpp_redis::reply& reply) {
-    if (!reply.is_null()) {
-      ASSERT_EQ(reply.as_string(), "OK");
-    }
-  });
-
-  client.sync_commit();
-
-  ASSERT_EQ(testObject_StoreGetAttrs->get_obj_attrs(null_yield, dpp, NULL), 0); /* Cache attributes */
-
-  /* Check the attributes on the store layer */ 
-  rgw::sal::Attrs driverAttrs = nextObject->get_attrs();
-  rgw::sal::Attrs::iterator driverattrs;
-  vector< pair<string, string> > driverValues;
-
-  for (driverattrs = driverAttrs.begin(); driverattrs != driverAttrs.end(); ++driverattrs) {
-    driverValues.push_back(make_pair(driverattrs->first, driverattrs->second.to_str()));
-  }
-
-  EXPECT_EQ((int)driverValues.size(), 10);
-
-  int i = 0;
-
-  for (const auto& pair : driverValues) {
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-
-    if (i == 5) {
-      tmp_value = "new_" + tmp_value;
-    }
-
-    EXPECT_EQ(pair, make_pair(tmp_key, tmp_value));
-    ++i;
-  }
-
-  /* Restore and check original attributes */
-  nextObject->get_obj_attrs(null_yield, dpp, NULL);
-  driverAttrs = nextObject->get_attrs();
-  driverValues.clear();
-
-  for (driverattrs = driverAttrs.begin(); driverattrs != driverAttrs.end(); ++driverattrs) {
-    driverValues.push_back(make_pair(driverattrs->first, driverattrs->second.to_str()));
-  }
-
-  EXPECT_EQ((int)driverValues.size(), 10);
-
-  i = 0;
-
-  for (const auto& pair : driverValues) {
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-
-    EXPECT_EQ(pair, make_pair(tmp_key, tmp_value));
-    ++i;
-  }
-
-  clientReset(&client);
-}
-
-TEST_F(D4NFilterFixture, StoreGetMetadata) {
-  cpp_redis::client client;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  putObject("StoreGetMetadata");
-  unique_ptr<rgw::sal::Object> testObject_StoreGetMetadata = testBucket->get_object(rgw_obj_key("test_object_StoreGetMetadata"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreGetMetadata.get())->get_next();
-
-  EXPECT_NE(nextObject, nullptr);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Change metadata values through redis */
-  vector< pair<string, string> > value;
-  value.push_back(make_pair("mtime", "2021-11-08T21:13:38.334696731Z"));
-  value.push_back(make_pair("object_size", "100"));
-  value.push_back(make_pair("accounted_size", "200"));
-  value.push_back(make_pair("epoch", "3")); /* version_id is not tested because the object does not have an instance */
-  value.push_back(make_pair("source_zone_short_id", "300"));
-  value.push_back(make_pair("bucket_count", "10"));
-  value.push_back(make_pair("bucket_size", "20"));
-
-  client.hmset("rgw-object:test_object_StoreGetMetadata:cache", value, [](cpp_redis::reply& reply) {
-    if (!reply.is_null()) {
-      EXPECT_EQ(reply.as_string(), "OK");
-    }
-  });
-
-  client.sync_commit();
-
-  /* Artificially adding the data field so getObject will succeed 
-     for the purposes of this test                                */
-  value.clear();
-  value.push_back(make_pair("data", ""));
-
-  client.hmset("rgw-object:test_object_StoreGetMetadata:cache", value, [](cpp_redis::reply& reply) {
-    if (!reply.is_null()) {
-      ASSERT_EQ(reply.as_string(), "OK");
-    }
-  });
-
-  client.sync_commit();
-
-  unique_ptr<rgw::sal::Object::ReadOp> testROp = testObject_StoreGetMetadata->get_read_op();
-
-  ASSERT_NE(testROp, nullptr);
-  ASSERT_EQ(testROp->prepare(null_yield, dpp), 0);
-
-  /* Check updated metadata values */ 
-  static StoreObject* storeObject = static_cast<StoreObject*>(dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreGetMetadata.get())->get_next());
-
-  EXPECT_EQ(to_iso_8601(storeObject->state.mtime), "2021-11-08T21:13:38.334696731Z");
-  EXPECT_EQ(testObject_StoreGetMetadata->get_obj_size(), (uint64_t)100);
-  EXPECT_EQ(storeObject->state.accounted_size, (uint64_t)200);
-  EXPECT_EQ(storeObject->state.epoch, (uint64_t)3);
-  EXPECT_EQ(storeObject->state.zone_short_id, (uint32_t)300);
-}
-
-TEST_F(D4NFilterFixture, StoreModifyAttr) {
-  createUser();
-  createBucket();
-  putObject("StoreModifyAttr");
-  unique_ptr<rgw::sal::Object> testObject_StoreModifyAttr = testBucket->get_object(rgw_obj_key("test_object_StoreModifyAttr"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreModifyAttr.get())->get_next();
-
-  ASSERT_NE(nextObject, nullptr);
-
-  /* Modify existing attribute */
-  buffer::list bl_tmp;
-  string tmp_value = "new_test_attrs_value_StoreModifyAttr";
-  bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-  
-  testObject_StoreModifyAttr->modify_obj_attrs("test_attrs_key_StoreModifyAttr", bl_tmp, null_yield, dpp);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Check the attribute */ 
-  rgw::sal::Attrs driverAttrs = nextObject->get_attrs();
-  pair<string, string> value(driverAttrs.begin()->first, driverAttrs.begin()->second.to_str());
-
-  EXPECT_EQ(value, make_pair(string("test_attrs_key_StoreModifyAttr"), string("new_test_attrs_value_StoreModifyAttr")));
-}
-
-TEST_F(D4NFilterFixture, StoreDelAttrs) {
-  createUser();
-  createBucket();
-  putObject("StoreDelAttrs");
-  unique_ptr<rgw::sal::Object> testObject_StoreDelAttrs = testBucket->get_object(rgw_obj_key("test_object_StoreDelAttrs"));
-
-  /* Get the underlying store */
-  static rgw::sal::Object* nextObject = dynamic_cast<rgw::sal::FilterObject*>(testObject_StoreDelAttrs.get())->get_next();
-
-  ASSERT_NE(nextObject, nullptr);
-
-  /* Set more attributes */
-  map<string, bufferlist> test_attrs_base;
-
-  for (int i = 0; i < 10; ++i) {
-    buffer::list bl_tmp;
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-    bl_tmp.append(tmp_value.data(), strlen(tmp_value.data()));
-    
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    test_attrs_base.insert({tmp_key, bl_tmp});
-  }
-
-  testObject_StoreDelAttrs->set_obj_attrs(dpp, &test_attrs_base, NULL, null_yield);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Check that the attributes exist before deletion */
-  rgw::sal::Attrs driverAttrs = nextObject->get_attrs();
-
-  EXPECT_EQ(driverAttrs.size(), (long unsigned int)11);
-
-  rgw::sal::Attrs::iterator driverattrs;
-  vector< pair<string, string> > driverValues;
-
-  for (driverattrs = ++driverAttrs.begin(); driverattrs != driverAttrs.end(); ++driverattrs) {
-    driverValues.push_back(make_pair(driverattrs->first, driverattrs->second.to_str()));
-  }
-
-  int i = 0;
-
-  for (const auto& pair : driverValues) {
-    string tmp_key = "test_attrs_key_extra_" + to_string(i);
-    string tmp_value = "test_attrs_value_extra_" + to_string(i);
-
-    EXPECT_EQ(pair, make_pair(tmp_key, tmp_value));
-    ++i;
-  }
-
-  testObject_StoreDelAttrs->set_obj_attrs(dpp, NULL, &test_attrs_base, null_yield);
-
-  ASSERT_EQ(nextObject->get_obj_attrs(null_yield, dpp, NULL), 0);
-
-  /* Check that the attributes do not exist after deletion */ 
-  driverAttrs = nextObject->get_attrs();
-
-  EXPECT_EQ(driverAttrs.size(), (long unsigned int)1);
-
-  pair<string, string> value(driverAttrs.begin()->first, driverAttrs.begin()->second.to_str());
-
-  EXPECT_EQ(value, make_pair(string("test_attrs_key_StoreDelAttrs"), string("test_attrs_value_StoreDelAttrs")));
-}
-
-/* SAL object data storage check */
-TEST_F(D4NFilterFixture, DataCheck) {
-  cpp_redis::client client;
-  clientSetUp(&client); 
-
-  createUser();
-  createBucket();
-  
-  /* Prepare, process, and complete object write */
-  unique_ptr<rgw::sal::Object> obj = testBucket->get_object(rgw_obj_key("test_object_DataCheck"));
-  rgw_user owner;
-  rgw_placement_rule ptail_placement_rule;
-  uint64_t olh_epoch = 123;
-  string unique_tag;
-
-  obj->get_obj_attrs(null_yield, dpp);
-
-  testWriter = driver->get_atomic_writer(dpp, 
-           null_yield,
-           obj.get(),
-           owner,
-           &ptail_placement_rule,
-           olh_epoch,
-           unique_tag);
-
-  size_t accounted_size = 4;
-  string etag("test_etag");
-  ceph::real_time mtime; 
-  ceph::real_time set_mtime;
-
-  buffer::list bl;
-  string tmp = "test_attrs_value_DataCheck";
-  bl.append("test_attrs_value_DataCheck");
-  map<string, bufferlist> attrs{{"test_attrs_key_DataCheck", bl}};
-  buffer::list data;
-  data.append("test data");
-
-  ceph::real_time delete_at;
-  char if_match;
-  char if_nomatch;
-  string user_data;
-  rgw_zone_set zones_trace;
-  bool canceled;
-
-  ASSERT_EQ(testWriter->prepare(null_yield), 0);
-  
-  ASSERT_EQ(testWriter->process(std::move(data), 0), 0);
-
-  ASSERT_EQ(testWriter->complete(accounted_size, etag,
-                &mtime, set_mtime,
-                attrs,
-                delete_at,
-                &if_match, &if_nomatch,
-                &user_data,
-                &zones_trace, &canceled,
-                rctx, rgw::sal::FLAG_LOG_OP), 0);
-  client.hget("rgw-object:test_object_DataCheck:cache", "data", [&data](cpp_redis::reply& reply) {
-    if (reply.is_string()) {
-      EXPECT_EQ(reply.as_string(), data.to_str());
-    }
-  });
-
-  client.sync_commit();
-
-  /* Change data and ensure redis stores the new value */
-  buffer::list dataNew;
-  dataNew.append("new test data");
-
-  ASSERT_EQ(testWriter->prepare(null_yield), 0);
-  
-  ASSERT_EQ(testWriter->process(std::move(dataNew), 0), 0);
-
-  ASSERT_EQ(testWriter->complete(accounted_size, etag,
-                &mtime, set_mtime,
-                attrs,
-                delete_at,
-                &if_match, &if_nomatch,
-                &user_data,
-                &zones_trace, &canceled,
-                rctx, rgw::sal::FLAG_LOG_OP), 0);
-
-  client.hget("rgw-object:test_object_DataCheck:cache", "data", [&dataNew](cpp_redis::reply& reply) {
-    if (reply.is_string()) {
-      EXPECT_EQ(reply.as_string(), dataNew.to_str());
-    }
-  });
-
-  client.sync_commit();
-
-  clientReset(&client);
-}
-
-int main(int argc, char *argv[]) {
-  ::testing::InitGoogleTest(&argc, argv);
-
-  /* Other host and port can be passed to the program */
-  if (argc == 1) {
-    portStr = "6379";
-    hostStr = "127.0.0.1";
-  } else if (argc == 3) {
-    hostStr = argv[1];
-    portStr = argv[2];
-  } else {
-    std::cout << "Incorrect number of arguments." << std::endl;
-    return -1;
-  }
-
-  redisHost = hostStr + ":" + portStr;
-
-  env = new Environment();
-  ::testing::AddGlobalTestEnvironment(env);
-
-  return RUN_ALL_TESTS();
-}
diff --git a/src/test/rgw/test_d4n_policy.cc b/src/test/rgw/test_d4n_policy.cc
new file mode 100644 (file)
index 0000000..e73cf3f
--- /dev/null
@@ -0,0 +1,253 @@
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/redis/connection.hpp>
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "rgw_auth_registry.h"
+#include "driver/d4n/d4n_policy.h"
+
+namespace net = boost::asio;
+using boost::redis::config;
+using boost::redis::connection;
+using boost::redis::request;
+using boost::redis::response;
+
+class Environment* env;
+
+class Environment : public ::testing::Environment {
+  public:
+    Environment() {}
+
+    virtual ~Environment() {}
+
+    void SetUp() override {
+      std::vector<const char*> args;
+      std::string conf_file_list;
+      std::string cluster = "";
+      CephInitParameters iparams = ceph_argparse_early_args(
+       args, CEPH_ENTITY_TYPE_CLIENT,
+       &cluster, &conf_file_list);
+
+      cct = common_preinit(iparams, CODE_ENVIRONMENT_UTILITY, {}); 
+      dpp = new DoutPrefix(cct->get(), dout_subsys, "D4N Object Directory Test: ");
+      
+      redisHost = cct->_conf->rgw_d4n_host + ":" + std::to_string(cct->_conf->rgw_d4n_port); 
+    }
+
+    std::string redisHost;
+    CephContext* cct;
+    DoutPrefixProvider* dpp;
+};
+
+class LFUDAPolicyFixture: public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+      block = new rgw::d4n::CacheBlock{
+       .cacheObj = {
+         .objName = "testName",
+         .bucketName = "testBucket",
+         .creationTime = 0,
+         .dirty = false,
+         .hostsList = { env->redisHost }
+       },
+        .blockID = 0,
+       .version = "",
+       .size = bl.length(),
+       .hostsList = { env->redisHost }
+      };
+
+      rgw::cache::Partition partition_info{ .location = "RedisCache" };
+      cacheDriver = new rgw::cache::RedisDriver{io, partition_info};
+      policyDriver = new rgw::d4n::PolicyDriver(io, "lfuda");
+      dir = new rgw::d4n::BlockDirectory{io};
+      conn = new connection{boost::asio::make_strand(io)};
+
+      ASSERT_NE(dir, nullptr);
+      ASSERT_NE(cacheDriver, nullptr);
+      ASSERT_NE(policyDriver, nullptr);
+      ASSERT_NE(conn, nullptr);
+
+      dir->init(env->cct, env->dpp);
+      cacheDriver->initialize(env->cct, env->dpp);
+      policyDriver->init();
+      policyDriver->get_cache_policy()->init(env->cct, env->dpp);
+
+      bl.append("test data");
+      bufferlist attrVal;
+      attrVal.append("attrVal");
+      attrs.insert({"attr", attrVal});
+
+      /* Run fixture's connection */
+      config cfg;
+      cfg.addr.host = env->cct->_conf->rgw_d4n_host;
+      cfg.addr.port = std::to_string(env->cct->_conf->rgw_d4n_port);
+
+      conn->async_run(cfg, {}, net::detached);
+    } 
+
+    virtual void TearDown() {
+      delete conn;
+      delete block;
+      delete dir;
+      delete cacheDriver;
+      delete policyDriver;
+    }
+
+    rgw::d4n::CacheBlock* block;
+    rgw::d4n::BlockDirectory* dir;
+    rgw::d4n::PolicyDriver* policyDriver;
+    rgw::cache::RedisDriver* cacheDriver;
+
+    net::io_context io;
+    connection* conn;
+
+    bufferlist bl;
+    rgw::sal::Attrs attrs;
+    std::string key = "testName";
+};
+
+TEST_F(LFUDAPolicyFixture, LocalGetBlockYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, key, bl, bl.length(), attrs, optional_yield{io, yield}));
+    policyDriver->get_cache_policy()->insert(env->dpp, key, 0, bl.length(), "", cacheDriver, optional_yield{io, yield});
+
+    /* Change cache age for testing purposes */
+    { 
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "lfuda", "age", "5");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    ASSERT_GE(policyDriver->get_cache_policy()->get_block(env->dpp, block, cacheDriver, optional_yield{io, yield}), 0);
+
+    dir->shutdown();
+    cacheDriver->shutdown();
+    policyDriver->get_cache_policy()->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HGET", "RedisCache/testName", "localWeight");
+    req.push("FLUSHALL");
+
+    response<std::string, boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), "5");
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(LFUDAPolicyFixture, RemoteGetBlockYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    /* Set victim block for eviction */
+    rgw::d4n::CacheBlock victim = rgw::d4n::CacheBlock{
+      .cacheObj = {
+       .objName = "victimName",
+       .bucketName = "testBucket",
+       .creationTime = 0,
+       .dirty = false,
+       .hostsList = { env->redisHost }
+      },
+      .blockID = 0,
+      .version = "",
+      .size = bl.length(),
+      .globalWeight = 5,
+      .hostsList = { env->redisHost }
+    };
+
+    bufferlist attrVal;
+    attrVal.append(std::to_string(bl.length()));
+    attrs.insert({"accounted_size", attrVal});
+    attrVal.clear();
+    attrVal.append("testBucket");
+    attrs.insert({"bucket_name", attrVal});
+
+    ASSERT_EQ(0, dir->set(&victim, optional_yield{io, yield}));
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, victim.cacheObj.objName, bl, bl.length(), attrs, optional_yield{io, yield}));
+    policyDriver->get_cache_policy()->insert(env->dpp, victim.cacheObj.objName, 0, bl.length(), "", cacheDriver, optional_yield{io, yield});
+
+    /* Remote block */
+    block->size = cacheDriver->get_free_space(env->dpp) + 1; /* To trigger eviction */
+    block->hostsList.clear();  
+    block->cacheObj.hostsList.clear();
+    block->hostsList.push_back("127.0.0.1:6000");
+    block->cacheObj.hostsList.push_back("127.0.0.1:6000");
+
+    ASSERT_EQ(0, dir->set(block, optional_yield{io, yield}));
+
+    ASSERT_GE(policyDriver->get_cache_policy()->get_block(env->dpp, block, cacheDriver, optional_yield{io, yield}), 0);
+
+    dir->shutdown();
+    cacheDriver->shutdown();
+    policyDriver->get_cache_policy()->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("EXISTS", "RedisCache/victimName");
+    req.push("HGET", "testBucket_victimName_0", "globalWeight");
+    req.push("HGET", "testBucket_testName_0", "globalWeight");
+    req.push("FLUSHALL");
+
+    response<int, std::string, std::string,
+             std::string, boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), 0);
+    EXPECT_EQ(std::get<1>(resp).value(), "5");
+    EXPECT_EQ(std::get<2>(resp).value(), "0");
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(LFUDAPolicyFixture, BackendGetBlockYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_GE(policyDriver->get_cache_policy()->get_block(env->dpp, block, cacheDriver, optional_yield{io, yield}), 0);
+
+    dir->shutdown();
+    cacheDriver->shutdown();
+    policyDriver->get_cache_policy()->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("FLUSHALL");
+
+    response<boost::redis::ignore_t> resp;
+    //response< std::vector<std::string>,
+    //          boost::redis::ignore_t > resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    //ASSERT_EQ((bool)ec, false);
+    //EXPECT_EQ(std::get<0>(resp).value(), vals);
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+int main(int argc, char *argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  env = new Environment();
+  ::testing::AddGlobalTestEnvironment(env);
+
+  return RUN_ALL_TESTS();
+}
diff --git a/src/test/rgw/test_redis_driver.cc b/src/test/rgw/test_redis_driver.cc
new file mode 100644 (file)
index 0000000..a1f8783
--- /dev/null
@@ -0,0 +1,497 @@
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/detached.hpp>
+#include <boost/redis/connection.hpp>
+
+#include "gtest/gtest.h"
+#include "common/ceph_argparse.h"
+#include "rgw_auth_registry.h"
+#include "rgw_redis_driver.h"
+
+namespace net = boost::asio;
+using boost::redis::config;
+using boost::redis::connection;
+using boost::redis::request;
+using boost::redis::response;
+
+class Environment* env;
+
+class Environment : public ::testing::Environment {
+  public:
+    Environment() {}
+
+    virtual ~Environment() {}
+
+    void SetUp() override {
+      std::vector<const char*> args;
+      std::string conf_file_list;
+      std::string cluster = "";
+      CephInitParameters iparams = ceph_argparse_early_args(
+        args, CEPH_ENTITY_TYPE_CLIENT,
+        &cluster, &conf_file_list);
+
+      cct = common_preinit(iparams, CODE_ENVIRONMENT_UTILITY, {});
+      dpp = new DoutPrefix(cct->get(), dout_subsys, "D4N Object Directory Test: ");
+
+      redisHost = cct->_conf->rgw_d4n_host + ":" + std::to_string(cct->_conf->rgw_d4n_port);
+    }
+
+    std::string redisHost;
+    CephContext* cct;
+    DoutPrefixProvider* dpp;
+};
+
+class RedisDriverFixture: public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+      rgw::cache::Partition partition_info{ .location = "RedisCache" };
+      cacheDriver = new rgw::cache::RedisDriver{io, partition_info};
+
+      conn = new connection{boost::asio::make_strand(io)};
+
+      ASSERT_NE(cacheDriver, nullptr);
+      ASSERT_NE(conn, nullptr);
+
+      cacheDriver->initialize(env->cct, env->dpp);
+
+      bl.append("test data");
+      bufferlist attrVal;
+      attrVal.append("attrVal");
+      attrs.insert({"attr", attrVal});
+
+      /* Run fixture's connection */
+      config cfg;
+      cfg.addr.host = env->cct->_conf->rgw_d4n_host;
+      cfg.addr.port = std::to_string(env->cct->_conf->rgw_d4n_port);
+
+      conn->async_run(cfg, {}, net::detached);
+    } 
+
+    virtual void TearDown() {
+      delete conn;
+      delete cacheDriver;
+    }
+
+    rgw::cache::RedisDriver* cacheDriver;
+
+    net::io_context io;
+    connection* conn; 
+
+    bufferlist bl;
+    rgw::sal::Attrs attrs;
+};
+
+TEST_F(RedisDriverFixture, PutYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HGET", "RedisCache/testName", "attr");
+    req.push("FLUSHALL");
+
+    response<std::string, boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), "attrVal");
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, GetYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "RedisCache/testName", "data", "new data", "attr", "newVal");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    bufferlist ret;
+    rgw::sal::Attrs retAttrs;
+
+    ASSERT_EQ(0, cacheDriver->get(env->dpp, "testName", 0, bl.length(), ret, retAttrs, optional_yield{io, yield}));
+    EXPECT_EQ(ret.to_str(), "new data");
+    EXPECT_EQ(retAttrs.begin()->second.to_str(), "newVal");
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("FLUSHALL");
+      response<boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, DelYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "RedisCache/testName");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
+    }
+
+    ASSERT_EQ(0, cacheDriver->del(env->dpp, "testName", optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("EXISTS", "RedisCache/testName");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+// How can I test get_async? -Sam
+
+TEST_F(RedisDriverFixture, AppendDataYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HGET", "RedisCache/testName", "data");
+      response<std::string> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), "test data");
+    }
+
+    bufferlist val;
+    val.append(" has been written");
+
+    ASSERT_EQ(0, cacheDriver->append_data(env->dpp, "testName", val, optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HGET", "RedisCache/testName", "data");
+      req.push("FLUSHALL");
+      response<std::string, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), "test data has been written");
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, DeleteDataYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HEXISTS", "RedisCache/testName", "data");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
+    }
+
+    ASSERT_EQ(0, cacheDriver->delete_data(env->dpp, "testName", optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HEXISTS", "RedisCache/testName", "data");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, SetAttrsYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    rgw::sal::Attrs newAttrs;
+    bufferlist newVal;
+    newVal.append("newVal");
+    newAttrs.insert({"newAttr", newVal});
+     
+    newVal.clear();
+    newVal.append("nextVal");
+    newAttrs.insert({"nextAttr", newVal});
+
+    ASSERT_EQ(0, cacheDriver->set_attrs(env->dpp, "testName", newAttrs, optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HMGET", "RedisCache/testName", "newAttr", "nextAttr");
+    req.push("FLUSHALL");
+
+    response< std::vector<std::string>,
+              boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value()[0], "newVal");
+    EXPECT_EQ(std::get<0>(resp).value()[1], "nextVal");
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, GetAttrsYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    rgw::sal::Attrs nextAttrs = attrs;
+    bufferlist nextVal;
+    nextVal.append("nextVal");
+    nextAttrs.insert({"nextAttr", nextVal});
+
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), nextAttrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "RedisCache/testName", "attr", "newVal1", "nextAttr", "newVal2");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    rgw::sal::Attrs retAttrs;
+
+    ASSERT_EQ(0, cacheDriver->get_attrs(env->dpp, "testName", retAttrs, optional_yield{io, yield}));
+   
+    auto it = retAttrs.begin();
+    EXPECT_EQ(it->second.to_str(), "newVal1");
+    ++it;
+    EXPECT_EQ(it->second.to_str(), "newVal2");
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("FLUSHALL");
+      response<boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, UpdateAttrsYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    rgw::sal::Attrs newAttrs;
+    bufferlist newVal;
+    newVal.append("newVal");
+    newAttrs.insert({"attr", newVal});
+
+    ASSERT_EQ(0, cacheDriver->update_attrs(env->dpp, "testName", newAttrs, optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HGET", "RedisCache/testName", "attr");
+    req.push("FLUSHALL");
+    response<std::string, boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), "newVal");
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, DeleteAttrsYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HEXISTS", "RedisCache/testName", "attr");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 1);
+    }
+
+    rgw::sal::Attrs delAttrs;
+    bufferlist delVal;
+    delAttrs.insert({"attr", delVal});
+
+    ASSERT_GE(cacheDriver->delete_attrs(env->dpp, "testName", delAttrs, optional_yield{io, yield}), 0);
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HEXISTS", "RedisCache/testName", "attr");
+      req.push("FLUSHALL");
+      response<int, boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, SetAttrYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+    ASSERT_GE(cacheDriver->set_attr(env->dpp, "testName", "newAttr", "newVal", optional_yield{io, yield}), 0);
+    cacheDriver->shutdown();
+
+    boost::system::error_code ec;
+    request req;
+    req.push("HGET", "RedisCache/testName", "newAttr");
+    req.push("FLUSHALL");
+
+    response<std::string, boost::redis::ignore_t> resp;
+
+    conn->async_exec(req, resp, yield[ec]);
+
+    ASSERT_EQ((bool)ec, false);
+    EXPECT_EQ(std::get<0>(resp).value(), "newVal");
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+TEST_F(RedisDriverFixture, GetAttrYield)
+{
+  spawn::spawn(io, [this] (spawn::yield_context yield) {
+    ASSERT_EQ(0, cacheDriver->put(env->dpp, "testName", bl, bl.length(), attrs, optional_yield{io, yield}));
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("HSET", "RedisCache/testName", "attr", "newVal");
+      response<int> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+      EXPECT_EQ(std::get<0>(resp).value(), 0);
+    }
+
+    ASSERT_EQ("newVal", cacheDriver->get_attr(env->dpp, "testName", "attr", optional_yield{io, yield}));
+    cacheDriver->shutdown();
+
+    {
+      boost::system::error_code ec;
+      request req;
+      req.push("FLUSHALL");
+      response<boost::redis::ignore_t> resp;
+
+      conn->async_exec(req, resp, yield[ec]);
+
+      ASSERT_EQ((bool)ec, false);
+    }
+
+    conn->cancel();
+  });
+
+  io.run();
+}
+
+
+int main(int argc, char *argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  env = new Environment();
+  ::testing::AddGlobalTestEnvironment(env);
+
+  return RUN_ALL_TESTS();
+}