userLockedForFailedAttempt: add unit test

This commit adds unit test for the |userLockedForFailedAttempt|
function. In order to do so, this commits adds a new virtual function
which reads the failed attempt. The function is overridden in unit test.

Tested: unit test passed.

Coverage:
  lines......: 83.1% (2007 of 2415 lines)
  functions..: 94.1% (430 of 457 functions)
  branches...: 32.3% (3181 of 9855 branches)

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I940ec42936c8b1c387fc0f19db13bc90a61c9852
diff --git a/test/user_mgr_test.cpp b/test/user_mgr_test.cpp
index f613f9e..474e055 100644
--- a/test/user_mgr_test.cpp
+++ b/test/user_mgr_test.cpp
@@ -290,6 +290,9 @@
     MOCK_METHOD(void, executeUserModifyUserEnable, (const char*, bool),
                 (override));
 
+    MOCK_METHOD(std::vector<std::string>, getFailedAttempt, (const char*),
+                (override));
+
   protected:
     static sdbusplus::bus_t busInTest;
     std::string tempPamConfigFile;
@@ -716,5 +719,100 @@
     EXPECT_NO_THROW(UserMgr::deleteUser(username));
 }
 
+TEST_F(
+    UserMgrInTest,
+    UserLockedForFailedAttemptReturnsFalseIfMaxLoginAttemptBeforeLockoutIsZero)
+{
+    EXPECT_FALSE(userLockedForFailedAttempt("whatever"));
+}
+
+TEST_F(UserMgrInTest, UserLockedForFailedAttemptZeroFailuresReturnsFalse)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+    // Example output from BMC
+    // root@s7106:~# pam_tally2 -u root
+    // Login           Failures Latest failure     From
+    // root                0
+    std::vector<std::string> output = {"whatever", "root\t0"};
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Return(output));
+
+    EXPECT_FALSE(userLockedForFailedAttempt(username));
+}
+
+TEST_F(UserMgrInTest, UserLockedForFailedAttemptFailIfGetFailedAttemptFail)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Throw(
+            sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure()));
+
+    EXPECT_THROW(
+        userLockedForFailedAttempt(username),
+        sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
+}
+
+TEST_F(UserMgrInTest,
+       UserLockedForFailedAttemptThrowsInternalFailureIfFailAttemptsOutOfRange)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+    std::vector<std::string> output = {"whatever", "root\t1000000"};
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Return(output));
+
+    EXPECT_THROW(
+        userLockedForFailedAttempt(username),
+        sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
+}
+
+TEST_F(UserMgrInTest,
+       UserLockedForFailedAttemptThrowsInternalFailureIfNoFailDateTime)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+    std::vector<std::string> output = {"whatever", "root\t2"};
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Return(output));
+
+    EXPECT_THROW(
+        userLockedForFailedAttempt(username),
+        sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
+}
+
+TEST_F(UserMgrInTest,
+       UserLockedForFailedAttemptThrowsInternalFailureIfWrongDateFormat)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+
+    // Choose a date in the past.
+    std::vector<std::string> output = {"whatever",
+                                       "root\t2\t10/24/2002\t00:00:00"};
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Return(output));
+
+    EXPECT_THROW(
+        userLockedForFailedAttempt(username),
+        sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
+}
+
+TEST_F(UserMgrInTest,
+       UserLockedForFailedAttemptReturnsFalseIfLastFailTimeHasTimedOut)
+{
+    std::string username = "user001";
+    initializeAccountPolicy();
+
+    // Choose a date in the past.
+    std::vector<std::string> output = {"whatever",
+                                       "root\t2\t10/24/02\t00:00:00"};
+    EXPECT_CALL(*this, getFailedAttempt(testing::StrEq(username.c_str())))
+        .WillOnce(testing::Return(output));
+
+    EXPECT_EQ(userLockedForFailedAttempt(username), false);
+}
+
 } // namespace user
 } // namespace phosphor