]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/test/rgw/test_rgw_lua.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / rgw / test_rgw_lua.cc
index 0b5d1a72f6cd810911daf39eb3b20d9876a19432..a539c025b50778d41565a40d97ef1283323d58ab 100644 (file)
@@ -1,10 +1,12 @@
 #include <gtest/gtest.h>
 #include "common/ceph_context.h"
-#include "rgw/rgw_common.h"
-#include "rgw/rgw_auth.h"
-#include "rgw/rgw_process.h"
-#include "rgw/rgw_sal_rados.h"
-#include "rgw/rgw_lua_request.h"
+#include "rgw_common.h"
+#include "rgw_auth_registry.h"
+#include "rgw_process_env.h"
+#include "rgw_sal_rados.h"
+#include "rgw_lua_request.h"
+#include "rgw_lua_background.h"
+#include "rgw_lua_data_filter.h"
 
 using namespace std;
 using namespace rgw;
@@ -66,7 +68,7 @@ public:
   }
 };
 
-class TestUser : public sal::User {
+class TestUser : public sal::StoreUser {
 public:
   virtual std::unique_ptr<User> clone() override {
     return std::unique_ptr<User>(new TestUser(*this));
@@ -118,6 +120,9 @@ public:
   virtual int merge_and_store_attrs(const DoutPrefixProvider *dpp, rgw::sal::Attrs& attrs, optional_yield y) override {
     return 0;
   }
+  virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override {
+    return 0;
+  }
   virtual ~TestUser() = default;
 };
 
@@ -152,24 +157,26 @@ public:
   }
 };
 
-auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+auto g_cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
+
+CctCleaner cleaner(g_cct);
 
-CctCleaner cleaner(cct);
+tracing::Tracer tracer;
+
+#define DEFINE_REQ_STATE RGWProcessEnv pe; RGWEnv e; req_state s(g_cct, pe, &e, 0);
+#define INIT_TRACE tracer.init("test"); \
+                   s.trace = tracer.start_trace("test", true);
 
 TEST(TestRGWLua, EmptyScript)
 {
   const std::string script;
 
-  RGWEnv e;
-  uint64_t id = 0;
-  req_state s(cct, &e, id); 
+  DEFINE_REQ_STATE;
 
   const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
   ASSERT_EQ(rc, 0);
 }
 
-#define DEFINE_REQ_STATE RGWEnv e; req_state s(cct, &e, 0);
-
 TEST(TestRGWLua, SyntaxError)
 {
   const std::string script = R"(
@@ -267,7 +274,7 @@ TEST(TestRGWLua, SetResponse)
   ASSERT_EQ(rc, 0);
 }
 
-TEST(TestRGWLua, SetRGWId)
+TEST(TestRGWLua, RGWIdNotWriteable)
 {
   const std::string script = R"(
     assert(Request.RGWId == "foo")
@@ -340,6 +347,39 @@ TEST(TestRGWLua, Bucket)
   ASSERT_EQ(rc, 0);
 }
 
+TEST(TestRGWLua, WriteBucket)
+{
+  const std::string script = R"(
+    assert(Request.Bucket)
+    assert(Request.Bucket.Name == "myname")
+    Request.Bucket.Name = "othername"
+  )";
+
+  DEFINE_REQ_STATE;
+  s.init_state.url_bucket = "myname";
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+  ASSERT_EQ(s.init_state.url_bucket, "othername");
+}
+
+TEST(TestRGWLua, WriteBucketFail)
+{
+  const std::string script = R"(
+    assert(Request.Bucket)
+    assert(Request.Bucket.Name == "myname")
+    Request.Bucket.Name = "othername"
+  )";
+
+  DEFINE_REQ_STATE;
+  rgw_bucket b;
+  b.name = "myname";
+  s.bucket.reset(new sal::RadosBucket(nullptr, b));
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_NE(rc, 0);
+}
+
 TEST(TestRGWLua, GenericAttributes)
 {
   const std::string script = R"(
@@ -479,7 +519,7 @@ TEST(TestRGWLua, Acl)
   ACLOwner owner;
   owner.set_id(rgw_user("jack", "black"));
   owner.set_name("jack black");
-  s.user_acl.reset(new RGWAccessControlPolicy(cct));
+  s.user_acl.reset(new RGWAccessControlPolicy(g_cct));
   s.user_acl->set_owner(owner);
   ACLGrant grant1, grant2, grant3, grant4, grant5;
   grant1.set_canon(rgw_user("jane", "doe"), "her grant", 1);
@@ -593,46 +633,12 @@ TEST(TestRGWLua, NotAllowedInLib)
   const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
   ASSERT_NE(rc, 0);
 }
