lg2: commit: add methods for new sdbusplus events

Add implementations and test cases for the `lg2::commit` functions.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I25a87fd65738e4debbe98f3473709f77d51777e9
diff --git a/lib/lg2_commit.cpp b/lib/lg2_commit.cpp
index 2c20311..60dd30e 100644
--- a/lib/lg2_commit.cpp
+++ b/lib/lg2_commit.cpp
@@ -1,21 +1,90 @@
+#include <sys/syslog.h>
+
+#include <nlohmann/json.hpp>
 #include <phosphor-logging/lg2/commit.hpp>
 #include <sdbusplus/async.hpp>
 #include <sdbusplus/exception.hpp>
+#include <xyz/openbmc_project/Logging/Create/client.hpp>
+#include <xyz/openbmc_project/Logging/Entry/client.hpp>
 
 namespace lg2::details
 {
 
-auto commit([[maybe_unused]] sdbusplus::exception::generated_event_base&& t)
-    -> sdbusplus::message::object_path
+using Create = sdbusplus::client::xyz::openbmc_project::logging::Create<>;
+using Entry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
+
+/* Convert syslog severity to Entry::Level */
+static auto severity_from_syslog(int s) -> Entry::Level
 {
-    return {};
+    switch (s)
+    {
+        case LOG_DEBUG:
+            return Entry::Level::Debug;
+
+        case LOG_INFO:
+            return Entry::Level::Informational;
+
+        case LOG_NOTICE:
+            return Entry::Level::Notice;
+
+        case LOG_WARNING:
+            return Entry::Level::Warning;
+
+        case LOG_ERR:
+            return Entry::Level::Error;
+
+        case LOG_CRIT:
+            return Entry::Level::Critical;
+
+        case LOG_ALERT:
+            return Entry::Level::Alert;
+
+        case LOG_EMERG:
+            return Entry::Level::Emergency;
+    }
+    return Entry::Level::Emergency;
 }
 
-auto commit([[maybe_unused]] sdbusplus::async::context& ctx,
-            [[maybe_unused]] sdbusplus::exception::generated_event_base&& t)
+using AdditionalData_t = std::map<std::string, std::string>;
+
+/* Create AdditionalData from the sdbusplus event json. */
+static auto data_from_json(sdbusplus::exception::generated_event_base& t)
+    -> AdditionalData_t
+{
+    AdditionalData_t result{};
+
+    auto j = t.to_json()[t.name()];
+    for (const auto& item : j.items())
+    {
+        result.emplace(item.key(), item.value().dump());
+    }
+
+    return result;
+}
+
+auto commit(sdbusplus::exception::generated_event_base&& t)
+    -> sdbusplus::message::object_path
+{
+    auto b = sdbusplus::bus::new_default();
+    auto m = b.new_method_call(Create::default_service, Create::instance_path,
+                               Create::interface, "Create");
+
+    m.append(t.name(), severity_from_syslog(t.severity()), data_from_json(t));
+
+    auto reply = b.call(m);
+
+    return reply.unpack<sdbusplus::message::object_path>();
+}
+
+auto commit(sdbusplus::async::context& ctx,
+            sdbusplus::exception::generated_event_base&& t)
     -> sdbusplus::async::task<sdbusplus::message::object_path>
 {
-    co_return {};
+    co_return co_await Create(ctx)
+        .service(Create::default_service)
+        .path(Create::instance_path)
+        .create(t.name(), severity_from_syslog(t.severity()),
+                data_from_json(t));
 }
 
 } // namespace lg2::details
