| #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 |