-#include <sys/socket.h>
-#include <stdlib.h>
-
-bool unix_socket_client_ended_ok = false;
-
-void unix_socket_client(const std::string& path) {
-  int fd;
-  // create the socket
-  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
-    std::cout << "unix socket error: " << errno << std::endl;
-    return;
-  }
-  // set the path
-  struct sockaddr_un addr;
-  memset(&addr, 0, sizeof(addr));
-  addr.sun_family = AF_UNIX;
-  strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path)-1);
-
-       // let the socket be created by the "rgw" side
-       std::this_thread::sleep_for(std::chrono::seconds(2));
-       if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
-               std::cout << "unix socket connect error: " << errno << std::endl;
-               return;
-       }
 
-  char buff[256];
-       int rc;
-       while((rc=read(fd, buff, sizeof(buff))) > 0) {
-               std::cout << std::string(buff, rc);
-    unix_socket_client_ended_ok = true;
-  }
-}
+#define MAKE_STORE auto store = std::unique_ptr<sal::RadosStore>(new sal::RadosStore); \
+                        store->setRados(new RGWRados);
 
 TEST(TestRGWLua, OpsLog)
 {
-       const std::string unix_socket_path = "./aSocket.sock";
-       unlink(unix_socket_path.c_str());
-
-       std::thread unix_socket_thread(unix_socket_client, unix_socket_path);
-
   const std::string script = R"(
                if Request.Response.HTTPStatusCode == 200 then
                        assert(Request.Response.Message == "Life is great")
@@ -642,10 +648,13 @@ TEST(TestRGWLua, OpsLog)
                end
   )";
 
-  auto store = std::unique_ptr<sal::RadosStore>(new sal::RadosStore);
-  store->setRados(new RGWRados);
-  auto olog = std::unique_ptr<OpsLogSocket>(new OpsLogSocket(cct, 1024));
-  ASSERT_TRUE(olog->init(unix_socket_path));
+  MAKE_STORE;
+
+  struct MockOpsLogSink : OpsLogSink {
+    bool logged = false;
+    int log(req_state*, rgw_log_entry&) override { logged = true; return 0; }
+  };
+  MockOpsLogSink olog;
 
   DEFINE_REQ_STATE;
   s.err.http_ret = 200;
@@ -669,16 +678,661 @@ TEST(TestRGWLua, OpsLog)
   s.auth.identity = std::unique_ptr<rgw::auth::Identity>(
                         new FakeIdentity());
 
-  auto rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, nullptr, script);
+  auto rc = lua::request::execute(store.get(), nullptr, &olog, &s, nullptr, script);
   EXPECT_EQ(rc, 0);
+  EXPECT_FALSE(olog.logged); // don't log http_ret=200
  
        s.err.http_ret = 400;
-  rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, nullptr, script);
+  rc = lua::request::execute(store.get(), nullptr, &olog, &s, nullptr, script);
   EXPECT_EQ(rc, 0);