diff --git a/test/basic_event_commit.cpp b/test/basic_event_commit.cpp
deleted file mode 100644
index fc454d9..0000000
--- a/test/basic_event_commit.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <phosphor-logging/commit.hpp>
-#include <xyz/openbmc_project/Logging/event.hpp>
-
-#include <gtest/gtest.h>
-
-namespace phosphor::logging::test
-{
-
-using LoggingCleared = sdbusplus::event::xyz::openbmc_project::Logging::Cleared;
-
-TEST(TestBasicEventCommit, GenerateSimpleEvent)
-{
-    EXPECT_THROW(
-        { throw LoggingCleared("NUMBER_OF_LOGS", 1); }, LoggingCleared);
-    return;
-}
-
-TEST(TestBasicEventCommit, CallCommit)
-{
-    lg2::commit(LoggingCleared("NUMBER_OF_LOGS", 3));
-}
-
-} // namespace phosphor::logging::test
diff --git a/test/log_manager_dbus_tests.cpp b/test/log_manager_dbus_tests.cpp
new file mode 100644
index 0000000..ac33ded
--- /dev/null
+++ b/test/log_manager_dbus_tests.cpp
@@ -0,0 +1,153 @@
+#include "config.h"
+
+#include "log_manager.hpp"
+#include "paths.hpp"
+
+#include <phosphor-logging/commit.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include <xyz/openbmc_project/Logging/Entry/client.hpp>
+#include <xyz/openbmc_project/Logging/event.hpp>
+
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace phosphor::logging::test
+{
+using LoggingCleared = sdbusplus::event::xyz::openbmc_project::Logging::Cleared;
+using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
+
+// Fixture to spawn the log-manager for dbus-based testing.
+class TestLogManagerDbus : public ::testing::Test
+{
+  protected:
+    // Create the daemon and sdbusplus::async::contexts.
+    void SetUp() override
+    {
+        // The daemon requires directories to be created first.
+        std::filesystem::create_directories(phosphor::logging::paths::error());
+
+        data = std::make_unique<fixture_data>();
+    }
+
+    // Stop the daemon, etc.
+    void TearDown() override
+    {
+        data.reset();
+    }
+
+    /** Run a client task, wait for it to complete, and stop daemon. */
+    template <typename T>
+    void run(T&& t)
+    {
+        data->client_ctx.spawn(std::move(t) | stdexec::then([this]() {
+                                   data->stop(data->client_ctx);
+                               }));
+        data->client_ctx.run();
+    }
+
+    // Data for the fixture.
+    struct fixture_data
+    {
+        fixture_data() :
+            client_ctx(), server_ctx(), objManager(server_ctx, OBJ_LOGGING),
+            iMgr(server_ctx, OBJ_INTERNAL), mgr(server_ctx, OBJ_LOGGING, iMgr)
+        {
+            // Create a thread for the daemon.
+            task = std::thread([this]() {
+                server_ctx.request_name(BUSNAME_LOGGING);
+                server_ctx.run();
+            });
+        }
+
+        ~fixture_data()
+        {
+            // Stop the server and wait for the thread to exit.
+            stop(server_ctx);
+            task.join();
+        }
+
+        // Spawn a task to gracefully shutdown an sdbusplus::async::context
+        static void stop(sdbusplus::async::context& ctx)
+        {
+            ctx.spawn(stdexec::just() |
+                      stdexec::then([&ctx]() { ctx.request_stop(); }));
+        }
+
+        sdbusplus::async::context client_ctx;
+        sdbusplus::async::context server_ctx;
+        sdbusplus::server::manager_t objManager;
+        internal::Manager iMgr;
+        Manager mgr;
+        std::thread task;
+    };
+
+    std::unique_ptr<fixture_data> data;
+};
+
+// Ensure we can successfully create and throw an sdbusplus event.
+TEST_F(TestLogManagerDbus, GenerateSimpleEvent)
+{
+    EXPECT_THROW(
+        { throw LoggingCleared("NUMBER_OF_LOGS", 1); }, LoggingCleared);
+    return;
+}
+
+// Call the synchronous version of the commit function and verify that the
+// daemon gives us a path.
+TEST_F(TestLogManagerDbus, CallCommitSync)
+{
+    auto path = lg2::commit(LoggingCleared("NUMBER_OF_LOGS", 3));
+    ASSERT_FALSE(path.str.empty());
+    EXPECT_THAT(path.str,
+                ::testing::StartsWith(
+                    std::filesystem::path(LoggingEntry::namespace_path::value) /
+                    LoggingEntry::namespace_path::entry));
+}
+
+// Call the asynchronous version of the commit function and verify that the
+// metadata is saved correctly.
+TEST_F(TestLogManagerDbus, CallCommitAsync)
+{
+    sdbusplus::message::object_path path{};
+    std::string log_count{};
+
+    auto create_log = [this, &path, &log_count]() -> sdbusplus::async::task<> {
+        // Log an event.
+        path = co_await lg2::commit(data->client_ctx,
+                                    LoggingCleared("NUMBER_OF_LOGS", 6));
+
+        // Grab the additional data.
+        auto additionalData = co_await LoggingEntry(data->client_ctx)
+                                  .service(Entry::default_service)
+                                  .path(path.str)
+                                  .additional_data();
+
+        // Extract the NUMBER_OF_LOGS.
+        for (const auto& value : additionalData)
+        {
+            if (value.starts_with("NUMBER_OF_LOGS="))
+            {
+                log_count = value.substr(value.find_first_of('=') + 1);
+            }
+        }
+
+        co_return;
+    };
+
+    run(create_log());
+
+    ASSERT_FALSE(path.str.empty());
+    ASSERT_FALSE(log_count.empty());
+
+    EXPECT_THAT(path.str,
+                ::testing::StartsWith(
+                    std::filesystem::path(LoggingEntry::namespace_path::value) /
+                    LoggingEntry::namespace_path::entry));
+
+    EXPECT_EQ(log_count, "6");
+}
+
+} // namespace phosphor::logging::test
diff --git a/test/meson.build b/test/meson.build
index bdf3cdb..822a4f8 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -24,8 +24,8 @@
 endif
 
 tests = [
-    'basic_event_commit',
     'extensions_test',
+    'log_manager_dbus_tests',
     'remote_logging_test_address',
     'remote_logging_test_config',
     'remote_logging_test_port',