blob: ef1cf8bdad05380ffb19e03bbef4d87cb8d8f03c [file] [log] [blame]
#include "persistent_storage_impl.h"
#include <cstdint>
#include <cstdio>
#include <filesystem> // NOLINT(build/c++17)
#include <iterator>
#include <string>
#include <vector>
#include "safepower_agent.pb.h"
#include "state_persistence.pb.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
namespace persistent_storage {
namespace {
using safepower_agent_persistence_proto::SavedAction;
using safepower_agent_persistence_proto::SavedActions;
constexpr uint64_t kNEW_EPOCH = 987654321;
constexpr absl::string_view gkey = "key";
SavedActions GetSavedActionsWhole(std::string key = std::string(gkey)) {
SavedActions saved_actions;
SavedAction saved_action;
safepower_agent_proto::StartActionRequest* start_action_request =
saved_action.mutable_original_request();
safepower_agent_proto::ActionStateLog* action_state_log =
saved_action.mutable_action_state_log();
action_state_log->set_epoch_ms(123456789);
action_state_log->set_current_state(
safepower_agent_proto::ACTION_STATE_RUNNING_ACTION);
safepower_agent_proto::Action* action =
start_action_request->mutable_action();
action->set_action_type(safepower_agent_proto::ACTION_TYPE_ON);
start_action_request->set_caller_cookie("test_cookie");
(*saved_actions.mutable_actions())[key] = saved_action;
return saved_actions;
}
SavedActions GetSavedActionsExpectedSize() {
SavedActions saved_actions;
std::string text_format = R"(
actions {
key: "key"
value {
original_request {
caller_cookie: "cookie_monster"
precondition {
state_condition {
node_entity_tag: "node"
comparison_type: COMPARISON_TYPE_EQ
abort: false
}
}
action {
action_type: ACTION_TYPE_ON
}
validation {
state_condition {
node_entity_tag: "node"
comparison_type: COMPARISON_TYPE_EQ
abort: false
}
}
}
action_state_log {
epoch_ms: 999999999
current_state: ACTION_STATE_CHECKING_PRECONDITION
history {
changed_at {
seconds: 333333333
}
status {
code: 1
message: "a_ok"
}
}
history {
changed_at {
seconds: 444444444
}
status {
code: 2
message: "b_ok"
}
}
history {
changed_at {
seconds: 555555555
}
status {
code: 3
message: "c_ok"
}
}
history {
changed_at {
seconds: 888888888
}
status {
code: 4
message: "d_ok"
}
}
}
}
}
)";
google::protobuf::TextFormat::Parser parser;
EXPECT_TRUE(parser.ParseFromString(text_format, &saved_actions));
return saved_actions;
}
SavedActions GetSavedActionsUpdate(std::string key = std::string(gkey))
{
// append ONLY a change to example saved actions
// Only the time and the Action state will be different
// status: running -> success
// time: smaller -> larger
SavedActions saved_actions_diff;
SavedAction saved_action_diff;
safepower_agent_proto::ActionStateLog* action_state_log_diff =
saved_action_diff.mutable_action_state_log();
action_state_log_diff->set_epoch_ms(kNEW_EPOCH);
action_state_log_diff->set_current_state(
safepower_agent_proto::ACTION_STATE_SUCCESS);
(*saved_actions_diff.mutable_actions())[key] = saved_action_diff;
return saved_actions_diff;
}
void evalChangeOn(SavedActions result, std::string key = std::string(gkey)) {
auto result_saved_action = result.actions().find(key)->second;
safepower_agent_proto::ActionStateLog* result_action_state_log =
result_saved_action.mutable_action_state_log();
EXPECT_EQ(result_action_state_log->epoch_ms(), kNEW_EPOCH);
EXPECT_EQ(result_action_state_log->current_state(),
safepower_agent_proto::ACTION_STATE_SUCCESS);
// check the old cookie and action type
safepower_agent_proto::StartActionRequest* result_start_action_request =
result_saved_action.mutable_original_request();
safepower_agent_proto::Action* action =
result_start_action_request->mutable_action();
EXPECT_EQ(action->action_type(), safepower_agent_proto::ACTION_TYPE_ON);
EXPECT_EQ(result_start_action_request->caller_cookie(), "test_cookie");
}
TEST(PersistentStorageBMC, WriteStateAndRead) {
auto saved_actions = GetSavedActionsWhole();
std::string dir_name = std::tmpnam(nullptr);
std::filesystem::create_directory(dir_name);
persistent_storage::PersistentStorageManagerImpl persist;
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
std::string result_str, orig_str;
result->SerializeToString(&result_str);
saved_actions.SerializeToString(&orig_str);
EXPECT_EQ(result_str, orig_str);
}
TEST(PersistentStorageBMC, WriteStateAndModifyAndRead) {
std::string dir_name = std::tmpnam(nullptr);
std::filesystem::create_directory(dir_name);
auto saved_actions = GetSavedActionsWhole();
// save example saved actions
persistent_storage::PersistentStorageManagerImpl persist;
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
auto update = GetSavedActionsUpdate();
EXPECT_EQ(persist.WriteSavedActionsChange(update, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
// check the new time, and the new state
evalChangeOn(*result);
}
TEST(PersistentStorageBMC, WriteStateAndModifyAndReadManyTimes) {
std::string dir_name = std::tmpnam(nullptr);
std::filesystem::create_directory(dir_name);
persistent_storage::PersistentStorageManagerImpl persist;
auto special = GetSavedActionsWhole("special");
EXPECT_EQ(persist.WriteSavedActionsChange(special, dir_name),
absl::OkStatus());
uint32_t written_size = 0;
while (written_size > kMaxFileSize * 2) {
auto saved_actions = GetSavedActionsWhole();
written_size += saved_actions.ByteSizeLong();
// save example saved actions
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
}
written_size = 0;
while (written_size > kMaxFileSize * 2) {
auto update = GetSavedActionsUpdate();
written_size += update.ByteSizeLong();
EXPECT_EQ(persist.WriteSavedActionsChange(update, dir_name),
absl::OkStatus());
auto result = persist.ReadSavedActions(dir_name);
// check the new time, and the new state
evalChangeOn(*result);
// check special
auto result_saved_action = result->actions().find("special")->second;
safepower_agent_proto::ActionStateLog* result_action_state_log =
result_saved_action.mutable_action_state_log();
EXPECT_EQ((result_action_state_log->epoch_ms()), 123456789);
}
}
TEST(PersistentStorageBMC, testDropOldestProto) {
std::string dir_name = std::tmpnam(nullptr);
std::filesystem::create_directory(dir_name);
persistent_storage::PersistentStorageManagerImpl persist;
std::vector<uint32_t> counts;
uint32_t written_size = 0;
uint32_t count = 0;
while (written_size < kMaxFileSize * 2) {
SavedActions saved_actions = GetSavedActionsExpectedSize();
(*saved_actions.mutable_actions())["key"]
.mutable_action_state_log()
->set_epoch_ms(count);
auto sl =
(*saved_actions.mutable_actions())["key"].mutable_action_state_log();
auto size = sl->history().size();
auto change_at = sl->mutable_history(size - 1)->mutable_changed_at();
// the change at time, will be incrementing by one
change_at->set_seconds(count);
(*saved_actions.mutable_actions())[std::to_string(count)] =
saved_actions.actions().at("key");
saved_actions.mutable_actions()->erase("key");
// save the time in the vector, so we can check the reverse order
counts.push_back(count);
count++;
written_size += saved_actions.ByteSizeLong();
EXPECT_EQ(persist.WriteSavedActionsChange(saved_actions, dir_name),
absl::OkStatus());
}
auto result = persist.ReadSavedActions(dir_name);
// we expect protos to be dropped, because we wrote 2 X max filesize
EXPECT_LE(result->actions().size(), count);
// we expect (at least) the last kProtoKeepNumber
// and we can check the count value equals the epoch_ms set above
for (auto i = counts.rbegin();
i != std::next(counts.rbegin(), kProtoKeepNumber); i++) {
EXPECT_EQ(result->actions().contains(std::to_string(*i)), true);
auto const sl =
(result->actions()).find(std::to_string(*i))->second.action_state_log();
auto size = sl.history().size();
auto ct = sl.history(size - 1).changed_at();
EXPECT_EQ(ct.seconds(), *i);
}
}
} // namespace
} // namespace persistent_storage