+  EXPECT_TRUE(olog.logged);
+}
+
+class TestBackground : public rgw::lua::Background {
+  const unsigned read_time;
+
+protected:
+  int read_script() override {
+    // don't read the object from the store
+    std::this_thread::sleep_for(std::chrono::seconds(read_time));
+    return 0;
+  }
+
+public:
+  TestBackground(sal::RadosStore* store, const std::string& script, unsigned read_time = 0) : 
+    rgw::lua::Background(store, g_cct, "", /* luarocks path */ 1 /* run every second */),
+    read_time(read_time) {
+      // the script is passed in the constructor
+      rgw_script = script;
+    }
+
+  ~TestBackground() override {
+    shutdown();
+  }
+};
+
+TEST(TestRGWLuaBackground, Start)
+{
+  MAKE_STORE;
+  {
+    // ctr and dtor without running
+    TestBackground lua_background(store.get(), "");
+  }
+  {
+    // ctr and dtor with running
+    TestBackground lua_background(store.get(), "");
+    lua_background.start();
+  }
+}
+
+
+constexpr auto wait_time = std::chrono::seconds(3);
+
+template<typename T>
+const T& get_table_value(const TestBackground& b, const std::string& index) {
+  try {
+    return std::get<T>(b.get_table_value(index));
+  } catch (std::bad_variant_access const& ex) {
+    std::cout << "expected RGW[" << index << "] to be: " << typeid(T).name() << std::endl;
+    throw(ex);
+  }
+}
+
+TEST(TestRGWLuaBackground, Script)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "world"
+    RGW[key] = value
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), script);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "world");
+}
+
+TEST(TestRGWLuaBackground, RequestScript)
+{
+  const std::string background_script = R"(
+    local key = "hello"
+    local value = "from background"
+    RGW[key] = value
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), background_script);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+
+  const std::string request_script = R"(
+    local key = "hello"
+    assert(RGW[key] == "from background") 
+    local value = "from request"
+    RGW[key] = value
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  // to make sure test is consistent we have to puase the background
+  lua_background.pause();
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "from request");
+  // now we resume and let the background set the value
+  lua_background.resume(store.get());
+  std::this_thread::sleep_for(wait_time);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "from background");
+}
+
+TEST(TestRGWLuaBackground, Pause)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "1"
+    if RGW[key] then
+      RGW[key] = value..RGW[key]
+    else
+      RGW[key] = value
+    end
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), script);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+  EXPECT_GT(value_len, 0);
+  lua_background.pause();
+  std::this_thread::sleep_for(wait_time);
+  // no change in len
+  EXPECT_EQ(value_len, get_table_value<std::string>(lua_background, "hello").size());
+}
+
+TEST(TestRGWLuaBackground, PauseWhileReading)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "world"
+    RGW[key] = value
+    if RGW[key] then
+      RGW[key] = value..RGW[key]
+    else
+      RGW[key] = value
+    end
+  )";
+
+  MAKE_STORE;
+  constexpr auto long_wait_time = std::chrono::seconds(6);
+  TestBackground lua_background(store.get(), script, 2);
+  lua_background.start();
+  std::this_thread::sleep_for(long_wait_time);
+  const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+  EXPECT_GT(value_len, 0);
+  lua_background.pause();
+  std::this_thread::sleep_for(long_wait_time);
+  // one execution might occur after pause
+  EXPECT_TRUE(value_len + 1 >= get_table_value<std::string>(lua_background, "hello").size());
+}
+
+TEST(TestRGWLuaBackground, ReadWhilePaused)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "world"
+    RGW[key] = value
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), script);
+  lua_background.pause();
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "");
+  lua_background.resume(store.get());
+  std::this_thread::sleep_for(wait_time);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "hello"), "world");
+}
+
+TEST(TestRGWLuaBackground, PauseResume)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "1"
+    if RGW[key] then
+      RGW[key] = value..RGW[key]
+    else
+      RGW[key] = value
+    end
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), script);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+  EXPECT_GT(value_len, 0);
+  lua_background.pause();
+  std::this_thread::sleep_for(wait_time);
+  // no change in len
+  EXPECT_EQ(value_len, get_table_value<std::string>(lua_background, "hello").size());
+  lua_background.resume(store.get());
+  std::this_thread::sleep_for(wait_time);
+  // should be a change in len
+  EXPECT_GT(get_table_value<std::string>(lua_background, "hello").size(), value_len);
+}
+
+TEST(TestRGWLuaBackground, MultipleStarts)
+{
+  const std::string script = R"(
+    local key = "hello"
+    local value = "1"
+    if RGW[key] then
+      RGW[key] = value..RGW[key]
+    else
+      RGW[key] = value
+    end
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), script);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  const auto value_len = get_table_value<std::string>(lua_background, "hello").size();
+  EXPECT_GT(value_len, 0);
+  lua_background.start();
+  lua_background.shutdown();
+  lua_background.shutdown();
+  std::this_thread::sleep_for(wait_time);
+  lua_background.start();
+  std::this_thread::sleep_for(wait_time);
+  // should be a change in len
+  EXPECT_GT(get_table_value<std::string>(lua_background, "hello").size(), value_len);
+}
+
+TEST(TestRGWLuaBackground, TableValues)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = "string value"
+    RGW["key2"] = 42
+    RGW["key3"] = 42.2
+    RGW["key4"] = true
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+  EXPECT_TRUE(get_table_value<bool>(lua_background, "key4"));
+}
+
+TEST(TestRGWLuaBackground, TablePersist)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  std::string request_script = R"(
+    RGW["key1"] = "string value"
+    RGW["key2"] = 42
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  
+  request_script = R"(
+    RGW["key3"] = RGW["key1"]
+    RGW["key4"] = RGW["key2"]
+  )";
+  
+  rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key4"), 42);
+}
+
+TEST(TestRGWLuaBackground, TableValuesFromRequest)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+  lua_background.start();
+
+  const std::string request_script = R"(
+    RGW["key1"] = Request.Response.RGWCode
+    RGW["key2"] = Request.Response.Message
+    RGW["key3"] = Request.Response.RGWCode*0.1
+    RGW["key4"] = Request.Tags["key1"] == Request.Tags["key2"] 
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  s.tagset.add_tag("key1", "val1");
+  s.tagset.add_tag("key2", "val1");
+  s.err.ret = -99;
+  s.err.message = "hi";
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key1"), -99);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key2"), "hi");
+  EXPECT_EQ(get_table_value<double>(lua_background, "key3"), -9.9);
+  EXPECT_EQ(get_table_value<bool>(lua_background, "key4"), true);
+}
+
+TEST(TestRGWLuaBackground, TableInvalidValue)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+  lua_background.start();
+
+  const std::string request_script = R"(
+    RGW["key1"] = "val1"
+    RGW["key2"] = 42
+    RGW["key3"] = 42.2
+    RGW["key4"] = true
+    RGW["key5"] = Request.Tags
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+  s.tagset.add_tag("key1", "val1");
+  s.tagset.add_tag("key2", "val2");
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "val1");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+  EXPECT_EQ(get_table_value<bool>(lua_background, "key4"), true);
+}
+
+TEST(TestRGWLuaBackground, TableErase)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  std::string request_script = R"(
+    RGW["size"] = 0
+    RGW["key1"] = "string value"
+    RGW["key2"] = 42
+    RGW["key3"] = "another string value"
+    RGW["size"] = #RGW
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "another string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 4);
+  
+  request_script = R"(
+    -- erase key1
+    RGW["key1"] = nil
+    -- following should be a no op
+    RGW["key4"] = nil
+    RGW["size"] = #RGW
+  )";
+  
+  rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key3"), "another string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 3);
+}
+
+TEST(TestRGWLuaBackground, TableIterate)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = "string value"
+    RGW["key2"] = 42
+    RGW["key3"] = 42.2
+    RGW["key4"] = true
+    RGW["size"] = 0
+    for k, v in pairs(RGW) do
+      RGW["size"] = RGW["size"] + 1
+    end
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+  EXPECT_EQ(get_table_value<std::string>(lua_background, "key1"), "string value");
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "key2"), 42);
+  EXPECT_EQ(get_table_value<double>(lua_background, "key3"), 42.2);
+  EXPECT_TRUE(get_table_value<bool>(lua_background, "key4"));
+  EXPECT_EQ(get_table_value<long long int>(lua_background, "size"), 5);
+}
+
+TEST(TestRGWLuaBackground, TableIncrement)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = 42
+    RGW["key2"] = 42.2
+    RGW.increment("key1")
+    assert(RGW["key1"] == 43)
+    RGW.increment("key2")
+    assert(RGW["key2"] == 43.2)
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementBy)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = 42
+    RGW["key2"] = 42.2
+    RGW.increment("key1", 10)
+    assert(RGW["key1"] == 52)
+    RGW.increment("key2", 10)
+    assert(RGW["key2"] == 52.2)
+    RGW.increment("key1", 0.2)
+    assert(RGW["key1"] == 52.2)
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrement)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = 42
+    RGW["key2"] = 42.2
+    RGW.decrement("key1")
+    assert(RGW["key1"] == 41)
+    RGW.decrement("key2")
+    assert(RGW["key2"] == 41.2)
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrementBy)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  const std::string request_script = R"(
+    RGW["key1"] = 42
+    RGW["key2"] = 42.2
+    RGW.decrement("key1", 10)
+    assert(RGW["key1"] == 32)
+    RGW.decrement("key2", 10)
+    assert(RGW["key2"] == 32.2)
+    RGW.decrement("key1", 0.8)
+    assert(RGW["key1"] == 31.2)
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementValueError)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  std::string request_script = R"(
+    -- cannot increment string values
+    RGW["key1"] = "hello"
+    RGW.increment("key1")
+  )";
 
