requester: Modified MctpDiscovery class

Modified MctpDiscovery class to take list of managers instead of single
fwManager. The change is for adding platform-mc manager.
Added loadStaticEndpoints API for MCTP layer which doesn't implement
/xyz/openbmc_project/MCTP/Endpoint.Interface

The patch is part of implementation of design document below.
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/47252

Signed-off-by: Gilbert Chen <gilbert.chen@arm.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I1e1673504583a87f2a9bc3adf76fb49c2dc30254
diff --git a/common/types.hpp b/common/types.hpp
index 0c7514c..90c94c6 100644
--- a/common/types.hpp
+++ b/common/types.hpp
@@ -16,10 +16,16 @@
 {
 
 using eid = uint8_t;
+using UUID = std::string;
 using Request = std::vector<uint8_t>;
 using Response = std::vector<uint8_t>;
 using Command = uint8_t;
 
+using MctpMedium = std::string;
+using NetworkId = uint32_t;
+using MctpInfo = std::tuple<eid, UUID, MctpMedium, NetworkId>;
+using MctpInfos = std::vector<MctpInfo>;
+
 namespace dbus
 {
 
diff --git a/common/utils.cpp b/common/utils.cpp
index 9eed1be..e9b2c68 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -569,6 +569,18 @@
     return bus.call(method).unpack<ObjectValueTree>();
 }
 
+PropertyMap
+    DBusHandler::getDbusPropertiesVariant(const char* serviceName,
+                                          const char* objPath,
+                                          const char* dbusInterface) const
+{
+    auto& bus = DBusHandler::getBus();
+    auto method = bus.new_method_call(serviceName, objPath, dbusProperties,
+                                      "GetAll");
+    method.append(dbusInterface);
+    return bus.call(method, dbusTimeout).unpack<PropertyMap>();
+}
+
 PropertyValue jsonEntryToDbusVal(std::string_view type,
                                  const nlohmann::json& value)
 {
diff --git a/common/utils.hpp b/common/utils.hpp
index c2bdb0d..10038b4 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -16,6 +16,7 @@
 #include <sdbusplus/server.hpp>
 #include <xyz/openbmc_project/Inventory/Manager/client.hpp>
 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
 
 #include <deque>
 #include <exception>
@@ -46,6 +47,14 @@
 using Entities = std::vector<pldm_entity_node*>;
 using EntityAssociations = std::vector<Entities>;
 using ObjectPathMaps = std::map<fs::path, pldm_entity_node*>;
+using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
+
+using inventoryManager =
+    sdbusplus::client::xyz::openbmc_project::inventory::Manager<>;
+
+constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
+constexpr auto mapperService = ObjectMapper::default_service;
+constexpr auto inventoryPath = "/xyz/openbmc_project/inventory";
 
 const std::map<EntityType, EntityName> entityMaps = {
     {PLDM_ENTITY_SYSTEM_CHASSIS, "chassis"},
@@ -166,13 +175,6 @@
     return bcd;
 }
 
-using inventoryManager =
-    sdbusplus::client::xyz::openbmc_project::inventory::Manager<>;
-
-constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
-constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
-constexpr auto inventoryPath = "/xyz/openbmc_project/inventory";
-
 struct DBusMapping
 {
     std::string objectPath;   //!< D-Bus object path
@@ -220,6 +222,10 @@
     virtual PropertyValue
         getDbusPropertyVariant(const char* objPath, const char* dbusProp,
                                const char* dbusInterface) const = 0;
+
+    virtual PropertyMap
+        getDbusPropertiesVariant(const char* serviceName, const char* objPath,
+                                 const char* dbusInterface) const = 0;
 };
 
 /**
@@ -284,6 +290,20 @@
         getDbusPropertyVariant(const char* objPath, const char* dbusProp,
                                const char* dbusInterface) const override;
 
+    /** @brief Get All properties(type: variant) from the requested dbus
+     *
+     *  @param[in] serviceName - The Dbus service name
+     *  @param[in] objPath - The Dbus object path
+     *  @param[in] dbusInterface - The Dbus interface
+     *
+     *  @return The values of the properties(type: variant)
+     *
+     *  @throw sdbusplus::exception_t when it fails
+     */
+    PropertyMap
+        getDbusPropertiesVariant(const char* serviceName, const char* objPath,
+                                 const char* dbusInterface) const override;
+
     /** @brief The template function to get property from the requested dbus
      *         path
      *
diff --git a/fw-update/manager.hpp b/fw-update/manager.hpp
index 018a703..be45eb7 100644
--- a/fw-update/manager.hpp
+++ b/fw-update/manager.hpp
@@ -6,6 +6,7 @@
 #include "device_updater.hpp"
 #include "inventory_manager.hpp"
 #include "requester/handler.hpp"
+#include "requester/mctp_endpoint_discovery.hpp"
 #include "update_manager.hpp"
 
 #include <unordered_map>
@@ -22,7 +23,7 @@
  * This class handles all the aspects of the PLDM FW update specification for
  * the MCTP devices
  */
-class Manager
+class Manager : public pldm::MctpDiscoveryHandlerIntf
 {
   public:
     Manager() = delete;
@@ -44,18 +45,32 @@
                       componentInfoMap)
     {}
 
-    /** @brief Discover MCTP endpoints that support the PLDM firmware update
-     *         specification
+    /** @brief Helper function to invoke registered handlers for
+     *         the added MCTP endpoints
      *
-     *  @param[in] eids - Array of MCTP endpoints
-     *
-     *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
+     *  @param[in] mctpInfos - information of discovered MCTP endpoints
      */
-    void handleMCTPEndpoints(const std::vector<mctp_eid_t>& eids)
+    void handleMctpEndpoints(const MctpInfos& mctpInfos)
     {
+        std::vector<mctp_eid_t> eids;
+        for (const auto& mctpInfo : mctpInfos)
+        {
+            eids.emplace_back(std::get<mctp_eid_t>(mctpInfo));
+        }
+
         inventoryMgr.discoverFDs(eids);
     }
 
+    /** @brief Helper function to invoke registered handlers for
+     *         the removed MCTP endpoints
+     *
+     *  @param[in] mctpInfos - information of removed MCTP endpoints
+     */
+    void handleRemovedMctpEndpoints(const MctpInfos&)
+    {
+        return;
+    }
+
     /** @brief Handle PLDM request for the commands in the FW update
      *         specification
      *
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index 1dd5de8..0adba8c 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -308,7 +308,9 @@
     std::unique_ptr<fw_update::Manager> fwManager =
         std::make_unique<fw_update::Manager>(event, reqHandler, instanceIdDb);
     std::unique_ptr<MctpDiscovery> mctpDiscoveryHandler =
-        std::make_unique<MctpDiscovery>(bus, fwManager.get());
+        std::make_unique<MctpDiscovery>(
+            bus,
+            std::initializer_list<MctpDiscoveryHandlerIntf*>{fwManager.get()});
     auto callback = [verbose, &invoker, &reqHandler, &fwManager, &pldmTransport,
                      TID](IO& io, int fd, uint32_t revents) mutable {
         if (!(revents & EPOLLIN))
diff --git a/requester/mctp_endpoint_discovery.cpp b/requester/mctp_endpoint_discovery.cpp
index a962228..9440ed0 100644
--- a/requester/mctp_endpoint_discovery.cpp
+++ b/requester/mctp_endpoint_discovery.cpp
@@ -1,100 +1,214 @@
+#include "config.h"
+
 #include "mctp_endpoint_discovery.hpp"
 
 #include "common/types.hpp"
 #include "common/utils.hpp"
 
+#include <phosphor-logging/lg2.hpp>
+
 #include <algorithm>
+#include <fstream>
+#include <iostream>
 #include <map>
 #include <string>
 #include <string_view>
 #include <vector>
 
+using namespace sdbusplus::bus::match::rules;
+
+PHOSPHOR_LOG2_USING;
+
 namespace pldm
 {
-MctpDiscovery::MctpDiscovery(sdbusplus::bus_t& bus,
-                             fw_update::Manager* fwManager) :
+MctpDiscovery::MctpDiscovery(
+    sdbusplus::bus::bus& bus,
+    std::initializer_list<MctpDiscoveryHandlerIntf*> list) :
     bus(bus),
-    fwManager(fwManager),
-    mctpEndpointSignal(bus,
-                       sdbusplus::bus::match::rules::interfacesAdded(
-                           "/xyz/openbmc_project/mctp"),
-                       std::bind_front(&MctpDiscovery::dicoverEndpoints, this))
+    mctpEndpointAddedSignal(
+        bus, interfacesAdded(MCTPPath),
+        std::bind_front(&MctpDiscovery::discoverEndpoints, this)),
+    mctpEndpointRemovedSignal(
+        bus, interfacesRemoved(MCTPPath),
+        std::bind_front(&MctpDiscovery::removeEndpoints, this)),
+    handlers(list)
 {
-    pldm::utils::ObjectValueTree objects;
+    getMctpInfos(existingMctpInfos);
+    handleMctpEndpoints(existingMctpInfos);
+}
 
+void MctpDiscovery::getMctpInfos(MctpInfos& mctpInfos)
+{
+    // Find all implementations of the MCTP Endpoint interface
+    pldm::utils::GetSubTreeResponse mapperResponse;
     try
     {
-        objects = pldm::utils::DBusHandler::getManagedObj(MCTPService,
-                                                          MCTPPath);
+        mapperResponse = pldm::utils::DBusHandler().getSubtree(
+            MCTPPath, 0, std::vector<std::string>({MCTPInterface}));
     }
-    catch (const std::exception& e)
+    catch (const sdbusplus::exception_t& e)
     {
-        error("Failed to call the D-Bus Method: {ERROR}", "ERROR", e);
+        error("getSubtree call failed with, {ERROR} {PATH} {INTERFACE}",
+              "ERROR", e, "PATH", MCTPPath, "INTERFACE", MCTPInterface);
         return;
     }
 
-    std::vector<mctp_eid_t> eids;
-
-    for (const auto& [objectPath, interfaces] : objects)
+    for (const auto& [path, services] : mapperResponse)
     {
-        for (const auto& [intfName, properties] : interfaces)
+        for (const auto& serviceIter : services)
         {
-            if (intfName == mctpEndpointIntfName)
+            const std::string& service = serviceIter.first;
+            try
             {
-                if (properties.contains("EID") &&
+                auto properties =
+                    pldm::utils::DBusHandler().getDbusPropertiesVariant(
+                        service.c_str(), path.c_str(), MCTPInterface);
+
+                if (properties.contains("NetworkId") &&
+                    properties.contains("EID") &&
                     properties.contains("SupportedMessageTypes"))
                 {
+                    auto networkId =
+                        std::get<NetworkId>(properties.at("NetworkId"));
                     auto eid = std::get<mctp_eid_t>(properties.at("EID"));
                     auto types = std::get<std::vector<uint8_t>>(
                         properties.at("SupportedMessageTypes"));
                     if (std::find(types.begin(), types.end(), mctpTypePLDM) !=
                         types.end())
                     {
-                        eids.emplace_back(eid);
+                        info("Adding Endpoint networkId={NETWORK} EID={EID}",
+                             "NETWORK", networkId, "EID", unsigned(eid));
+                        mctpInfos.emplace_back(
+                            MctpInfo(eid, emptyUUID, "", networkId));
                     }
                 }
             }
+            catch (const sdbusplus::exception_t& e)
+            {
+                error(
+                    "Error reading MCTP Endpoint property, {ERROR} {SERVICE} {PATH}",
+                    "ERROR", e, "SERVICE", service, "PATH", path);
+                return;
+            }
         }
     }
-
-    if (eids.size() && fwManager)
-    {
-        fwManager->handleMCTPEndpoints(eids);
-    }
 }
 
-void MctpDiscovery::dicoverEndpoints(sdbusplus::message_t& msg)
+void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg,
+                                      MctpInfos& mctpInfos)
 {
-    constexpr std::string_view mctpEndpointIntfName{
-        "xyz.openbmc_project.MCTP.Endpoint"};
-    std::vector<mctp_eid_t> eids;
+    using ObjectPath = sdbusplus::message::object_path;
+    ObjectPath objPath;
+    using Property = std::string;
+    using PropertyMap = std::map<Property, dbus::Value>;
+    std::map<std::string, PropertyMap> interfaces;
 
-    sdbusplus::message::object_path objPath;
-    std::map<std::string, std::map<std::string, dbus::Value>> interfaces;
-    msg.read(objPath, interfaces);
+    try
+    {
+        msg.read(objPath, interfaces);
+    }
+    catch (const sdbusplus::exception_t& e)
+    {
+        error("Error reading MCTP Endpoint addedInterace message, {ERROR}",
+              "ERROR", e);
+        return;
+    }
 
     for (const auto& [intfName, properties] : interfaces)
     {
-        if (intfName == mctpEndpointIntfName)
+        if (intfName == MCTPInterface)
         {
-            if (properties.contains("EID") &&
+            if (properties.contains("NetworkId") &&
+                properties.contains("EID") &&
                 properties.contains("SupportedMessageTypes"))
             {
+                auto networkId =
+                    std::get<NetworkId>(properties.at("NetworkId"));
                 auto eid = std::get<mctp_eid_t>(properties.at("EID"));
                 auto types = std::get<std::vector<uint8_t>>(
                     properties.at("SupportedMessageTypes"));
                 if (std::find(types.begin(), types.end(), mctpTypePLDM) !=
                     types.end())
                 {
-                    eids.emplace_back(eid);
+                    info("Adding Endpoint networkId={NETWORK} EID={EID}",
+                         "NETWORK", networkId, "EID", unsigned(eid));
+                    mctpInfos.emplace_back(
+                        MctpInfo(eid, emptyUUID, "", networkId));
                 }
             }
         }
     }
+}
 
-    if (eids.size() && fwManager)
+void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos)
+{
+    for (const auto& mctpInfo : addedInfos)
     {
-        fwManager->handleMCTPEndpoints(eids);
+        if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(),
+                      mctpInfo) == existingMctpInfos.end())
+        {
+            existingMctpInfos.emplace_back(mctpInfo);
+        }
+    }
+}
+
+void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos,
+                                                MctpInfos& removedInfos)
+{
+    for (const auto& mctpInfo : existingMctpInfos)
+    {
+        if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) ==
+            mctpInfos.end())
+        {
+            removedInfos.emplace_back(mctpInfo);
+        }
+    }
+    for (const auto& mctpInfo : removedInfos)
+    {
+        info("Removing Endpoint networkId={NETWORK} EID={EID}", "NETWORK",
+             std::get<3>(mctpInfo), "EID", unsigned(std::get<0>(mctpInfo)));
+        existingMctpInfos.erase(std::remove(existingMctpInfos.begin(),
+                                            existingMctpInfos.end(), mctpInfo),
+                                existingMctpInfos.end());
+    }
+}
+
+void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg)
+{
+    MctpInfos addedInfos;
+    getAddedMctpInfos(msg, addedInfos);
+    addToExistingMctpInfos(addedInfos);
+    handleMctpEndpoints(addedInfos);
+}
+
+void MctpDiscovery::removeEndpoints(sdbusplus::message_t&)
+{
+    MctpInfos mctpInfos;
+    MctpInfos removedInfos;
+    getMctpInfos(mctpInfos);
+    removeFromExistingMctpInfos(mctpInfos, removedInfos);
+    handleRemovedMctpEndpoints(removedInfos);
+}
+
+void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos)
+{
+    for (const auto& handler : handlers)
+    {
+        if (handler)
+        {
+            handler->handleMctpEndpoints(mctpInfos);
+        }
+    }
+}
+
+void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos)
+{
+    for (const auto& handler : handlers)
+    {
+        if (handler)
+        {
+            handler->handleRemovedMctpEndpoints(mctpInfos);
+        }
     }
 }
 
diff --git a/requester/mctp_endpoint_discovery.hpp b/requester/mctp_endpoint_discovery.hpp
index de3a6bd..675b6c7 100644
--- a/requester/mctp_endpoint_discovery.hpp
+++ b/requester/mctp_endpoint_discovery.hpp
@@ -1,14 +1,36 @@
 #pragma once
 
-#include "fw-update/manager.hpp"
+#include "common/types.hpp"
+#include "common/utils.hpp"
+
+#include <libpldm/pldm.h>
 
 #include <sdbusplus/bus/match.hpp>
 
+#include <filesystem>
+#include <initializer_list>
+#include <vector>
+
 namespace pldm
 {
 
-constexpr auto MCTPService = "xyz.openbmc_project.MCTP";
-constexpr auto MCTPPath = "/xyz/openbmc_project/mctp";
+const std::string emptyUUID = "00000000-0000-0000-0000-000000000000";
+constexpr const char* MCTPService = "xyz.openbmc_project.MCTP";
+constexpr const char* MCTPInterface = "xyz.openbmc_project.MCTP.Endpoint";
+constexpr const char* MCTPPath = "/xyz/openbmc_project/mctp";
+
+/** @class MctpDiscoveryHandlerIntf
+ *
+ * This abstract class defines the APIs for MctpDiscovery class has common
+ * interface to execute function from different Manager Classes
+ */
+class MctpDiscoveryHandlerIntf
+{
+  public:
+    virtual void handleMctpEndpoints(const MctpInfos& mctpInfos) = 0;
+    virtual void handleRemovedMctpEndpoints(const MctpInfos& mctpInfos) = 0;
+    virtual ~MctpDiscoveryHandlerIntf() {}
+};
 
 class MctpDiscovery
 {
@@ -24,26 +46,85 @@
      *         MCTP enabled devices
      *
      *  @param[in] bus - reference to systemd bus
-     *  @param[in] fwManager - pointer to the firmware manager
+     *  @param[in] list - initializer list to the MctpDiscoveryHandlerIntf
      */
-    explicit MctpDiscovery(sdbusplus::bus_t& bus,
-                           fw_update::Manager* fwManager);
+    explicit MctpDiscovery(
+        sdbusplus::bus::bus& bus,
+        std::initializer_list<MctpDiscoveryHandlerIntf*> list);
 
-  private:
     /** @brief reference to the systemd bus */
     sdbusplus::bus_t& bus;
 
-    fw_update::Manager* fwManager;
-
     /** @brief Used to watch for new MCTP endpoints */
-    sdbusplus::bus::match_t mctpEndpointSignal;
+    sdbusplus::bus::match_t mctpEndpointAddedSignal;
 
-    void dicoverEndpoints(sdbusplus::message_t& msg);
+    /** @brief Used to watch for the removed MCTP endpoints */
+    sdbusplus::bus::match_t mctpEndpointRemovedSignal;
 
+    /** @brief List of handlers need to notify when new MCTP
+     * Endpoint is Added/Removed */
+    std::vector<MctpDiscoveryHandlerIntf*> handlers;
+
+    /** @brief The existing MCTP endpoints */
+    MctpInfos existingMctpInfos;
+
+    /** @brief Callback function when MCTP endpoints addedInterface
+     * D-Bus signal raised.
+     *
+     *  @param[in] msg - Data associated with subscribed signal
+     */
+    void discoverEndpoints(sdbusplus::message_t& msg);
+
+    /** @brief Callback function when MCTP endpoint removedInterface
+     * D-Bus signal raised.
+     *
+     *  @param[in] msg - Data associated with subscribed signal
+     */
+    void removeEndpoints(sdbusplus::message_t& msg);
+
+    /** @brief Helper function to invoke registered handlers for
+     *  the added MCTP endpoints
+     *
+     *  @param[in] mctpInfos - information of discovered MCTP endpoints
+     */
+    void handleMctpEndpoints(const MctpInfos& mctpInfos);
+
+    /** @brief Helper function to invoke registered handlers for
+     *  the removed MCTP endpoints
+     *
+     *  @param[in] mctpInfos - information of removed MCTP endpoints
+     */
+    void handleRemovedMctpEndpoints(const MctpInfos& mctpInfos);
+
+    /** @brief Get list of MctpInfos in MCTP control interface.
+     *
+     *  @param[in] mctpInfos - information of discovered MCTP endpoints
+     */
+    void getMctpInfos(MctpInfos& mctpInfos);
+
+    /** @brief Get list of new MctpInfos in addedInterace D-Bus signal message.
+     *
+     *  @param[in] msg - addedInterace D-Bus signal message
+     *  @param[in] mctpInfos - information of added MCTP endpoints
+     */
+    void getAddedMctpInfos(sdbusplus::message_t& msg, MctpInfos& mctpInfos);
+
+    /** @brief Add new MctpInfos to existingMctpInfos.
+     *
+     *  @param[in] mctpInfos - information of new MCTP endpoints
+     */
+    void addToExistingMctpInfos(const MctpInfos& mctpInfos);
+
+    /** @brief Erase the removed MCTP endpoint from existingMctpInfos.
+     *
+     *  @param[in] mctpInfos - the remaining MCTP endpoints
+     *  @param[out] removedInfos - the removed MCTP endpoints
+     */
+    void removeFromExistingMctpInfos(MctpInfos& mctpInfos,
+                                     MctpInfos& removedInfos);
+
+  private:
     static constexpr uint8_t mctpTypePLDM = 1;
-
-    static constexpr std::string_view mctpEndpointIntfName{
-        "xyz.openbmc_project.MCTP.Endpoint"};
 };
 
 } // namespace pldm
diff --git a/requester/test/mctp_endpoint_discovery_test.cpp b/requester/test/mctp_endpoint_discovery_test.cpp
new file mode 100644
index 0000000..df390a0
--- /dev/null
+++ b/requester/test/mctp_endpoint_discovery_test.cpp
@@ -0,0 +1,137 @@
+#include "config.h"
+
+#include "common/utils.hpp"
+#include "requester/test/mock_mctp_discovery_handler_intf.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::_;
+
+TEST(MctpEndpointDiscoveryTest, SingleHandleMctpEndpoint)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+
+    EXPECT_CALL(manager, handleMctpEndpoints(_)).Times(1);
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler = nullptr;
+}
+
+TEST(MctpEndpointDiscoveryTest, MultipleHandleMctpEndpoints)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager1;
+    pldm::MockManager manager2;
+
+    EXPECT_CALL(manager1, handleMctpEndpoints(_)).Times(1);
+    EXPECT_CALL(manager2, handleMctpEndpoints(_)).Times(1);
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager1,
+                                                                    &manager2});
+    mctpDiscoveryHandler = nullptr;
+}
+
+TEST(MctpEndpointDiscoveryTest, goodGetMctpInfos)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+    pldm::MctpInfos mctpInfos;
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler->getMctpInfos(mctpInfos);
+    EXPECT_EQ(mctpInfos.size(), 0);
+}
+
+TEST(MctpEndpointDiscoveryTest, goodAddToExistingMctpInfos)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+    const pldm::MctpInfos& mctpInfos = {
+        pldm::MctpInfo(11, pldm::emptyUUID, "", 1),
+        pldm::MctpInfo(12, pldm::emptyUUID, "abc", 1)};
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler->addToExistingMctpInfos(mctpInfos);
+    EXPECT_EQ(mctpDiscoveryHandler->existingMctpInfos.size(), 2);
+    pldm::MctpInfo mctpInfo = mctpDiscoveryHandler->existingMctpInfos.back();
+    EXPECT_EQ(std::get<0>(mctpInfo), 12);
+    EXPECT_EQ(std::get<2>(mctpInfo), "abc");
+    EXPECT_EQ(std::get<3>(mctpInfo), 1);
+}
+
+TEST(MctpEndpointDiscoveryTest, badAddToExistingMctpInfos)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+    const pldm::MctpInfos& mctpInfos = {
+        pldm::MctpInfo(11, pldm::emptyUUID, "", 1)};
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler->addToExistingMctpInfos(mctpInfos);
+    EXPECT_NE(mctpDiscoveryHandler->existingMctpInfos.size(), 2);
+}
+
+TEST(MctpEndpointDiscoveryTest, goodRemoveFromExistingMctpInfos)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+    const pldm::MctpInfos& mctpInfos = {
+        pldm::MctpInfo(11, pldm::emptyUUID, "def", 2),
+        pldm::MctpInfo(12, pldm::emptyUUID, "abc", 1)};
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler->addToExistingMctpInfos(mctpInfos);
+    EXPECT_EQ(mctpDiscoveryHandler->existingMctpInfos.size(), 2);
+    pldm::MctpInfo mctpInfo = mctpDiscoveryHandler->existingMctpInfos.back();
+    EXPECT_EQ(std::get<0>(mctpInfo), 12);
+    EXPECT_EQ(std::get<2>(mctpInfo), "abc");
+    EXPECT_EQ(std::get<3>(mctpInfo), 1);
+    pldm::MctpInfos removedInfos;
+    pldm::MctpInfos remainMctpInfos;
+    remainMctpInfos.emplace_back(pldm::MctpInfo(12, pldm::emptyUUID, "abc", 1));
+
+    mctpDiscoveryHandler->removeFromExistingMctpInfos(remainMctpInfos,
+                                                      removedInfos);
+    EXPECT_EQ(mctpDiscoveryHandler->existingMctpInfos.size(), 1);
+    mctpInfo = mctpDiscoveryHandler->existingMctpInfos.back();
+    EXPECT_EQ(std::get<0>(mctpInfo), 12);
+    EXPECT_EQ(std::get<2>(mctpInfo), "abc");
+    EXPECT_EQ(std::get<3>(mctpInfo), 1);
+    EXPECT_EQ(removedInfos.size(), 1);
+    mctpInfo = removedInfos.back();
+    EXPECT_EQ(std::get<0>(mctpInfo), 11);
+    EXPECT_EQ(std::get<2>(mctpInfo), "def");
+    EXPECT_EQ(std::get<3>(mctpInfo), 2);
+}
+
+TEST(MctpEndpointDiscoveryTest, goodRemoveEndpoints)
+{
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    pldm::MockManager manager;
+    const pldm::MctpInfos& mctpInfos = {
+        pldm::MctpInfo(11, pldm::emptyUUID, "def", 2),
+        pldm::MctpInfo(12, pldm::emptyUUID, "abc", 1)};
+
+    auto mctpDiscoveryHandler = std::make_unique<pldm::MctpDiscovery>(
+        bus, std::initializer_list<pldm::MctpDiscoveryHandlerIntf*>{&manager});
+    mctpDiscoveryHandler->addToExistingMctpInfos(mctpInfos);
+    EXPECT_EQ(mctpDiscoveryHandler->existingMctpInfos.size(), 2);
+    pldm::MctpInfo mctpInfo = mctpDiscoveryHandler->existingMctpInfos.back();
+    EXPECT_EQ(std::get<0>(mctpInfo), 12);
+    EXPECT_EQ(std::get<2>(mctpInfo), "abc");
+    EXPECT_EQ(std::get<3>(mctpInfo), 1);
+    sdbusplus::message_t msg = sdbusplus::bus::new_default().new_method_call(
+        "xyz.openbmc_project.sdbusplus.test.Object",
+        "/xyz/openbmc_project/sdbusplus/test/object",
+        "xyz.openbmc_project.sdbusplus.test.Object", "Unused");
+    mctpDiscoveryHandler->removeEndpoints(msg);
+    EXPECT_EQ(mctpDiscoveryHandler->existingMctpInfos.size(), 0);
+}
diff --git a/requester/test/meson.build b/requester/test/meson.build
index 29f4303..a08a990 100644
--- a/requester/test/meson.build
+++ b/requester/test/meson.build
@@ -1,6 +1,13 @@
+test_src = declare_dependency(
+          sources: [
+            '../mctp_endpoint_discovery.cpp',
+            '../../common/utils.cpp',
+          ])
+
 tests = [
   'handler_test',
   'request_test',
+  'mctp_endpoint_discovery_test',
 ]
 
 foreach t : tests
@@ -16,6 +23,7 @@
                          phosphor_logging_dep,
                          sdbusplus,
                          sdeventplus,
+                         test_src,
                     ]),
        workdir: meson.current_source_dir())
 endforeach
diff --git a/requester/test/mock_mctp_discovery_handler_intf.hpp b/requester/test/mock_mctp_discovery_handler_intf.hpp
new file mode 100644
index 0000000..8aada96
--- /dev/null
+++ b/requester/test/mock_mctp_discovery_handler_intf.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "requester/mctp_endpoint_discovery.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace pldm
+{
+
+class MockManager : public pldm::MctpDiscoveryHandlerIntf
+{
+  public:
+    MOCK_METHOD(void, handleMctpEndpoints, (const MctpInfos& mctpInfos),
+                (override));
+    MOCK_METHOD(void, handleRemovedMctpEndpoints, (const MctpInfos& mctpInfos),
+                (override));
+};
+
+} // namespace pldm