Add unit test for certificate install
Change-Id: I25700842983be152edc18f78912a917220867b0b
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index a597c38..7ff740a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,3 +21,5 @@
phosphor_certificate_manager_CXXFLAGS = \
$(SYSTEMD_CFLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+SUBDIRS = . test
diff --git a/certs_manager.hpp b/certs_manager.hpp
index a23f044..f001df7 100644
--- a/certs_manager.hpp
+++ b/certs_manager.hpp
@@ -68,10 +68,10 @@
private:
/** @brief Client certificate Installation helper function **/
- void clientInstall();
+ virtual void clientInstall();
/** @brief Server certificate Installation helper function **/
- void serverInstall();
+ virtual void serverInstall();
/** @brief systemd unit reload helper function
* @param[in] unit - service need to reload.
diff --git a/configure.ac b/configure.ac
index 02a0a94..da35058 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,6 +29,27 @@
# Code coverage
AX_CODE_COVERAGE
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+AC_ARG_ENABLE([oe-sdk],
+ AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.])
+)
+AC_ARG_VAR(OECORE_TARGET_SYSROOT,
+ [Path to the OE SDK SYSROOT])
+AS_IF([test "x$enable_oe_sdk" == "xyes"],
+ AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"],
+ AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk])
+ )
+ AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT])
+ [
+ testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib"
+ testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib"
+ testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`"
+ ]
+ AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
+)
AC_ARG_VAR(BUSNAME, [The D-Bus busname to own])
AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.Certs.Manager"])
AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The D-Bus busname to own])
@@ -37,5 +58,5 @@
AC_DEFINE_UNQUOTED([OBJPATH], ["$OBJPATH"], [The certificate manager D-Bus root])
# Create configured output
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..814966e
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,33 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(PTHREAD_CPPFLAGS) \
+ $(SDBUSPLUS_CPPFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CPPFLAGS)
+
+AM_CFLAGS = \
+ $(PTHREAD_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+AM_CXXFLAGS = \
+ $(PTHREAD_CXXFLAGS) \
+ $(SDBUSPLUS_CXXFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CXXFLAGS)
+
+AM_LDFLAGS = \
+ -lstdc++fs \
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ -lgtest -lgtest_main -lgmock $(PTHREAD_CFLAGS) $(OESDK_TESTCASE_FLAGS)
+
+check_PROGRAMS =
+
+# Run all 'check' test programs
+TESTS = $(check_PROGRAMS)
+
+# Build/add certs_manager_test to test suite
+check_PROGRAMS += certs_manager_test
+certs_manager_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_CPPFLAGS)
+certs_manager_test_LDFLAGS = $(AM_LDFLAGS) $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS)
+certs_manager_test_SOURCES = certs_manager_test.cpp
+certs_manager_test_LDADD = $(top_builddir)/certs_manager.o
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
new file mode 100644
index 0000000..aaf491c
--- /dev/null
+++ b/test/certs_manager_test.cpp
@@ -0,0 +1,168 @@
+#include "certs_manager.hpp"
+
+#include <algorithm>
+#include <experimental/filesystem>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <xyz/openbmc_project/Certs/Install/error.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace fs = std::experimental::filesystem;
+static constexpr auto BUSNAME = "xyz.openbmc_project.Certs.Manager";
+static constexpr auto OBJPATH = "/xyz/openbmc_project/certs";
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+class TestCertsManager : public ::testing::Test
+{
+ public:
+ TestCertsManager() : bus(sdbusplus::bus::new_default())
+ {
+ }
+ void SetUp() override
+ {
+ char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
+ auto dirPtr = mkdtemp(dirTemplate);
+ if (dirPtr == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ certDir = dirPtr;
+ certificateFile = "cert.pem";
+ std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
+ cmd += "-keyout cert.pem -out cert.pem -days 3650 ";
+ cmd += "-subj "
+ "/O=openbmc-project.xyz/CN=localhost"
+ " -nodes";
+ auto val = std::system(cmd.c_str());
+ if (val)
+ {
+ std::cout << "COMMAND Error: " << val << std::endl;
+ }
+ }
+ void TearDown() override
+ {
+ fs::remove_all(certDir);
+ fs::remove(certificateFile);
+ }
+
+ bool compareFiles(const std::string& file1, const std::string& file2)
+ {
+ std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate);
+ std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate);
+
+ if (f1.fail() || f2.fail())
+ {
+ return false; // file problem
+ }
+
+ if (f1.tellg() != f2.tellg())
+ {
+ return false; // size mismatch
+ }
+
+ // seek back to beginning and use std::equal to compare contents
+ f1.seekg(0, std::ifstream::beg);
+ f2.seekg(0, std::ifstream::beg);
+ return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
+ std::istreambuf_iterator<char>(),
+ std::istreambuf_iterator<char>(f2.rdbuf()));
+ }
+
+ protected:
+ sdbusplus::bus::bus bus;
+ std::string certificateFile;
+
+ std::string certDir;
+};
+
+class MainApp
+{
+ public:
+ MainApp(phosphor::certs::Manager* manager) : manager(manager)
+ {
+ }
+ void install(std::string& path)
+ {
+ manager->install(path);
+ }
+ phosphor::certs::Manager* manager;
+};
+
+class MockCertManager : public phosphor::certs::Manager
+{
+ public:
+ MockCertManager(sdbusplus::bus::bus& bus, const char* path,
+ std::string& type, std::string&& unit,
+ std::string&& certPath) :
+ Manager(bus, path, type, std::forward<std::string>(unit),
+ std::forward<std::string>(certPath))
+ {
+ }
+ virtual ~MockCertManager()
+ {
+ }
+
+ MOCK_METHOD0(clientInstall, void());
+ MOCK_METHOD0(serverInstall, void());
+};
+
+/** @brief Check if server install routine is invoked for server setup
+ */
+TEST_F(TestCertsManager, InvokeServerInstall)
+{
+ std::string endpoint("https");
+ std::string unit("nginx.service");
+ std::string type("server");
+ std::string path(certDir + "/" + certificateFile);
+ std::string verifyPath(path);
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+ MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+ std::move(path));
+ EXPECT_CALL(manager, serverInstall()).Times(1);
+
+ MainApp mainApp(&manager);
+ EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+ EXPECT_TRUE(fs::exists(verifyPath));
+}
+
+/** @brief Check if client install routine is invoked for client setup
+ */
+TEST_F(TestCertsManager, InvokeClientInstall)
+{
+ std::string endpoint("ldap");
+ std::string unit("nslcd.service");
+ std::string type("client");
+ std::string path(certDir + "/" + certificateFile);
+ std::string verifyPath(path);
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+ MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+ std::move(path));
+ EXPECT_CALL(manager, clientInstall()).Times(1);
+ MainApp mainApp(&manager);
+ EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+ EXPECT_TRUE(fs::exists(verifyPath));
+}
+
+/** @brief Compare the installed certificate with the copied certificate
+ */
+TEST_F(TestCertsManager, CompareInstalledCertificate)
+{
+ std::string endpoint("ldap");
+ std::string unit("nslcd.service");
+ std::string type("client");
+ std::string path(certDir + "/" + certificateFile);
+ std::string verifyPath(path);
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+ MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+ std::move(path));
+ EXPECT_CALL(manager, clientInstall()).Times(1);
+ MainApp mainApp(&manager);
+ EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+ EXPECT_TRUE(fs::exists(verifyPath));
+ EXPECT_TRUE(compareFiles(verifyPath, certificateFile));
+}