-       // give the socket client time to read
-       std::this_thread::sleep_for(std::chrono::seconds(5));
-       unix_socket_thread.detach(); // read is stuck there, so we cannot join
-  EXPECT_TRUE(unix_socket_client_ended_ok);
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+  
+  request_script = R"(
+    -- cannot increment bool values
+    RGW["key1"] = true
+    RGW.increment("key1")
+  )";
+
+  rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+  
+  request_script = R"(
+    -- cannot increment by string values
+    RGW["key1"] = 99
+    RGW.increment("key1", "kaboom")
+  )";
+
+  rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementError)
+{
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+
+  std::string request_script = R"(
+    -- missing argument
+    RGW["key1"] = 11
+    RGW.increment()
+  )";
+
+  DEFINE_REQ_STATE;
+  pe.lua.background = &lua_background;
+
+  auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+  
+  request_script = R"(
+    -- used as settable field
+    RGW.increment = 11
+  )";
+
+  rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, request_script);
+  ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, TracingSetAttribute)
+{
+  const std::string script = R"(
+    Request.Trace.SetAttribute("str-attr", "value")
+    Request.Trace.SetAttribute("int-attr", 42)
+    Request.Trace.SetAttribute("double-attr", 42.5)
+  )";
+
+  DEFINE_REQ_STATE;
+  INIT_TRACE;
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, TracingSetBadAttribute)
+{
+  const std::string script = R"(
+    Request.Trace.SetAttribute("attr", nil)
+  )";
+
+  DEFINE_REQ_STATE;
+  INIT_TRACE;
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  #ifdef HAVE_JAEGER
+   ASSERT_NE(rc, 0);
+  #else
+   ASSERT_EQ(rc, 0);
+  #endif
+}
+
+TEST(TestRGWLua, TracingAddEvent)
+{
+  const std::string script = R"(
+    event_attrs = {}
+    event_attrs["x"] = "value-x"
+    event_attrs[42] = 42
+    event_attrs[42.5] = 42.5
+    event_attrs["y"] = "value-y"
+
+    Request.Trace.AddEvent("my_event", event_attrs)
+  )";
+
+  DEFINE_REQ_STATE;
+  INIT_TRACE;
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, Data)
+{
+  const std::string script = R"(
+    local expected = "The quick brown fox jumps over the lazy dog"
+    local actual = ""
+    RGW["key1"] = 0
+    
+    for i, c in pairs(Data) do
+      actual = actual .. c
+      RGW.increment("key1")
+    end 
+    assert(expected == actual)
+    assert(#Data == #expected);
+    assert(RGW["key1"] == #Data)
+    assert(Request.RGWId == "foo")
+    assert(Offset == 12345678)
+  )";
+
+  MAKE_STORE;
+  TestBackground lua_background(store.get(), "");
+  DEFINE_REQ_STATE;
+  s.host_id = "foo";
+  pe.lua.background = &lua_background;
+  lua::RGWObjFilter filter(&s, script);
+  bufferlist bl;
+  bl.append("The quick brown fox jumps over the lazy dog");
+  off_t offset = 12345678;
+  const auto rc = filter.execute(bl, offset, "put_obj");
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, WriteDataFail)
+{
+  const std::string script = R"(
+    Data[1] = "h"
+    Data[2] = "e"
+    Data[3] = "l"
+    Data[4] = "l"
+    Data[5] = "o"
+  )";
+
+  DEFINE_REQ_STATE;
+  lua::RGWObjFilter filter(&s, script);
+  bufferlist bl;
+  bl.append("The quick brown fox jumps over the lazy dog");
+  const auto rc = filter.execute(bl, 0, "put_obj");
+  ASSERT_NE(rc, 0);
 }