diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 4c25a808..440e0bc0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -80,10 +80,13 @@ jobs: sudo apt-get update sudo apt-get install -y lcov - - name: Extract Coverage for kvs.cpp + - name: Extract Coverage for CPP Files run: | REPORT=$(find "$(bazel info output_path)" -name _coverage_report.dat | head -n1) lcov \ --rc branch_coverage=1 \ - --extract "$REPORT" "src/cpp/src/kvs.cpp" \ + --extract "$REPORT" '*.cpp' -o "${REPORT}.cpp" \ --output-file kvs_coverage.info + + - name: Bazel Benchmark + run: bazel run -c opt //:bm_kvs_cpp diff --git a/BUILD b/BUILD index 5c928f0f..4ae08ce7 100644 --- a/BUILD +++ b/BUILD @@ -45,7 +45,7 @@ use_format_targets() alias( name = "kvs_cpp", - actual = "//src/cpp:kvs_cpp", + actual = "//src/cpp/src:kvs_cpp", visibility = ["//visibility:public"], ) diff --git a/src/cpp/BUILD b/src/cpp/src/BUILD similarity index 64% rename from src/cpp/BUILD rename to src/cpp/src/BUILD index 011415a8..8f77455c 100644 --- a/src/cpp/BUILD +++ b/src/cpp/src/BUILD @@ -13,16 +13,37 @@ # The filegroup is used to collect all source files for the tests. +cc_library( + name = "kvsvalue", + srcs = [ + "kvsvalue.cpp", + ], + hdrs = ["kvsvalue.hpp"], + includes = ["."], + visibility = [ + "//:__pkg__", + "//src/cpp/src/internal:__pkg__", + ], +) + cc_library( name = "kvs_cpp", srcs = [ - "inc/internal/kvs_helper.hpp", - "src/kvs.cpp", + "kvs.cpp", + "kvsbuilder.cpp", + ], + hdrs = [ + "kvs.hpp", + "kvsbuilder.hpp", + ], + implementation_deps = [ + "//src/cpp/src/internal:kvs_helper", ], - hdrs = ["inc/kvs.hpp"], - includes = ["inc"], + includes = ["."], visibility = ["//:__pkg__"], deps = [ + ":kvsvalue", + "//src/cpp/src/internal:error", "@score-baselibs//score/filesystem:filesystem", "@score-baselibs//score/json", "@score-baselibs//score/result:result", diff --git a/src/cpp/src/internal/BUILD b/src/cpp/src/internal/BUILD new file mode 100644 index 00000000..a7707189 --- /dev/null +++ b/src/cpp/src/internal/BUILD @@ -0,0 +1,48 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +cc_library( + name = "error", + srcs = [ + "error.cpp", + ], + hdrs = [ + "error.hpp", + ], + visibility = [ + "//src/cpp/src:__pkg__", + "//src/cpp/tests:__pkg__", + ], + deps = [ + "@score-baselibs//score/result:result", + ], +) + +cc_library( + name = "kvs_helper", + srcs = [ + "kvs_helper.cpp", + ], + hdrs = [ + "kvs_helper.hpp", + ], + visibility = [ + "//src/cpp/src:__pkg__", + "//src/cpp/tests:__pkg__", + ], + deps = [ + ":error", + "//src/cpp/src:kvsvalue", + "@score-baselibs//score/json", + ], +) diff --git a/src/cpp/src/internal/error.cpp b/src/cpp/src/internal/error.cpp new file mode 100644 index 00000000..2b7e11c6 --- /dev/null +++ b/src/cpp/src/internal/error.cpp @@ -0,0 +1,99 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "error.hpp" + +namespace score::mw::per::kvs { + +/*********************** Error Implementation *********************/ +std::string_view MyErrorDomain::MessageFor(score::result::ErrorCode const& code) const noexcept +{ + std::string_view msg; + switch (static_cast(code)) + { + case ErrorCode::UnmappedError: + msg = "Error that was not yet mapped"; + break; + case ErrorCode::FileNotFound: + msg = "File not found"; + break; + case ErrorCode::KvsFileReadError: + msg = "KVS file read error"; + break; + case ErrorCode::KvsHashFileReadError: + msg = "KVS hash file read error"; + break; + case ErrorCode::JsonParserError: + msg = "JSON parser error"; + break; + case ErrorCode::JsonGeneratorError: + msg = "JSON generator error"; + break; + case ErrorCode::PhysicalStorageFailure: + msg = "Physical storage failure"; + break; + case ErrorCode::IntegrityCorrupted: + msg = "Integrity corrupted"; + break; + case ErrorCode::ValidationFailed: + msg = "Validation failed"; + break; + case ErrorCode::EncryptionFailed: + msg = "Encryption failed"; + break; + case ErrorCode::ResourceBusy: + msg = "Resource is busy"; + break; + case ErrorCode::OutOfStorageSpace: + msg = "Out of storage space"; + break; + case ErrorCode::QuotaExceeded: + msg = "Quota exceeded"; + break; + case ErrorCode::AuthenticationFailed: + msg = "Authentication failed"; + break; + case ErrorCode::KeyNotFound: + msg = "Key not found"; + break; + case ErrorCode::KeyDefaultNotFound: + msg = "Key default value not found"; + break; + case ErrorCode::SerializationFailed: + msg = "Serialization failed"; + break; + case ErrorCode::InvalidSnapshotId: + msg = "Invalid snapshot ID"; + break; + case ErrorCode::ConversionFailed: + msg = "Conversion failed"; + break; + case ErrorCode::MutexLockFailed: + msg = "Mutex failed"; + break; + case ErrorCode::InvalidValueType: + msg = "Invalid value type"; + break; + default: + msg = "Unknown Error!"; + break; + } + + return msg; +} + +score::result::Error MakeError(ErrorCode code, std::string_view user_message) noexcept +{ + return {static_cast(code), my_error_domain, user_message}; +} + +} /* namespace score::mw::per::kvs */ diff --git a/src/cpp/src/internal/error.hpp b/src/cpp/src/internal/error.hpp new file mode 100644 index 00000000..164c4840 --- /dev/null +++ b/src/cpp/src/internal/error.hpp @@ -0,0 +1,97 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#ifndef SCORE_LIB_KVS_INTERNAL_ERROR_HPP +#define SCORE_LIB_KVS_INTERNAL_ERROR_HPP + +#include "score/result/result.h" + +namespace score::mw::per::kvs { + +/* @brief */ +enum class ErrorCode : score::result::ErrorCode { + /* Error that was not yet mapped*/ + UnmappedError, + + /* File not found*/ + FileNotFound, + + /* KVS file read error*/ + KvsFileReadError, + + /* KVS hash file read error*/ + KvsHashFileReadError, + + /* JSON parser error*/ + JsonParserError, + + /* JSON generator error*/ + JsonGeneratorError, + + /* Physical storage failure*/ + PhysicalStorageFailure, + + /* Integrity corrupted*/ + IntegrityCorrupted, + + /* Validation failed*/ + ValidationFailed, + + /* Encryption failed*/ + EncryptionFailed, + + /* Resource is busy*/ + ResourceBusy, + + /* Out of storage space*/ + OutOfStorageSpace, + + /* Quota exceeded*/ + QuotaExceeded, + + /* Authentication failed*/ + AuthenticationFailed, + + /* Key not found*/ + KeyNotFound, + + /* Key default value not found*/ + KeyDefaultNotFound, + + /* Serialization failed*/ + SerializationFailed, + + /* Invalid snapshot ID*/ + InvalidSnapshotId, + + /* Conversion failed*/ + ConversionFailed, + + /* Mutex failed*/ + MutexLockFailed, + + /* Invalid value type*/ + InvalidValueType, +}; + +class MyErrorDomain final : public score::result::ErrorDomain +{ +public: + std::string_view MessageFor(score::result::ErrorCode const& code) const noexcept override; +}; + +constexpr MyErrorDomain my_error_domain; +score::result::Error MakeError(ErrorCode code, std::string_view user_message = "") noexcept; + +} /* namespace score::mw::per::kvs */ + +#endif /* SCORE_LIB_KVS_INTERNAL_ERROR_HPP */ diff --git a/src/cpp/src/internal/kvs_helper.cpp b/src/cpp/src/internal/kvs_helper.cpp new file mode 100644 index 00000000..ae7e43a8 --- /dev/null +++ b/src/cpp/src/internal/kvs_helper.cpp @@ -0,0 +1,319 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "kvs_helper.hpp" + +namespace score::mw::per::kvs { + +/*********************** Hash Functions *********************/ +/*Adler 32 checksum algorithm*/ +// Optimized version: processes data in blocks to reduce modulo operations +uint32_t calculate_hash_adler32(const std::string& data) { + constexpr size_t ADLER32_NMAX = 5552; + constexpr uint32_t ADLER32_BASE = 65521; + uint32_t a = 1, b = 0; + size_t len = data.size(); + size_t i = 0; + + // Process in blocks of 5552 bytes (as recommended for Adler-32) + while (len > 0) { + size_t tlen = len > ADLER32_NMAX ? ADLER32_NMAX : len; + len -= tlen; + for (size_t j = 0; j < tlen; ++j, ++i) { + a += static_cast(data[i]); + b += a; + } + a %= ADLER32_BASE; + b %= ADLER32_BASE; + } + return (b << 16) | a; +} + +/*Parse Adler32 checksum Byte-Array to uint32 */ +uint32_t parse_hash_adler32(std::istream& in) +{ + std::array buf{}; + in.read(reinterpret_cast(buf.data()), buf.size()); + + uint32_t value = (uint32_t(buf[0]) << 24) + | (uint32_t(buf[1]) << 16) + | (uint32_t(buf[2]) << 8) + | uint32_t(buf[3]); + return value; +} + +/* Split uint32 checksum in bytes for writing*/ +std::array get_hash_bytes_adler32(uint32_t hash) +{ + std::array value = { + uint8_t((hash >> 24) & 0xFF), + uint8_t((hash >> 16) & 0xFF), + uint8_t((hash >> 8) & 0xFF), + uint8_t((hash ) & 0xFF) + }; + return value; +} + +/***** Wrapper Functions for Hash *****/ +/* Wrapper Functions should isolate the Hash Algorithm, so that the algorithm can be easier replaced*/ + +/* Wrapper Function to get checksum in bytes*/ +std::array get_hash_bytes(const std::string& data) +{ + uint32_t hash = calculate_hash_adler32(data); + std::array value = get_hash_bytes_adler32(hash); + return value; +} + +/* Wrapper Function to check, if Hash is valid*/ +bool check_hash(const std::string& data_calculate, std::istream& data_parse){ + bool result; + uint32_t calculated_hash = calculate_hash_adler32(data_calculate); + uint32_t parsed_hash = parse_hash_adler32(data_parse); + if(calculated_hash == parsed_hash){ + result = true; + }else{ + result = false; + } + + return result; +} + +/*********************** Standalone Helper Functions *********************/ + +/* Helper Function for Any -> KVSValue conversion */ +score::Result any_to_kvsvalue(const score::json::Any& any){ + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); + if (auto o = any.As(); o.has_value()) { + const auto& objAny = o.value().get(); + auto type = objAny.find("t"); + auto value = objAny.find("v"); + if (type != objAny.end() && value != objAny.end()) { + if (auto typeStr = type->second.As(); typeStr.has_value()) { + const std::string_view typeStrV = typeStr.value().get(); + const score::json::Any& valueAny = value->second; + + if (typeStrV == "i32") { + if (auto n = valueAny.As(); n.has_value()){ + result = KvsValue(static_cast(n.value())); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "u32") { + if (auto n = valueAny.As(); n.has_value()) { + result = KvsValue(static_cast(n.value())); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "i64") { + if (auto n = valueAny.As(); n.has_value()) { + result = KvsValue(static_cast(n.value())); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "u64") { + if (auto n = valueAny.As(); n.has_value()) { + result = KvsValue(static_cast(n.value())); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "f64") { + if (auto n = valueAny.As(); n.has_value()) { + result = KvsValue(n.value()); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "bool") { + if (auto b = valueAny.As(); b.has_value()) { + result = KvsValue(b.value()); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "str") { + if (auto s = valueAny.As(); s.has_value()) { + result = KvsValue(s.value().get()); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "null") { + if (valueAny.As().has_value()) { + result = KvsValue(nullptr); + } + else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "arr") { + if (auto l = valueAny.As(); l.has_value()) { + KvsValue::Array arr; + bool error = false; + for (auto const& elem : l.value().get()) { + auto conv = any_to_kvsvalue(elem); + if (!conv) { + error = true; + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + break; + } + arr.emplace_back(std::move(conv.value())); + } + if (!error){ + result = KvsValue(std::move(arr)); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } + else if (typeStrV == "obj") { + if (auto obj = valueAny.As(); obj.has_value()) { + KvsValue::Object map; + bool error = false; + for (auto const& [key, valAny] : obj.value().get()) { + auto conv = any_to_kvsvalue(valAny); + if (!conv) { + error = true; + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + break; + } + map.emplace(key.GetAsStringView().to_string(), std::move(conv.value())); + } + if (!error) { + result = KvsValue(std::move(map)); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + } else { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + } + + return result; +} + +/* Helper Function for KVSValue -> Any conversion */ +score::Result kvsvalue_to_any(const KvsValue& kv) { + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); + bool error = false; + score::json::Object obj; + switch (kv.getType()) { + case KvsValue::Type::i32: { + obj.emplace("t", score::json::Any(std::string("i32"))); + obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); + break; + } + case KvsValue::Type::u32: { + obj.emplace("t", score::json::Any(std::string("u32"))); + obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); + break; + } + case KvsValue::Type::i64: { + obj.emplace("t", score::json::Any(std::string("i64"))); + obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); + break; + } + case KvsValue::Type::u64: { + obj.emplace("t", score::json::Any(std::string("u64"))); + obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); + break; + } + case KvsValue::Type::f64: { + obj.emplace("t", score::json::Any(std::string("f64"))); + obj.emplace("v", score::json::Any(std::get(kv.getValue()))); + break; + } + case KvsValue::Type::Boolean: { + obj.emplace("t", score::json::Any(std::string("bool"))); + obj.emplace("v", score::json::Any(std::get(kv.getValue()))); + break; + } + case KvsValue::Type::String: { + obj.emplace("t", score::json::Any(std::string("str"))); + obj.emplace("v", score::json::Any(std::get(kv.getValue()))); + break; + } + case KvsValue::Type::Null: { + obj.emplace("t", score::json::Any(std::string("null"))); + obj.emplace("v", score::json::Any(score::json::Null{})); + break; + } + case KvsValue::Type::Array: { + obj.emplace("t", score::json::Any(std::string("arr"))); + score::json::List list; + for (auto& elem : std::get(kv.getValue())) { + auto conv = kvsvalue_to_any(elem); + if (!conv) { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + error = true; + break; + } + list.push_back(std::move(conv.value())); + } + if (!error) { + obj.emplace("v", score::json::Any(std::move(list))); + } + break; + } + case KvsValue::Type::Object: { + obj.emplace("t", score::json::Any(std::string("obj"))); + score::json::Object inner_obj; + for (auto& [key, value] : std::get(kv.getValue())) { + auto conv = kvsvalue_to_any(value); + if (!conv) { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + error = true; + break; + } + inner_obj.emplace(key, std::move(conv.value())); + } + if (!error) { + obj.emplace("v", score::json::Any(std::move(inner_obj))); + } + break; + } + default: { + result = score::MakeUnexpected(ErrorCode::InvalidValueType); + error = true; + break; + } + } + + if (!error) { + result = score::json::Any(std::move(obj)); + } + + return result; +} + + +} /* namespace score::mw::per::kvs */ diff --git a/src/cpp/inc/internal/kvs_helper.hpp b/src/cpp/src/internal/kvs_helper.hpp similarity index 83% rename from src/cpp/inc/internal/kvs_helper.hpp rename to src/cpp/src/internal/kvs_helper.hpp index 1e542c32..f5afa54d 100644 --- a/src/cpp/inc/internal/kvs_helper.hpp +++ b/src/cpp/src/internal/kvs_helper.hpp @@ -15,17 +15,24 @@ #include #include +#include "error.hpp" +#include "kvsvalue.hpp" +#include "score/json/json_parser.h" /* For JSON Any Type */ /* * This header defines helper functions used internally by the Key-Value Store (KVS) implementation. * It exists to allow unit tests to access these internal functions. */ +namespace score::mw::per::kvs { uint32_t parse_hash_adler32(std::istream& in); uint32_t calculate_hash_adler32(const std::string& data); std::array get_hash_bytes_adler32(uint32_t hash); std::array get_hash_bytes(const std::string& data); +bool check_hash(const std::string& data_calculate, std::istream& data_parse); score::Result any_to_kvsvalue(const score::json::Any& any); score::Result kvsvalue_to_any(const KvsValue& kv); +} /* namespace score::mw::per::kvs */ + #endif // SCORE_LIB_KVS_INTERNAL_KVS_HELPER_HPP diff --git a/src/cpp/src/kvs.cpp b/src/cpp/src/kvs.cpp index 2a885dd0..fef4e64d 100644 --- a/src/cpp/src/kvs.cpp +++ b/src/cpp/src/kvs.cpp @@ -10,13 +10,11 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #include #include #include -#include "kvs.hpp" #include "internal/kvs_helper.hpp" - +#include "kvs.hpp" //TODO Default Value Handling TBD //TODO Add Score Logging @@ -25,433 +23,7 @@ using namespace std; -/*********************** Hash Functions *********************/ -/*Adler 32 checksum algorithm*/ -// Optimized version: processes data in blocks to reduce modulo operations -uint32_t calculate_hash_adler32(const string& data) { - constexpr size_t ADLER32_NMAX = 5552; - constexpr uint32_t ADLER32_BASE = 65521; - uint32_t a = 1, b = 0; - size_t len = data.size(); - size_t i = 0; - - // Process in blocks of 5552 bytes (as recommended for Adler-32) - while (len > 0) { - size_t tlen = len > ADLER32_NMAX ? ADLER32_NMAX : len; - len -= tlen; - for (size_t j = 0; j < tlen; ++j, ++i) { - a += static_cast(data[i]); - b += a; - } - a %= ADLER32_BASE; - b %= ADLER32_BASE; - } - return (b << 16) | a; -} - -/*Parse Adler32 checksum Byte-Array to uint32 */ -uint32_t parse_hash_adler32(std::istream& in) -{ - std::array buf{}; - in.read(reinterpret_cast(buf.data()), buf.size()); - - uint32_t value = (uint32_t(buf[0]) << 24) - | (uint32_t(buf[1]) << 16) - | (uint32_t(buf[2]) << 8) - | uint32_t(buf[3]); - return value; -} - -/* Split uint32 checksum in bytes for writing*/ -std::array get_hash_bytes_adler32(uint32_t hash) -{ - std::array value = { - uint8_t((hash >> 24) & 0xFF), - uint8_t((hash >> 16) & 0xFF), - uint8_t((hash >> 8) & 0xFF), - uint8_t((hash ) & 0xFF) - }; - return value; -} - -/***** Wrapper Functions for Hash *****/ -/* Wrapper Functions should isolate the Hash Algorithm, so that the algorithm can be easier replaced*/ - -/* Wrapper Function to get checksum in bytes*/ -std::array get_hash_bytes(const string& data) -{ - uint32_t hash = calculate_hash_adler32(data); - std::array value = get_hash_bytes_adler32(hash); - return value; -} - -/* Wrapper Function to check, if Hash is valid*/ -bool check_hash(const string& data_calculate, std::istream& data_parse){ - bool result; - uint32_t calculated_hash = calculate_hash_adler32(data_calculate); - uint32_t parsed_hash = parse_hash_adler32(data_parse); - if(calculated_hash == parsed_hash){ - result = true; - }else{ - result = false; - } - - return result; -} - -/*********************** Standalone Helper Functions *********************/ - -/* Helper Function for Any -> KVSValue conversion */ -score::Result any_to_kvsvalue(const score::json::Any& any){ - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); - if (auto o = any.As(); o.has_value()) { - const auto& objAny = o.value().get(); - auto type = objAny.find("t"); - auto value = objAny.find("v"); - if (type != objAny.end() && value != objAny.end()) { - if (auto typeStr = type->second.As(); typeStr.has_value()) { - const std::string_view typeStrV = typeStr.value().get(); - const score::json::Any& valueAny = value->second; - - if (typeStrV == "i32") { - if (auto n = valueAny.As(); n.has_value()){ - result = KvsValue(static_cast(n.value())); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "u32") { - if (auto n = valueAny.As(); n.has_value()) { - result = KvsValue(static_cast(n.value())); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "i64") { - if (auto n = valueAny.As(); n.has_value()) { - result = KvsValue(static_cast(n.value())); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "u64") { - if (auto n = valueAny.As(); n.has_value()) { - result = KvsValue(static_cast(n.value())); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "f64") { - if (auto n = valueAny.As(); n.has_value()) { - result = KvsValue(n.value()); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "bool") { - if (auto b = valueAny.As(); b.has_value()) { - result = KvsValue(b.value()); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "str") { - if (auto s = valueAny.As(); s.has_value()) { - result = KvsValue(s.value().get()); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "null") { - if (valueAny.As().has_value()) { - result = KvsValue(nullptr); - } - else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "arr") { - if (auto l = valueAny.As(); l.has_value()) { - KvsValue::Array arr; - bool error = false; - for (auto const& elem : l.value().get()) { - auto conv = any_to_kvsvalue(elem); - if (!conv) { - error = true; - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - break; - } - arr.emplace_back(std::move(conv.value())); - } - if (!error){ - result = KvsValue(std::move(arr)); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } - else if (typeStrV == "obj") { - if (auto obj = valueAny.As(); obj.has_value()) { - KvsValue::Object map; - bool error = false; - for (auto const& [key, valAny] : obj.value().get()) { - auto conv = any_to_kvsvalue(valAny); - if (!conv) { - error = true; - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - break; - } - map.emplace(key.GetAsStringView().to_string(), std::move(conv.value())); - } - if (!error) { - result = KvsValue(std::move(map)); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - } else { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - } - - return result; -} - -/* Helper Function for KVSValue -> Any conversion */ -score::Result kvsvalue_to_any(const KvsValue& kv) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); - bool error = false; - score::json::Object obj; - switch (kv.getType()) { - case KvsValue::Type::i32: { - obj.emplace("t", score::json::Any(std::string("i32"))); - obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); - break; - } - case KvsValue::Type::u32: { - obj.emplace("t", score::json::Any(std::string("u32"))); - obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); - break; - } - case KvsValue::Type::i64: { - obj.emplace("t", score::json::Any(std::string("i64"))); - obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); - break; - } - case KvsValue::Type::u64: { - obj.emplace("t", score::json::Any(std::string("u64"))); - obj.emplace("v", score::json::Any(static_cast(std::get(kv.getValue())))); - break; - } - case KvsValue::Type::f64: { - obj.emplace("t", score::json::Any(std::string("f64"))); - obj.emplace("v", score::json::Any(std::get(kv.getValue()))); - break; - } - case KvsValue::Type::Boolean: { - obj.emplace("t", score::json::Any(std::string("bool"))); - obj.emplace("v", score::json::Any(std::get(kv.getValue()))); - break; - } - case KvsValue::Type::String: { - obj.emplace("t", score::json::Any(std::string("str"))); - obj.emplace("v", score::json::Any(std::get(kv.getValue()))); - break; - } - case KvsValue::Type::Null: { - obj.emplace("t", score::json::Any(std::string("null"))); - obj.emplace("v", score::json::Any(score::json::Null{})); - break; - } - case KvsValue::Type::Array: { - obj.emplace("t", score::json::Any(std::string("arr"))); - score::json::List list; - for (auto& elem : std::get(kv.getValue())) { - auto conv = kvsvalue_to_any(elem); - if (!conv) { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - error = true; - break; - } - list.push_back(std::move(conv.value())); - } - if (!error) { - obj.emplace("v", score::json::Any(std::move(list))); - } - break; - } - case KvsValue::Type::Object: { - obj.emplace("t", score::json::Any(std::string("obj"))); - score::json::Object inner_obj; - for (auto& [key, value] : std::get(kv.getValue())) { - auto conv = kvsvalue_to_any(value); - if (!conv) { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - error = true; - break; - } - inner_obj.emplace(key, std::move(conv.value())); - } - if (!error) { - obj.emplace("v", score::json::Any(std::move(inner_obj))); - } - break; - } - default: { - result = score::MakeUnexpected(MyErrorCode::InvalidValueType); - error = true; - break; - } - } - - if (!error) { - result = score::json::Any(std::move(obj)); - } - - return result; -} - - -/*********************** Error Implementation *********************/ -std::string_view MyErrorDomain::MessageFor(score::result::ErrorCode const& code) const noexcept -{ - std::string_view msg; - switch (static_cast(code)) - { - case MyErrorCode::UnmappedError: - msg = "Error that was not yet mapped"; - break; - case MyErrorCode::FileNotFound: - msg = "File not found"; - break; - case MyErrorCode::KvsFileReadError: - msg = "KVS file read error"; - break; - case MyErrorCode::KvsHashFileReadError: - msg = "KVS hash file read error"; - break; - case MyErrorCode::JsonParserError: - msg = "JSON parser error"; - break; - case MyErrorCode::JsonGeneratorError: - msg = "JSON generator error"; - break; - case MyErrorCode::PhysicalStorageFailure: - msg = "Physical storage failure"; - break; - case MyErrorCode::IntegrityCorrupted: - msg = "Integrity corrupted"; - break; - case MyErrorCode::ValidationFailed: - msg = "Validation failed"; - break; - case MyErrorCode::EncryptionFailed: - msg = "Encryption failed"; - break; - case MyErrorCode::ResourceBusy: - msg = "Resource is busy"; - break; - case MyErrorCode::OutOfStorageSpace: - msg = "Out of storage space"; - break; - case MyErrorCode::QuotaExceeded: - msg = "Quota exceeded"; - break; - case MyErrorCode::AuthenticationFailed: - msg = "Authentication failed"; - break; - case MyErrorCode::KeyNotFound: - msg = "Key not found"; - break; - case MyErrorCode::KeyDefaultNotFound: - msg = "Key default value not found"; - break; - case MyErrorCode::SerializationFailed: - msg = "Serialization failed"; - break; - case MyErrorCode::InvalidSnapshotId: - msg = "Invalid snapshot ID"; - break; - case MyErrorCode::ConversionFailed: - msg = "Conversion failed"; - break; - case MyErrorCode::MutexLockFailed: - msg = "Mutex failed"; - break; - case MyErrorCode::InvalidValueType: - msg = "Invalid value type"; - break; - default: - msg = "Unknown Error!"; - break; - } - - return msg; -} - -score::result::Error MakeError(MyErrorCode code, std::string_view user_message) noexcept -{ - return {static_cast(code), my_error_domain, user_message}; -} - - -/*********************** KVS Builder Implementation *********************/ -KvsBuilder::KvsBuilder(const InstanceId& instance_id) - : instance_id(instance_id) - , need_defaults(false) - , need_kvs(false) - , directory("./data_folder/") /* Default Directory */ -{} - -KvsBuilder& KvsBuilder::need_defaults_flag(bool flag) { - need_defaults = flag; - return *this; -} - -KvsBuilder& KvsBuilder::need_kvs_flag(bool flag) { - need_kvs = flag; - return *this; -} - -KvsBuilder& KvsBuilder::dir(std::string&& dir_path) { - this->directory = std::move(dir_path); - return *this; -} - - -score::Result KvsBuilder::build() { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); - - /* Use current directory if empty */ - if ("" == directory) { - directory = "./"; - } - - result = Kvs::open( - instance_id, - need_defaults ? OpenNeedDefaults::Required : OpenNeedDefaults::Optional, - need_kvs ? OpenNeedKvs::Required : OpenNeedKvs::Optional, - std::move(directory) - ); - - return result; -} - +namespace score::mw::per::kvs { /*********************** KVS Implementation *********************/ Kvs::~Kvs(){ @@ -520,11 +92,11 @@ Kvs& Kvs::operator=(Kvs&& other) noexcept /* Helper Function to parse JSON data for open_json*/ score::Result> Kvs::parse_json_data(const std::string& data) { - score::Result> result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result> result = score::MakeUnexpected(ErrorCode::UnmappedError); auto any_res = parser->FromBuffer(data); if (!any_res) { - result = score::MakeUnexpected(MyErrorCode::JsonParserError); + result = score::MakeUnexpected(ErrorCode::JsonParserError); }else{ score::json::Any root = std::move(any_res).value(); std::unordered_map result_value; @@ -537,7 +109,7 @@ score::Result> Kvs::parse_json_data(co auto conv = any_to_kvsvalue(element.second); if (!conv) { - result = score::MakeUnexpected(static_cast(*conv.error())); + result = score::MakeUnexpected(static_cast(*conv.error())); error = true; break; }else{ @@ -548,7 +120,7 @@ score::Result> Kvs::parse_json_data(co result = std::move(result_value); } } else { - result = score::MakeUnexpected(MyErrorCode::JsonParserError); + result = score::MakeUnexpected(ErrorCode::JsonParserError); } } @@ -560,10 +132,10 @@ score::Result> Kvs::open_json(const score:: { score::filesystem::Path json_file = prefix.Native() + ".json"; score::filesystem::Path hash_file = prefix.Native() + ".hash"; - string data; + std::string data; bool error = false; /* Error flag */ bool new_kvs = false; /* Flag to check if new KVS file is created*/ - score::Result> result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result> result = score::MakeUnexpected(ErrorCode::UnmappedError); /* Read JSON file */ ifstream in(json_file.CStr()); @@ -571,7 +143,7 @@ score::Result> Kvs::open_json(const score:: if (need_file == OpenJsonNeedFile::Required) { cerr << "error: file " << json_file << " could not be read" << endl; error = true; - result = score::MakeUnexpected(MyErrorCode::KvsFileReadError); + result = score::MakeUnexpected(ErrorCode::KvsFileReadError); }else{ cout << "file " << json_file << " not found, using empty data" << endl; new_kvs = true; @@ -589,14 +161,14 @@ score::Result> Kvs::open_json(const score:: if (!hin) { cerr << "error: hash file " << hash_file << " could not be read" << endl; error = true; - result = score::MakeUnexpected(MyErrorCode::KvsHashFileReadError); + result = score::MakeUnexpected(ErrorCode::KvsHashFileReadError); }else{ bool valid_hash = check_hash(data, hin); if(!valid_hash){ cerr << "error: KVS data corrupted (" << json_file << ", " << hash_file << ")" << endl; error = true; - result = score::MakeUnexpected(MyErrorCode::ValidationFailed); + result = score::MakeUnexpected(ErrorCode::ValidationFailed); }else{ cout << "JSON data has valid hash" << endl; } @@ -609,7 +181,7 @@ score::Result> Kvs::open_json(const score:: if (!parse_res) { cerr << "error: parsing JSON data failed" << endl; error = true; - result = score::MakeUnexpected(static_cast(*parse_res.error())); + result = score::MakeUnexpected(static_cast(*parse_res.error())); }else{ result = std::move(parse_res.value()); } @@ -621,7 +193,7 @@ score::Result> Kvs::open_json(const score:: /* Open KVS Instance */ score::Result Kvs::open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); /* Redundant initialization needed, since Resul would call the implicitly-deleted default constructor of KVS */ + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); /* Redundant initialization needed, since Resul would call the implicitly-deleted default constructor of KVS */ score::filesystem::Path base_path(dir); score::filesystem::Path filename_prefix = base_path / ("kvs_" + std::to_string(instance_id.id)); @@ -633,14 +205,14 @@ score::Result Kvs::open(const InstanceId& instance_id, OpenNeedDefaults nee filename_default, need_defaults == OpenNeedDefaults::Required ? OpenJsonNeedFile::Required : OpenJsonNeedFile::Optional); if (!default_res){ - result = score::MakeUnexpected(static_cast(*default_res.error())); /* Dereferences the Error class to its underlying code -> error.h*/ + result = score::MakeUnexpected(static_cast(*default_res.error())); /* Dereferences the Error class to its underlying code -> error.h*/ } else{ auto kvs_res = kvs.open_json( filename_kvs, need_kvs == OpenNeedKvs::Required ? OpenJsonNeedFile::Required : OpenJsonNeedFile::Optional); if (!kvs_res){ - result = score::MakeUnexpected(static_cast(*kvs_res.error())); + result = score::MakeUnexpected(static_cast(*kvs_res.error())); }else{ cout << "opened KVS: instance '" << instance_id.id << "'" << endl; cout << "max snapshot count: " << KVS_MAX_SNAPSHOTS << endl; @@ -664,13 +236,13 @@ void Kvs::set_flush_on_exit(bool flush) { /* Reset KVS to initial state*/ score::ResultBlank Kvs::reset() { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { kvs.clear(); result = score::ResultBlank{}; }else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -678,7 +250,7 @@ score::ResultBlank Kvs::reset() { /* Retrieve all keys in the KVS*/ score::Result> Kvs::get_all_keys() { - score::Result> result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result> result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { std::vector keys; @@ -688,15 +260,15 @@ score::Result> Kvs::get_all_keys() { } result = std::move(keys); }else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; } /* Check if a key exists*/ -score::Result Kvs::key_exists(const string_view key) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); +score::Result Kvs::key_exists(const std::string_view key) { + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { auto search = kvs.find(std::string(key)); /* unordered_map find() needs string and doesnt work with string_view, workaround for c++20: heterogeneous lookup (applies to more functions) */ @@ -706,7 +278,7 @@ score::Result Kvs::key_exists(const string_view key) { result = false; } }else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -714,7 +286,7 @@ score::Result Kvs::key_exists(const string_view key) { /* Retrieve the value associated with a key*/ score::Result Kvs::get_value(const std::string_view key) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock_kvs(kvs_mutex, std::try_to_lock); if (lock_kvs.owns_lock()){ auto search_kvs = kvs.find(std::string(key)); @@ -725,12 +297,12 @@ score::Result Kvs::get_value(const std::string_view key) { if (search_default != default_values.end()) { result = search_default->second; } else { - result = score::MakeUnexpected(MyErrorCode::KeyNotFound); + result = score::MakeUnexpected(ErrorCode::KeyNotFound); } } } else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -738,13 +310,13 @@ score::Result Kvs::get_value(const std::string_view key) { /*Retrieve the default value associated with a key*/ score::Result Kvs::get_default_value(const std::string_view key) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); auto search = default_values.find(std::string(key)); if (search != default_values.end()) { result = search->second; } else { - result = score::MakeUnexpected(MyErrorCode::KeyNotFound); + result = score::MakeUnexpected(ErrorCode::KeyNotFound); } return result; @@ -753,15 +325,15 @@ score::Result Kvs::get_default_value(const std::string_view key) { /* Resets a Key to its default value (Deletes written key if default is available) */ score::ResultBlank Kvs::reset_key(const std::string_view key) { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock_kvs(kvs_mutex, std::try_to_lock); if (!lock_kvs.owns_lock()) { - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } else { auto search_default = default_values.find(std::string(key)); if (search_default == default_values.end()) { - result = score::MakeUnexpected(MyErrorCode::KeyDefaultNotFound); + result = score::MakeUnexpected(ErrorCode::KeyDefaultNotFound); } else { auto search_kvs = kvs.find(std::string(key)); @@ -779,7 +351,7 @@ score::ResultBlank Kvs::reset_key(const std::string_view key) /* Check if a key has a default value*/ score::Result Kvs::has_default_value(const std::string_view key) { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); auto search = default_values.find(std::string(key)); /* unordered_map find() needs string and doesnt work with string_view, workaround for c++20: heterogeneous lookup (applies to more functions) */ if (search != default_values.end()) { @@ -793,31 +365,31 @@ score::Result Kvs::has_default_value(const std::string_view key) { /* Set the value for a key*/ score::ResultBlank Kvs::set_value(const std::string_view key, const KvsValue& value) { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { kvs.insert_or_assign(std::string(key), value); result = score::ResultBlank{}; }else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; } /* Remove a key-value pair*/ -score::ResultBlank Kvs::remove_key(const string_view key) { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); +score::ResultBlank Kvs::remove_key(const std::string_view key) { + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { const auto erased = kvs.erase(std::string(key)); if (erased > 0U) { result = score::ResultBlank{}; } else { - result = score::MakeUnexpected(MyErrorCode::KeyNotFound); + result = score::MakeUnexpected(ErrorCode::KeyNotFound); } }else{ - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -826,24 +398,24 @@ score::ResultBlank Kvs::remove_key(const string_view key) { /* Helper Function to write JSON data to a file for flush process (also adds Hash file)*/ score::ResultBlank Kvs::write_json_data(const std::string& buf) { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); score::filesystem::Path json_path{filename_prefix.Native() + "_0.json"}; score::filesystem::Path dir = json_path.ParentPath(); if (!dir.Empty()) { const auto create_path_res = filesystem->standard->CreateDirectories(dir); if(!create_path_res.has_value()) { - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } else { std::ofstream out(json_path.CStr(), std::ios::binary); if (!out.write(buf.data(), buf.size())) { - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } else { /* Write Hash File */ std::array hash_bytes = get_hash_bytes(buf); score::filesystem::Path fn_hash = filename_prefix.Native() + "_0.hash"; std::ofstream hout(fn_hash.CStr(), std::ios::binary); if (!hout.write(reinterpret_cast(hash_bytes.data()), hash_bytes.size())) { - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } else { result = score::ResultBlank{}; } @@ -851,7 +423,7 @@ score::ResultBlank Kvs::write_json_data(const std::string& buf) } } else { std::cerr << "Failed to create directory for KVS file '" << json_path.CStr() << "'\n"; - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } return result; @@ -859,7 +431,7 @@ score::ResultBlank Kvs::write_json_data(const std::string& buf) /* Flush the key-value store*/ score::ResultBlank Kvs::flush() { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); /* Create JSON Object */ score::json::Object root_obj; bool error = false; @@ -869,7 +441,7 @@ score::ResultBlank Kvs::flush() { for (auto const& [key, value] : kvs) { auto conv = kvsvalue_to_any(value); if (!conv) { - result = score::MakeUnexpected(static_cast(*conv.error())); + result = score::MakeUnexpected(static_cast(*conv.error())); error = true; break; }else{ @@ -880,7 +452,7 @@ score::ResultBlank Kvs::flush() { } } } else { - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); error = true; } } @@ -889,7 +461,7 @@ score::ResultBlank Kvs::flush() { /* Serialize Buffer */ auto buf_res = writer->ToBuffer(root_obj); if (!buf_res) { - result = score::MakeUnexpected(MyErrorCode::JsonGeneratorError); + result = score::MakeUnexpected(ErrorCode::JsonGeneratorError); }else{ /* Rotate Snapshots */ auto rotate_result = snapshot_rotate(); @@ -908,7 +480,7 @@ score::ResultBlank Kvs::flush() { /* Retrieve the snapshot count*/ score::Result Kvs::snapshot_count() const { - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); size_t count = 0; bool error = false; for (size_t idx = 1; idx <= KVS_MAX_SNAPSHOTS; ++idx) { @@ -925,7 +497,7 @@ score::Result Kvs::snapshot_count() const { count = idx; } if (error) { - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } else { result = count; } @@ -940,7 +512,7 @@ size_t Kvs::snapshot_max_count() const { /* Rotate Snapshots */ score::ResultBlank Kvs::snapshot_rotate() { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { bool error = false; @@ -957,7 +529,7 @@ score::ResultBlank Kvs::snapshot_rotate() { if (errno != ENOENT) { error = true; cout << "error: could not rename hash file " << snap_old << ". Rename Errorcode " << errno << endl; - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } } if(!error){ @@ -967,7 +539,7 @@ score::ResultBlank Kvs::snapshot_rotate() { if (errno != ENOENT) { error = true; cout << "error: could not rename snapshot file " << snap_old << ". Rename Errorcode " << errno << endl; - result = score::MakeUnexpected(MyErrorCode::PhysicalStorageFailure); + result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure); } } } @@ -979,7 +551,7 @@ score::ResultBlank Kvs::snapshot_rotate() { result = score::ResultBlank{}; } } else { - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -987,25 +559,25 @@ score::ResultBlank Kvs::snapshot_rotate() { /* Restore the key-value store from a snapshot*/ score::ResultBlank Kvs::snapshot_restore(const SnapshotId& snapshot_id) { - score::ResultBlank result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError); std::unique_lock lock(kvs_mutex, std::try_to_lock); if (lock.owns_lock()) { auto snapshot_count_res = snapshot_count(); if (!snapshot_count_res) { - result = score::MakeUnexpected(static_cast(*snapshot_count_res.error())); + result = score::MakeUnexpected(static_cast(*snapshot_count_res.error())); }else{ /* Fail if the snapshot ID is the current KVS */ if (0 == snapshot_id.id) { - result = score::MakeUnexpected(MyErrorCode::InvalidSnapshotId); + result = score::MakeUnexpected(ErrorCode::InvalidSnapshotId); }else if (snapshot_count_res.value() < snapshot_id.id) { - result = score::MakeUnexpected(MyErrorCode::InvalidSnapshotId); + result = score::MakeUnexpected(ErrorCode::InvalidSnapshotId); }else{ score::filesystem::Path restore_path = filename_prefix.Native() + "_" + to_string(snapshot_id.id); auto data_res = open_json( restore_path, OpenJsonNeedFile::Required); if (!data_res) { - result = score::MakeUnexpected(static_cast(*data_res.error())); + result = score::MakeUnexpected(static_cast(*data_res.error())); }else{ kvs = std::move(data_res.value()); result = score::ResultBlank{}; @@ -1013,7 +585,7 @@ score::ResultBlank Kvs::snapshot_restore(const SnapshotId& snapshot_id) { } } } else { - result = score::MakeUnexpected(MyErrorCode::MutexLockFailed); + result = score::MakeUnexpected(ErrorCode::MutexLockFailed); } return result; @@ -1022,17 +594,17 @@ score::ResultBlank Kvs::snapshot_restore(const SnapshotId& snapshot_id) { /* Get the filename for a snapshot*/ score::Result Kvs::get_kvs_filename(const SnapshotId& snapshot_id) const { score::filesystem::Path filename = filename_prefix.Native() + "_" + std::to_string(snapshot_id.id) + ".json"; - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); const auto fname_exists_res = filesystem->standard->Exists(filename); if (fname_exists_res) { if (false == fname_exists_res.value()) { - result = score::MakeUnexpected(MyErrorCode::FileNotFound); + result = score::MakeUnexpected(ErrorCode::FileNotFound); } else { result = filename; } } else { - result = score::MakeUnexpected(static_cast(*fname_exists_res.error())); + result = score::MakeUnexpected(static_cast(*fname_exists_res.error())); } return result; } @@ -1040,17 +612,19 @@ score::Result Kvs::get_kvs_filename(const SnapshotId& s /* Get the hash filename for a snapshot*/ score::Result Kvs::get_hash_filename(const SnapshotId& snapshot_id) const { score::filesystem::Path filename = filename_prefix.Native() + "_" + std::to_string(snapshot_id.id) + ".hash"; - score::Result result = score::MakeUnexpected(MyErrorCode::UnmappedError); + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); const auto fname_exists_res = filesystem->standard->Exists(filename); if (fname_exists_res) { if (false == fname_exists_res.value()) { - result = score::MakeUnexpected(MyErrorCode::FileNotFound); + result = score::MakeUnexpected(ErrorCode::FileNotFound); } else { result = filename; } } else { - result = score::MakeUnexpected(static_cast(*fname_exists_res.error())); + result = score::MakeUnexpected(static_cast(*fname_exists_res.error())); } return result; } + +} /* namespace score::mw::per::kvs */ diff --git a/src/cpp/inc/kvs.hpp b/src/cpp/src/kvs.hpp similarity index 68% rename from src/cpp/inc/kvs.hpp rename to src/cpp/src/kvs.hpp index f51dd104..02210a29 100644 --- a/src/cpp/inc/kvs.hpp +++ b/src/cpp/src/kvs.hpp @@ -20,6 +20,8 @@ #include #include #include +#include "internal/error.hpp" +#include "kvsvalue.hpp" #include "score/filesystem/filesystem.h" #include "score/json/json_parser.h" #include "score/json/json_writer.h" @@ -27,81 +29,7 @@ #define KVS_MAX_SNAPSHOTS 3 -/* @brief */ -enum class MyErrorCode : score::result::ErrorCode { - /* Error that was not yet mapped*/ - UnmappedError, - - /* File not found*/ - FileNotFound, - - /* KVS file read error*/ - KvsFileReadError, - - /* KVS hash file read error*/ - KvsHashFileReadError, - - /* JSON parser error*/ - JsonParserError, - - /* JSON generator error*/ - JsonGeneratorError, - - /* Physical storage failure*/ - PhysicalStorageFailure, - - /* Integrity corrupted*/ - IntegrityCorrupted, - - /* Validation failed*/ - ValidationFailed, - - /* Encryption failed*/ - EncryptionFailed, - - /* Resource is busy*/ - ResourceBusy, - - /* Out of storage space*/ - OutOfStorageSpace, - - /* Quota exceeded*/ - QuotaExceeded, - - /* Authentication failed*/ - AuthenticationFailed, - - /* Key not found*/ - KeyNotFound, - - /* Key default value not found*/ - KeyDefaultNotFound, - - /* Serialization failed*/ - SerializationFailed, - - /* Invalid snapshot ID*/ - InvalidSnapshotId, - - /* Conversion failed*/ - ConversionFailed, - - /* Mutex failed*/ - MutexLockFailed, - - /* Invalid value type*/ - InvalidValueType, -}; - -class MyErrorDomain final : public score::result::ErrorDomain -{ -public: - std::string_view MessageFor(score::result::ErrorCode const& code) const noexcept override; -}; - -constexpr MyErrorDomain my_error_domain; -score::result::Error MakeError(MyErrorCode code, std::string_view user_message = "") noexcept; - +namespace score::mw::per::kvs { struct InstanceId { size_t id; @@ -137,90 +65,6 @@ enum class OpenJsonNeedFile { Required = 1 /* Required: The file must already exist */ }; -/* Define the KvsValue class*/ -/** - * @class KvsValue - * @brief Represents a flexible value type that can hold various data types, - * including numbers, booleans, strings, null, arrays, and objects. - * - * The KvsValue class provides a type-safe way to store and retrieve values of - * different types. It uses a std::variant to hold the underlying value and an - * enum to track the type of the value. - * - * ## Supported Types: - * - Number (double) - * - Boolean (bool) - * - String (std::string) - * - Null (std::nullptr_t) - * - Array (std::vector) - * - Object (std::unordered_map) - * - * ## Public Methods: - * - `KvsValue(double number)`: Constructs a KvsValue holding a number. - * - `KvsValue(bool boolean)`: - * - Access the underlying value using `getValue()` and `std::get`. - * - * ## Example: - * @code - * KvsValue numberValue(42.0); - * KvsValue stringValue("Hello, World!"); - * KvsValue arrayValue(KvsValue::Array{numberValue, stringValue}); - * - * if (numberValue.getType() == KvsValue::Type::Number) { - * double number = std::get(numberValue.getValue()); - * } - * @endcode - */ - -class KvsValue final{ -public: - /* Define the possible types for KvsValue*/ - using Array = std::vector; - using Object = std::unordered_map; - - /* Enum to represent the type of the value*/ - enum class Type { - i32, - u32, - i64, - u64, - f64, - Boolean, - String, - Null, - Array, - Object - }; - - /* Constructors for each type*/ - explicit KvsValue(int32_t number) : value(number), type(Type::i32) {} - explicit KvsValue(uint32_t number) : value(number), type(Type::u32) {} - explicit KvsValue(int64_t number) : value(number), type(Type::i64) {} - explicit KvsValue(uint64_t number) : value(number), type(Type::u64) {} - explicit KvsValue(double number) : value(number), type(Type::f64) {} - explicit KvsValue(bool boolean) : value(boolean), type(Type::Boolean) {} - explicit KvsValue(const std::string& str) : value(str), type(Type::String) {} - explicit KvsValue(std::nullptr_t) : value(nullptr), type(Type::Null) {} - explicit KvsValue(const Array& array) : value(array), type(Type::Array) {} - explicit KvsValue(const Object& object) : value(object), type(Type::Object) {} - - /* Get the type of the value*/ - Type getType() const { return type; } - - /* Access the underlying value (use std::get to retrieve the value)*/ - const std::variant& getValue() const { - return value; - } - -private: - /* The underlying value*/ - std::variant value; - - /* The type of the value*/ - Type type; -}; - - /** * @class Kvs * @brief A thread-safe key-value store (KVS) CPP Class. @@ -248,48 +92,31 @@ class KvsValue final{ * - `snapshot_count`: Retrieves the number of available snapshots. * - `snapshot_max_count`: Retrieves the maximum number of snapshots allowed. * - `snapshot_restore`: Restores the KVS from a specified snapshot. - * - `get_kvs_filename`: Retrieves the filename associated with a snapshot. - * - `get_hash_filename`: Retrieves the hash filename associated with a snapshot. + * - `get_kvs_filename`: Retrieves the filename (path) associated with a snapshot. + * - `get_hash_filename`: Retrieves the hashname (path) associated with a snapshot. + * + * Private Methods: + * - `snapshot_rotate`: Rotates the snapshots, ensuring that the maximum count is maintained. + * - `parse_json_data`: Parses JSON data into an unordered map of key-value pairs. + * - `open_json`: Opens a JSON file and returns its contents as an unordered map of key-value pairs. + * - `write_json_data`: Writes the provided data to a JSON file. * * Private Members: * - `kvs_mutex`: A mutex for ensuring thread safety. * - `kvs`: An unordered map for storing key-value pairs. * - `default_mutex`: A mutex for default value operations. * - `default_values`: An unordered map for storing optional default values. - * - `filename_prefix`: A string prefix for filenames associated with snapshots. + * - `filename_prefix`: A path prefix for filenames associated with snapshots. * - `flush_on_exit`: An atomic boolean flag indicating whether to flush on exit. + * - `filesystem`: A unique pointer to a filesystem handler for file operations. + * - `parser`: A unique pointer to a JSON parser for reading KVS data. + * - `writer`: A unique pointer to a JSON writer for writing KVS data. * * ----------------Notice---------------- * - Blank should be used instead of void for Result class * Refer: "Blank and score::ResultBlank shall be used for `T` instead of `void`" in result.h * A KVS Object is not copyable, but it can be moved. * - * \brief Example Usage - * \code - * #include - * #include "Kvs.hpp" - * - * int main() { - * // Open kvs - * auto open_res = KvsBuilder(0) - * .need_defaults_flag(true) - * .need_kvs_flag(true) - * .build(); - * if (!open_res) return 1; - * Kvs kvs = std::move(open_res.value()); - * - * // Set and get a value - * kvs.set_value("pi", KvsValue(3.14)); - * auto get_res = kvs.get_value("pi"); - * - * // Delete a key - * kvs.remove_key("pi"); - * std::cout << "has pi? " << (kvs.key_exists("pi").value_or(false) ? "yes" : "no") << "\n"; - * - * - * return 0; - * } - * \endcode */ class Kvs final { @@ -326,12 +153,8 @@ class Kvs final { * - A Kvs object if the operation is successful. * - An ErrorCode if an error occurs during the operation. * - * Possible Error Codes: - * - ErrorCode::FileNotFound: The KVS file was not found. - * - ErrorCode::KvsFileReadError: An error occurred while reading the KVS file. - * - ErrorCode::IntegrityCorrupted: The KVS integrity is corrupted. - * - ErrorCode::ValidationFailed: Validation of the KVS data failed. - * - ErrorCode::ResourceBusy: The KVS resource is currently in use. + * IMPORTANT: Instead of using the Kvs::open method directly, it is recommended to use the KvsBuilder class. + * */ static score::Result open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir); @@ -537,9 +360,6 @@ class Kvs final { /* Private constructor to prevent direct instantiation */ Kvs(); - /* Rotate Snapshots */ - score::ResultBlank snapshot_rotate(); - /* Internal storage and configuration details.*/ std::mutex kvs_mutex; std::unordered_map kvs; @@ -560,61 +380,13 @@ class Kvs final { std::unique_ptr parser; std::unique_ptr writer; + /* Private Methods */ + score::ResultBlank snapshot_rotate(); score::Result> parse_json_data(const std::string& data); score::Result> open_json(const score::filesystem::Path& prefix, OpenJsonNeedFile need_file); score::ResultBlank write_json_data(const std::string& buf); }; - -/** - * @class KvsBuilder - * @brief Builder for opening a KVS object. - */ -class KvsBuilder final { -public: - /** - * @brief Constructs a KvsBuilder for the given KVS instance. - * @param instance_id Unique identifier for the KVS instance. - */ - explicit KvsBuilder(const InstanceId& instance_id); - - /** - * @brief Specify whether default values must be loaded. - * @param flag True to require default values; false to make them optional. - * @return Reference to this builder (for chaining). - */ - KvsBuilder& need_defaults_flag(bool flag); - - /** - * @brief Configure if KVS must exist when opening the KVS. - * @param flag True to require an existing store; false to allow starting empty. - * @return Reference to this builder (for chaining). - */ - KvsBuilder& need_kvs_flag(bool flag); - - /** - * @brief Specify the directory where KVS files are stored. - * @param dir The directory path as a string. - * Use "" or "." for the current directory. - * - * @return Reference to this builder (for chaining). - */ - KvsBuilder& dir(std::string&& dir_path); - - /** - * @brief Builds and opens the Kvs instance with the configured options. - * - * Internally calls Kvs::open() with the selected flags and directory. - * - * @return A score::Result containing the opened store or an ErrorCode. - */ - score::Result build(); - -private: - InstanceId instance_id; ///< ID of the KVS instance - bool need_defaults; ///< Whether default values are required - bool need_kvs; ///< Whether an existing KVS is required - std::string directory; ///< Directory where to store the KVS Files -}; +} /* namespace score::mw::per::kvs */ #endif /* SCORE_LIB_KVS_KVS_HPP */ diff --git a/src/cpp/src/kvsbuilder.cpp b/src/cpp/src/kvsbuilder.cpp new file mode 100644 index 00000000..34acde4f --- /dev/null +++ b/src/cpp/src/kvsbuilder.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "kvsbuilder.hpp" + +namespace score::mw::per::kvs { + +/*********************** KVS Builder Implementation *********************/ +KvsBuilder::KvsBuilder(const InstanceId& instance_id) + : instance_id(instance_id) + , need_defaults(false) + , need_kvs(false) + , directory("./data_folder/") /* Default Directory */ +{} + +KvsBuilder& KvsBuilder::need_defaults_flag(bool flag) { + need_defaults = flag; + return *this; +} + +KvsBuilder& KvsBuilder::need_kvs_flag(bool flag) { + need_kvs = flag; + return *this; +} + +KvsBuilder& KvsBuilder::dir(std::string&& dir_path) { + this->directory = std::move(dir_path); + return *this; +} + + +score::Result KvsBuilder::build() { + score::Result result = score::MakeUnexpected(ErrorCode::UnmappedError); + + /* Use current directory if empty */ + if ("" == directory) { + directory = "./"; + } + + result = Kvs::open( + instance_id, + need_defaults ? OpenNeedDefaults::Required : OpenNeedDefaults::Optional, + need_kvs ? OpenNeedKvs::Required : OpenNeedKvs::Optional, + std::move(directory) + ); + + return result; +} + +} /* namespace score::mw::per::kvs */ diff --git a/src/cpp/src/kvsbuilder.hpp b/src/cpp/src/kvsbuilder.hpp new file mode 100644 index 00000000..23474310 --- /dev/null +++ b/src/cpp/src/kvsbuilder.hpp @@ -0,0 +1,108 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#ifndef SCORE_LIB_KVS_KVSBUILDER_HPP +#define SCORE_LIB_KVS_KVSBUILDER_HPP + +#include +#include "kvs.hpp" + +namespace score::mw::per::kvs { + +/** + * @class KvsBuilder + * @brief Builder for opening a KVS object. + * This class allows configuration of various options for opening a KVS instance, + * such as whether default values are required, whether the KVS data must already exist. + * + * Important: You don't need to include any other header files to use the KVS. + * For documentation of the KVS Functions, refer to the kvs.hpp documentation. + * + * \brief Example Usage + * \code + * #include + * #include "kvsbuilder.hpp" + * + * using namespace score::mw::per::kvs; + * + * int main() { + * // Open kvs + * auto open_res = KvsBuilder(0) + * .need_defaults_flag(true) + * .need_kvs_flag(true) + * .build(); + * if (!open_res) return 1; + * Kvs kvs = std::move(open_res.value()); + * + * // Set and get a value + * kvs.set_value("pi", KvsValue(3.14)); + * auto get_res = kvs.get_value("pi"); + * + * // Delete a key + * kvs.remove_key("pi"); + * std::cout << "has pi? " << (kvs.key_exists("pi").value_or(false) ? "yes" : "no") << "\n"; + * + * + * return 0; + * } + * \endcode + */ +class KvsBuilder final { +public: + /** + * @brief Constructs a KvsBuilder for the given KVS instance. + * @param instance_id Unique identifier for the KVS instance. + */ + explicit KvsBuilder(const InstanceId& instance_id); + + /** + * @brief Specify whether default values must be loaded. + * @param flag True to require default values; false to make them optional. + * @return Reference to this builder (for chaining). + */ + KvsBuilder& need_defaults_flag(bool flag); + + /** + * @brief Configure if KVS must exist when opening the KVS. + * @param flag True to require an existing store; false to allow starting empty. + * @return Reference to this builder (for chaining). + */ + KvsBuilder& need_kvs_flag(bool flag); + + /** + * @brief Specify the directory where KVS files are stored. + * @param dir The directory path as a string. + * Use "" or "." for the current directory. + * + * @return Reference to this builder (for chaining). + */ + KvsBuilder& dir(std::string&& dir_path); + + /** + * @brief Builds and opens the Kvs instance with the configured options. + * + * Internally calls Kvs::open() with the selected flags and directory. + * + * @return A score::Result containing the opened store or an ErrorCode. + */ + score::Result build(); + +private: + InstanceId instance_id; ///< ID of the KVS instance + bool need_defaults; ///< Whether default values are required + bool need_kvs; ///< Whether an existing KVS is required + std::string directory; ///< Directory where to store the KVS Files +}; + +} /* namespace score::mw::per::kvs */ + +#endif /* SCORE_LIB_KVS_KVSBUILDER_HPP */ diff --git a/src/cpp/src/kvsvalue.cpp b/src/cpp/src/kvsvalue.cpp new file mode 100644 index 00000000..98006d1d --- /dev/null +++ b/src/cpp/src/kvsvalue.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "kvsvalue.hpp" diff --git a/src/cpp/src/kvsvalue.hpp b/src/cpp/src/kvsvalue.hpp new file mode 100644 index 00000000..1d0794fb --- /dev/null +++ b/src/cpp/src/kvsvalue.hpp @@ -0,0 +1,109 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#ifndef SCORE_LIB_KVS_KVSVALUE_HPP +#define SCORE_LIB_KVS_KVSVALUE_HPP + +#include +#include +#include +#include +#include + +namespace score::mw::per::kvs { + +/* Define the KvsValue class*/ +/** + * @class KvsValue + * @brief Represents a flexible value type that can hold various data types, + * including numbers, booleans, strings, null, arrays, and objects. + * + * The KvsValue class provides a type-safe way to store and retrieve values of + * different types. It uses a std::variant to hold the underlying value and an + * enum to track the type of the value. + * + * ## Supported Types: + * - Number (double) + * - Boolean (bool) + * - String (std::string) + * - Null (std::nullptr_t) + * - Array (std::vector) + * - Object (std::unordered_map) + * + * ## Public Methods: + * - `KvsValue(double number)`: Constructs a KvsValue holding a number. + * - `KvsValue(bool boolean)`: + * - Access the underlying value using `getValue()` and `std::get`. + * + * ## Example: + * @code + * KvsValue numberValue(42.0); + * KvsValue stringValue("Hello, World!"); + * KvsValue arrayValue(KvsValue::Array{numberValue, stringValue}); + * + * if (numberValue.getType() == KvsValue::Type::Number) { + * double number = std::get(numberValue.getValue()); + * } + * @endcode + */ + +class KvsValue final{ +public: + /* Define the possible types for KvsValue*/ + using Array = std::vector; + using Object = std::unordered_map; + + /* Enum to represent the type of the value*/ + enum class Type { + i32, + u32, + i64, + u64, + f64, + Boolean, + String, + Null, + Array, + Object + }; + + /* Constructors for each type*/ + explicit KvsValue(int32_t number) : value(number), type(Type::i32) {} + explicit KvsValue(uint32_t number) : value(number), type(Type::u32) {} + explicit KvsValue(int64_t number) : value(number), type(Type::i64) {} + explicit KvsValue(uint64_t number) : value(number), type(Type::u64) {} + explicit KvsValue(double number) : value(number), type(Type::f64) {} + explicit KvsValue(bool boolean) : value(boolean), type(Type::Boolean) {} + explicit KvsValue(const std::string& str) : value(str), type(Type::String) {} + explicit KvsValue(std::nullptr_t) : value(nullptr), type(Type::Null) {} + explicit KvsValue(const Array& array) : value(array), type(Type::Array) {} + explicit KvsValue(const Object& object) : value(object), type(Type::Object) {} + + /* Get the type of the value*/ + Type getType() const { return type; } + + /* Access the underlying value (use std::get to retrieve the value)*/ + const std::variant& getValue() const { + return value; + } + +private: + /* The underlying value*/ + std::variant value; + + /* The type of the value*/ + Type type; +}; + +} /* namespace score::mw::per::kvs */ + +#endif /* SCORE_LIB_KVS_KVSVALUE_HPP */ diff --git a/src/cpp/tests/BUILD b/src/cpp/tests/BUILD index d6a648ba..0172e84b 100644 --- a/src/cpp/tests/BUILD +++ b/src/cpp/tests/BUILD @@ -11,24 +11,21 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -cc_library( - name = "json_stubs", - hdrs = glob(["stubs/score/json/**/*.h"]), - includes = ["stubs"], - visibility = [ - "//tests/cpp:__pkg__", - ], -) - cc_test( name = "test_kvs_cpp", size = "small", srcs = [ "test_kvs.cpp", + "test_kvs_builder.cpp", + "test_kvs_error.cpp", + "test_kvs_general.cpp", + "test_kvs_general.hpp", + "test_kvs_helper.cpp", ], visibility = ["//:__pkg__"], deps = [ "//:kvs_cpp", + "//src/cpp/src/internal:kvs_helper", "@googletest//:gtest_main", "@score-baselibs//score/filesystem:filesystem", "@score-baselibs//score/filesystem:mock", @@ -46,6 +43,7 @@ cc_test( visibility = ["//:__pkg__"], deps = [ "//:kvs_cpp", + "//src/cpp/src/internal:kvs_helper", "@google_benchmark//:benchmark", "@score-baselibs//score/filesystem:filesystem", "@score-baselibs//score/json", diff --git a/src/cpp/tests/bm_kvs.cpp b/src/cpp/tests/bm_kvs.cpp index 582617cf..54bc7f89 100644 --- a/src/cpp/tests/bm_kvs.cpp +++ b/src/cpp/tests/bm_kvs.cpp @@ -16,10 +16,11 @@ #define private public #define final -#include "kvs.hpp" +#include "kvsbuilder.hpp" #undef private #undef final #include "internal/kvs_helper.hpp" +using namespace score::mw::per::kvs; static void BM_get_hash_bytes(benchmark::State& state) { // Prepare a test string of configurable size diff --git a/src/cpp/tests/test_kvs.cpp b/src/cpp/tests/test_kvs.cpp index 676bb67b..3659f829 100644 --- a/src/cpp/tests/test_kvs.cpp +++ b/src/cpp/tests/test_kvs.cpp @@ -10,718 +10,7 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Change Private Members and final to public to allow access to member variables and derive from kvsvalue in unittests*/ -#define private public -#define final -#include "kvs.hpp" -#undef private -#undef final -#include "internal/kvs_helper.hpp" -#include "score/json/i_json_parser_mock.h" -#include "score/json/i_json_writer_mock.h" -#include "score/filesystem/filesystem_mock.h" - -//////////////////////////////////////////////////////////////////////////////// -/* Test Environment Setup - Standard Variables for tests*/ - -const std::uint32_t instance = 123; -const InstanceId instance_id{instance}; - -/* Notice: score::filesystem::Path could be constructed implicitly from std::string, but for readability, - we mostly use explicit construction from those strings in the testcode */ -const std::string data_dir = "./data_folder/"; -const std::string default_prefix = data_dir + "kvs_"+std::to_string(instance)+"_default"; -const std::string kvs_prefix = data_dir + "kvs_"+std::to_string(instance)+"_0"; -const std::string filename_prefix = data_dir + "kvs_"+std::to_string(instance); - -const std::string default_json = R"({ - "default": { - "t": "i32", - "v": 5 - } -})"; -const std::string kvs_json = R"({ - "kvs": { - "t": "i32", - "v": 2 - } -})"; - -//////////////////////////////////////////////////////////////////////////////// - -/* adler32 control instance */ -uint32_t adler32(const std::string& data) { - const uint32_t mod = 65521; - uint32_t a = 1, b = 0; - for (unsigned char c : data) { - a = (a + c) % mod; - b = (b + a) % mod; - } - return (b << 16) | a; -} - -void cleanup_environment() { - /* Cleanup the test environment */ - if (std::filesystem::exists(data_dir)) { - for (auto& p : std::filesystem::recursive_directory_iterator(data_dir)) { - std::filesystem::permissions(p, - std::filesystem::perms::owner_all | std::filesystem::perms::group_all | std::filesystem::perms::others_all, - std::filesystem::perm_options::replace); - } - std::filesystem::remove_all(data_dir); - } -} - -/* Create Test environment with default data, which is needed in most testcases */ -void prepare_environment(){ - /* Prepare the test environment */ - mkdir(data_dir.c_str(), 0777); - - std::ofstream default_json_file(default_prefix + ".json"); - default_json_file << default_json; - default_json_file.close(); - - std::ofstream kvs_json_file(kvs_prefix + ".json"); - kvs_json_file << kvs_json; - kvs_json_file.close(); - - uint32_t default_hash = adler32(default_json); - uint32_t kvs_hash = adler32(kvs_json); - - std::ofstream default_hash_file(default_prefix + ".hash", std::ios::binary); - default_hash_file.put((default_hash >> 24) & 0xFF); - default_hash_file.put((default_hash >> 16) & 0xFF); - default_hash_file.put((default_hash >> 8) & 0xFF); - default_hash_file.put(default_hash & 0xFF); - default_hash_file.close(); - - std::ofstream kvs_hash_file(kvs_prefix + ".hash", std::ios::binary); - kvs_hash_file.put((kvs_hash >> 24) & 0xFF); - kvs_hash_file.put((kvs_hash >> 16) & 0xFF); - kvs_hash_file.put((kvs_hash >> 8) & 0xFF); - kvs_hash_file.put(kvs_hash & 0xFF); - kvs_hash_file.close(); -} - -//////////////////////////////////////////////////////////////////////////////// - -TEST(kvs_calculate_hash_adler32, calculate_hash_adler32) { - /* Test the adler32 hash calculation - Parsing test is done automatically by the open tests */ - - std::string test_data = "Hello, World!"; - uint32_t calculated_hash = adler32(test_data); - EXPECT_EQ(calculated_hash, calculate_hash_adler32(test_data)); - - std::array buf{}; - std::istringstream stream(test_data); - stream.read(reinterpret_cast(buf.data()), buf.size()); - - std::array value = { - uint8_t((calculated_hash >> 24) & 0xFF), - uint8_t((calculated_hash >> 16) & 0xFF), - uint8_t((calculated_hash >> 8) & 0xFF), - uint8_t((calculated_hash ) & 0xFF) - }; - EXPECT_EQ(value, get_hash_bytes(test_data)); - -} - -TEST(kvs_calculate_hash_adler32, calculate_hash_adler32_large_data) { - // Create Teststring with more than 5552 characters to ensure that the hash is calculated correctly - std::string large_data(6000, 'A'); - uint32_t hash = calculate_hash_adler32(large_data); - - EXPECT_EQ(adler32(large_data), hash); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_bool) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("bool"))); - obj.emplace("v", score::json::Any(true)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::Boolean); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_i32) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("i32"))); - obj.emplace("v", score::json::Any(42.0)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::i32); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_u32) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("u32"))); - obj.emplace("v", score::json::Any(42.0)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::u32); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_i64) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("i64"))); - obj.emplace("v", score::json::Any(42.0)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::i64); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_u64) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("u64"))); - obj.emplace("v", score::json::Any(42.0)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::u64); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_f64) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("f64"))); - obj.emplace("v", score::json::Any(42.0)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::f64); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_string) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("str"))); - obj.emplace("v", score::json::Any(std::string("test"))); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::String); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_null) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("null"))); - obj.emplace("v", score::json::Any(score::json::Null())); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::Null); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_array) { - score::json::List list; - score::json::Object inner_obj_1; - inner_obj_1.emplace("t", score::json::Any(std::string("bool"))); - inner_obj_1.emplace("v", score::json::Any(true)); - list.push_back(score::json::Any(std::move(inner_obj_1))); - - score::json::Object inner_obj_2; - inner_obj_2.emplace("t", score::json::Any(std::string("f64"))); - inner_obj_2.emplace("v", score::json::Any(1.1)); - list.push_back(score::json::Any(std::move(inner_obj_2))); - - score::json::Object inner_obj_3; - inner_obj_3.emplace("t", score::json::Any(std::string("str"))); - inner_obj_3.emplace("v", score::json::Any(std::string("test"))); - list.push_back(score::json::Any(std::move(inner_obj_3))); - - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("arr"))); - obj.emplace("v", score::json::Any(std::move(list))); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::Array); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_object) { - score::json::Object inner_obj_1; - inner_obj_1.emplace("t", score::json::Any(std::string("bool"))); - inner_obj_1.emplace("v", score::json::Any(true)); - - score::json::Object inner_obj_2; - inner_obj_2.emplace("t", score::json::Any(std::string("f64"))); - inner_obj_2.emplace("v", score::json::Any(42.0)); - - score::json::Object combined_obj; - combined_obj.emplace("flag", score::json::Any(std::move(inner_obj_1))); - combined_obj.emplace("count", score::json::Any(std::move(inner_obj_2))); - - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("obj"))); - obj.emplace("v", score::json::Any(std::move(combined_obj))); - - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - ASSERT_TRUE(result); - EXPECT_EQ(result.value().getType(), KvsValue::Type::Object); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_format_invalid) { - score::json::Object obj_type; - obj_type.emplace("invalid", score::json::Any(std::string("bool"))); - obj_type.emplace("v", score::json::Any(true)); - score::json::Any any_obj_type(std::move(obj_type)); - auto result = any_to_kvsvalue(any_obj_type); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); - - score::json::Object obj_value; - obj_value.emplace("t", score::json::Any(std::string("bool"))); - obj_value.emplace("invalid", score::json::Any(true)); - score::json::Any any_obj_value(std::move(obj_value)); - result = any_to_kvsvalue(any_obj_value); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_no_object) { - score::json::Any any_bool(true); - auto result = any_to_kvsvalue(any_bool); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_type_no_string) { - score::json::Object obj; - obj.emplace("t", score::json::Any(42.0)); // Not a string - obj.emplace("v", score::json::Any(true)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_type_invalid) { - score::json::Object obj; - obj.emplace("t", "invalid"); - obj.emplace("v", score::json::Any(true)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_no_string_type) { - score::json::Object obj; - obj.emplace("t", score::json::Any(42.0)); // Not a string - obj.emplace("v", score::json::Any(true)); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_i32){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("i32"))); - obj.emplace("v", score::json::Any("invalid")); // Invalid value for I32 - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_u32){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("u32"))); - obj.emplace("v", score::json::Any("invalid")); // Invalid value for U32 - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_i64){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("i64"))); - obj.emplace("v", score::json::Any("invalid")); // Invalid value for I64 - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_u64){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("u64"))); - obj.emplace("v", score::json::Any("invalid")); // Invalid value for U64 - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_f64){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("f64"))); - obj.emplace("v", score::json::Any("invalid")); // Invalid value for F64 - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_boolean){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("bool"))); - obj.emplace("v", score::json::Any(42.0)); // Invalid value for Boolean - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_string){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("str"))); - obj.emplace("v", score::json::Any(42.0)); // Invalid value for String - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_null){ - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("null"))); - obj.emplace("v", score::json::Any(42.0)); // Invalid value for Null - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_array) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("arr"))); - obj.emplace("v", score::json::Any(42.0)); // Invalid value for Array - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_object) { - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("obj"))); - obj.emplace("v", score::json::Any(42.0)); // Invalid value for Object - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_array_with_invalid_element) { - score::json::List list; - - score::json::Object inner_obj1; - inner_obj1.emplace("t", score::json::Any(std::string("bool"))); - inner_obj1.emplace("v", score::json::Any(true)); - list.push_back(score::json::Any(std::move(inner_obj1))); - - score::json::Object inner_obj2; - inner_obj2.emplace("t", score::json::Any(std::string("InvalidType"))); - inner_obj2.emplace("v", score::json::Any(std::string("test"))); - list.push_back(score::json::Any(std::move(inner_obj2))); - - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("arr"))); - obj.emplace("v", score::json::Any(std::move(list))); - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_object_with_invalid_value) { - score::json::Object inner_obj1; - inner_obj1.emplace("t", score::json::Any(std::string("bool"))); - inner_obj1.emplace("v", score::json::Any(true)); - - score::json::Object inner_obj2; - inner_obj2.emplace("t", score::json::Any(std::string("InvalidType"))); - inner_obj2.emplace("v", score::json::Any(42.0)); - - score::json::Object value_obj; - value_obj.emplace("flag", score::json::Any(std::move(inner_obj1))); - value_obj.emplace("count", score::json::Any(std::move(inner_obj2))); - - score::json::Object obj; - obj.emplace("t", score::json::Any(std::string("obj"))); - obj.emplace("v", score::json::Any(std::move(value_obj))); - - score::json::Any any_obj(std::move(obj)); - auto result = any_to_kvsvalue(any_obj); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -/* Mock KvsValue for testing purposes (kvsvalue_to_any) */ -class BrokenKvsValue : public KvsValue { -public: - BrokenKvsValue() : KvsValue(nullptr) { - /* Intentionally break the type by assigning an invalid value */ - *(Type*)&this->type = static_cast(999); - } -}; - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_null) { - KvsValue null_val(nullptr); - auto result = kvsvalue_to_any(null_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "null"); - EXPECT_TRUE(obj.at("v").As().has_value()); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_boolean) { - KvsValue bool_val(true); - auto result = kvsvalue_to_any(bool_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "bool"); - EXPECT_EQ(obj.at("v").As().value(), true); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_i32) { - KvsValue i32_val(static_cast(42)); - auto result = kvsvalue_to_any(i32_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "i32"); - EXPECT_EQ(obj.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_u32) { - KvsValue u32_val(static_cast(42)); - auto result = kvsvalue_to_any(u32_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "u32"); - EXPECT_EQ(obj.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_i64) { - KvsValue i64_val(static_cast(42)); - auto result = kvsvalue_to_any(i64_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "i64"); - EXPECT_EQ(obj.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_u64) { - KvsValue u64_val(static_cast(42)); - auto result = kvsvalue_to_any(u64_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "u64"); - EXPECT_EQ(obj.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_f64) { - KvsValue f64_val(42.0); - auto result = kvsvalue_to_any(f64_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "f64"); - EXPECT_EQ(obj.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_string) { - std::string str = "test"; - KvsValue string_val(str); - auto result = kvsvalue_to_any(string_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "str"); - EXPECT_EQ(obj.at("v").As().value().get(), "test"); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_array) { - KvsValue::Array array; - array.push_back(KvsValue(true)); - array.push_back(KvsValue(1.1)); - array.push_back(KvsValue(std::string("test"))); - KvsValue array_val(array); - auto result = kvsvalue_to_any(array_val); - ASSERT_TRUE(result); - const auto& obj = result.value().As().value().get(); - EXPECT_EQ(obj.at("t").As().value().get(), "arr"); - const auto& list = obj.at("v").As().value().get(); - EXPECT_EQ(list.size(), 3); - - const auto& elem0 = list[0].As().value().get(); - EXPECT_EQ(elem0.at("t").As().value().get(), "bool"); - EXPECT_EQ(elem0.at("v").As().value(), true); - - const auto& elem1 = list[1].As().value().get(); - EXPECT_EQ(elem1.at("t").As().value().get(), "f64"); - EXPECT_EQ(elem1.at("v").As().value(), 1.1); - - const auto& elem2 = list[2].As().value().get(); - EXPECT_EQ(elem2.at("t").As().value().get(), "str"); - EXPECT_EQ(elem2.at("v").As().value().get(), "test"); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_object) { - KvsValue::Object obj; - obj.emplace("flag", KvsValue(true)); // Boolean - obj.emplace("count", KvsValue(42.0)); // F64 - KvsValue obj_val(obj); - - auto result = kvsvalue_to_any(obj_val); - ASSERT_TRUE(result); - - const auto& outer_obj = result.value().As().value().get(); - EXPECT_EQ(outer_obj.at("t").As().value().get(), "obj"); - - const auto& inner_obj = outer_obj.at("v").As().value().get(); - - const auto& flag_entry = inner_obj.at("flag").As().value().get(); - EXPECT_EQ(flag_entry.at("t").As().value().get(), "bool"); - EXPECT_EQ(flag_entry.at("v").As().value(), true); - - const auto& count_entry = inner_obj.at("count").As().value().get(); - EXPECT_EQ(count_entry.at("t").As().value().get(), "f64"); - EXPECT_EQ(count_entry.at("v").As().value(), 42.0); -} - -TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_invalid) { - /* InvalidType */ - BrokenKvsValue invalid; - auto result = kvsvalue_to_any(invalid); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); - - /* Invalid values in array and object */ - KvsValue::Array array; - array.push_back(KvsValue(42.0)); - array.push_back(invalid); - KvsValue array_invalid(array); - result = kvsvalue_to_any(array_invalid); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); - - KvsValue::Object obj; - obj.emplace("valid", KvsValue(42.0)); - obj.emplace("invalid", invalid); - KvsValue obj_invalid(obj); - result = kvsvalue_to_any(obj_invalid); - EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); -} - -TEST(kvs_MessageFor, MessageFor) { - struct { - MyErrorCode code; - std::string_view expected_message; - } test_cases[] = { - {MyErrorCode::UnmappedError, "Error that was not yet mapped"}, - {MyErrorCode::FileNotFound, "File not found"}, - {MyErrorCode::KvsFileReadError, "KVS file read error"}, - {MyErrorCode::KvsHashFileReadError, "KVS hash file read error"}, - {MyErrorCode::JsonParserError, "JSON parser error"}, - {MyErrorCode::JsonGeneratorError, "JSON generator error"}, - {MyErrorCode::PhysicalStorageFailure, "Physical storage failure"}, - {MyErrorCode::IntegrityCorrupted, "Integrity corrupted"}, - {MyErrorCode::ValidationFailed, "Validation failed"}, - {MyErrorCode::EncryptionFailed, "Encryption failed"}, - {MyErrorCode::ResourceBusy, "Resource is busy"}, - {MyErrorCode::OutOfStorageSpace, "Out of storage space"}, - {MyErrorCode::QuotaExceeded, "Quota exceeded"}, - {MyErrorCode::AuthenticationFailed, "Authentication failed"}, - {MyErrorCode::KeyNotFound, "Key not found"}, - {MyErrorCode::KeyDefaultNotFound, "Key default value not found"}, - {MyErrorCode::SerializationFailed, "Serialization failed"}, - {MyErrorCode::InvalidSnapshotId, "Invalid snapshot ID"}, - {MyErrorCode::ConversionFailed, "Conversion failed"}, - {MyErrorCode::MutexLockFailed, "Mutex failed"}, - {MyErrorCode::InvalidValueType, "Invalid value type"}, - }; - for (const auto& test : test_cases) { - SCOPED_TRACE(static_cast(test.code)); - score::result::ErrorCode code = static_cast(test.code); - EXPECT_EQ(my_error_domain.MessageFor(code), test.expected_message); - } - - score::result::ErrorCode invalid_code = static_cast(9999); - EXPECT_EQ(my_error_domain.MessageFor(invalid_code), "Unknown Error!"); - -} - -TEST(kvs_kvsbuilder, kvsbuilder_build) { - /* This test also checks the kvs open function with the KvsBuilder */ - - /* Test the KvsBuilder constructor */ - KvsBuilder builder(instance_id); - EXPECT_EQ(builder.instance_id.id, instance_id.id); - EXPECT_EQ(builder.need_defaults, false); - EXPECT_EQ(builder.need_kvs, false); - - /* Test the KvsBuilder methods */ - builder.need_defaults_flag(true); - EXPECT_EQ(builder.need_defaults, true); - builder.need_kvs_flag(true); - EXPECT_EQ(builder.need_kvs, true); - builder.dir("./kvsbuilder/"); - EXPECT_EQ(builder.directory, "./kvsbuilder/"); - - /* Test the KvsBuilder build method */ - /* We want to check, if OpenNeedDefaults::Required and OpenNeedKvs::Required is passed correctly - open() function should return an error, since the files are not available */ - auto result_build = builder.build(); - ASSERT_FALSE(result_build); - EXPECT_EQ(static_cast(*result_build.error()), MyErrorCode::KvsFileReadError); /* This error occurs in open_json and is passed through open()*/ - builder.need_defaults_flag(false); - result_build = builder.build(); - ASSERT_FALSE(result_build); - EXPECT_EQ(static_cast(*result_build.error()), MyErrorCode::KvsFileReadError); /* This error occurs in open_json and is passed through open()*/ - builder.need_kvs_flag(false); - result_build = builder.build(); - EXPECT_TRUE(result_build); - result_build.value().flush_on_exit = false; - EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvsbuilder/kvs_"+std::to_string(instance_id.id)); -} - -TEST(kvs_kvsbuilder, kvsbuilder_directory_check) { - - /* Test the KvsBuilder with all configurations for the current working directory */ - KvsBuilder builder(instance_id); - builder.dir(""); - auto result_build = builder.build(); - EXPECT_TRUE(result_build); - EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); - - builder.dir("./"); - result_build = builder.build(); - EXPECT_TRUE(result_build); - EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); - - builder.dir("."); - result_build = builder.build(); - EXPECT_TRUE(result_build); - EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); - -} +#include "test_kvs_general.hpp" TEST(kvs_constructor, move_constructor) { /* Also checks set_flush_on_exit */ @@ -832,7 +121,7 @@ TEST(kvs_TEST, parse_json_data_failure) { auto result = kvs->parse_json_data("data_not_used_in_mocking"); EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::JsonParserError); + EXPECT_EQ(result.error(), ErrorCode::JsonParserError); /* No Object returned Failure */ @@ -847,7 +136,7 @@ TEST(kvs_TEST, parse_json_data_failure) { result = kvs->parse_json_data("data_not_used_in_mocking"); EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::JsonParserError); + EXPECT_EQ(result.error(), ErrorCode::JsonParserError); /* any_to_kvsvalue error */ @@ -867,7 +156,7 @@ TEST(kvs_TEST, parse_json_data_failure) { result = kvs->parse_json_data("data_not_used_in_mocking"); EXPECT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), MyErrorCode::InvalidValueType); //error is passed from any_to_kvsvalue + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); //error is passed from any_to_kvsvalue cleanup_environment(); } @@ -916,13 +205,13 @@ TEST(kvs_open_json, open_json_json_invalid) { auto result = kvs.open_json(score::filesystem::Path(kvs_prefix), OpenJsonNeedFile::Required); ASSERT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::JsonParserError); /* Errorcode passed by parse json function*/ + EXPECT_EQ(static_cast(*result.error()), ErrorCode::JsonParserError); /* Errorcode passed by parse json function*/ /* JSON not existing */ system(("rm -rf " + kvs_prefix + ".json").c_str()); result = kvs.open_json(score::filesystem::Path(kvs_prefix), OpenJsonNeedFile::Required); ASSERT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::KvsFileReadError); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::KvsFileReadError); cleanup_environment(); } @@ -940,19 +229,19 @@ TEST(kvs_open_json, open_json_hash_invalid) { auto result = kvs.open_json(score::filesystem::Path(kvs_prefix), OpenJsonNeedFile::Optional); ASSERT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::ValidationFailed); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::ValidationFailed); /* Hash not existing */ system(("rm -rf " + kvs_prefix + ".hash").c_str()); result = kvs.open_json(score::filesystem::Path(kvs_prefix), OpenJsonNeedFile::Optional); ASSERT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::KvsHashFileReadError); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::KvsHashFileReadError); /* JSON not existing */ system(("rm -rf " + kvs_prefix + ".json").c_str()); result = kvs.open_json(score::filesystem::Path(kvs_prefix), OpenJsonNeedFile::Required); ASSERT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::KvsFileReadError); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::KvsFileReadError); cleanup_environment(); } @@ -1005,7 +294,7 @@ TEST(kvs_reset, reset_failure){ std::unique_lock lock(result.value().kvs_mutex); auto reset_result = result.value().reset(); EXPECT_FALSE(reset_result); - EXPECT_EQ(static_cast(*reset_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*reset_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1049,7 +338,7 @@ TEST(kvs_get_all_keys, get_all_keys_failure){ auto get_all_keys_result = result.value().get_all_keys(); EXPECT_FALSE(get_all_keys_result); - EXPECT_EQ(static_cast(*get_all_keys_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*get_all_keys_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1089,7 +378,7 @@ TEST(kvs_key_exists, key_exists_failure){ std::unique_lock lock(result.value().kvs_mutex); auto exists_result = result.value().key_exists("kvs"); EXPECT_FALSE(exists_result); - EXPECT_EQ(static_cast(*exists_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*exists_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1138,7 +427,7 @@ TEST(kvs_get_value, get_value_failure){ /* Check if non-existing key returns error */ auto get_value_result = result.value().get_value("non_existing_key"); EXPECT_FALSE(get_value_result); - EXPECT_EQ(get_value_result.error(), MyErrorCode::KeyNotFound); + EXPECT_EQ(get_value_result.error(), ErrorCode::KeyNotFound); /* Mutex locked */ result = Kvs::open(instance_id, OpenNeedDefaults::Required, OpenNeedKvs::Required, std::string(data_dir)); @@ -1147,7 +436,7 @@ TEST(kvs_get_value, get_value_failure){ std::unique_lock lock(result.value().kvs_mutex); get_value_result = result.value().get_value("kvs"); EXPECT_FALSE(get_value_result); - EXPECT_EQ(static_cast(*get_value_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*get_value_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1187,7 +476,7 @@ TEST(kvs_get_default_value, get_default_value_failure){ /* Check if non-existing key returns error */ auto get_def_value_result = result.value().get_default_value("non_existing_key"); EXPECT_FALSE(get_def_value_result); - EXPECT_EQ(get_def_value_result.error(), MyErrorCode::KeyNotFound); + EXPECT_EQ(get_def_value_result.error(), ErrorCode::KeyNotFound); cleanup_environment(); } @@ -1233,7 +522,7 @@ TEST(kvs_reset_key, reset_key_failure){ /* Reset a non-existing key */ auto reset_key_result = result.value().reset_key("non_existing_key"); EXPECT_FALSE(reset_key_result); - EXPECT_EQ(reset_key_result.error(), MyErrorCode::KeyDefaultNotFound); + EXPECT_EQ(reset_key_result.error(), ErrorCode::KeyDefaultNotFound); /* Reset a key without default value */ result = Kvs::open(instance_id, OpenNeedDefaults::Required, OpenNeedKvs::Required, std::string(data_dir)); @@ -1242,7 +531,7 @@ TEST(kvs_reset_key, reset_key_failure){ result.value().default_values.clear(); // Clear default values to ensure no default value exists for "kvs" reset_key_result = result.value().reset_key("kvs"); EXPECT_FALSE(reset_key_result); - EXPECT_EQ(reset_key_result.error(), MyErrorCode::KeyDefaultNotFound); + EXPECT_EQ(reset_key_result.error(), ErrorCode::KeyDefaultNotFound); /* Mutex locked */ result = Kvs::open(instance_id, OpenNeedDefaults::Required, OpenNeedKvs::Required, std::string(data_dir)); @@ -1251,7 +540,7 @@ TEST(kvs_reset_key, reset_key_failure){ std::unique_lock lock(result.value().kvs_mutex); reset_key_result = result.value().reset_key("kvs"); EXPECT_FALSE(reset_key_result); - EXPECT_EQ(static_cast(*reset_key_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*reset_key_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1318,7 +607,7 @@ TEST(kvs_set_value, set_value_failure){ std::unique_lock lock(result.value().kvs_mutex); auto set_value_result = result.value().set_value("new_key", KvsValue(3.0)); EXPECT_FALSE(set_value_result); - EXPECT_EQ(static_cast(*set_value_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*set_value_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1352,7 +641,7 @@ TEST(kvs_remove_key, remove_key_failure){ /* Remove a non-existing key */ auto remove_key_result = result.value().remove_key("non_existing_key"); EXPECT_FALSE(remove_key_result); - EXPECT_EQ(remove_key_result.error(), MyErrorCode::KeyNotFound); + EXPECT_EQ(remove_key_result.error(), ErrorCode::KeyNotFound); /* Mutex locked */ result = Kvs::open(instance_id, OpenNeedDefaults::Required, OpenNeedKvs::Required, std::string(data_dir)); @@ -1361,7 +650,7 @@ TEST(kvs_remove_key, remove_key_failure){ std::unique_lock lock(result.value().kvs_mutex); remove_key_result = result.value().remove_key("kvs"); EXPECT_FALSE(remove_key_result); - EXPECT_EQ(static_cast(*remove_key_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*remove_key_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1429,14 +718,14 @@ TEST(kvs_write_json_data, write_json_data_filesystem_failure){ auto result = kvs->write_json_data(kvs_json); EXPECT_FALSE(result); - EXPECT_EQ(result.error(), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(result.error(), ErrorCode::PhysicalStorageFailure); /* Test if path argument is missing parent path (will only occur if semantic errors will be done in flush() )*/ kvs->filename_prefix = score::filesystem::Path("no_parent_path"); /* Set the filename prefix to the test prefix */ result = kvs->write_json_data(kvs_json); EXPECT_FALSE(result); - EXPECT_EQ(result.error(), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(result.error(), ErrorCode::PhysicalStorageFailure); cleanup_environment(); } @@ -1457,7 +746,7 @@ TEST(kvs_write_json_data, write_json_data_permissions_failure){ kvs->filename_prefix = score::filesystem::Path(filename_prefix); /* Reset the filename prefix to the test prefix */ auto result = kvs->write_json_data(kvs_json); EXPECT_FALSE(result); - EXPECT_EQ(result.error(), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(result.error(), ErrorCode::PhysicalStorageFailure); /* Test writing to a non-writable kvs file */ std::ofstream out_json(kvs_prefix + ".json"); @@ -1467,7 +756,7 @@ TEST(kvs_write_json_data, write_json_data_permissions_failure){ kvs->filename_prefix = score::filesystem::Path(filename_prefix); /* Reset the filename prefix to the test prefix */ result = kvs->write_json_data(kvs_json); EXPECT_FALSE(result); - EXPECT_EQ(result.error(), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(result.error(), ErrorCode::PhysicalStorageFailure); cleanup_environment(); @@ -1548,7 +837,7 @@ TEST(kvs_snapshot_rotate, snapshot_rotate_failure_renaming_json){ std::filesystem::create_directory(filename_prefix + "_" + std::to_string(KVS_MAX_SNAPSHOTS) + ".json"); auto rotate_result = result.value().snapshot_rotate(); EXPECT_FALSE(rotate_result); - EXPECT_EQ(static_cast(*rotate_result.error()), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(static_cast(*rotate_result.error()), ErrorCode::PhysicalStorageFailure); cleanup_environment(); } @@ -1572,7 +861,7 @@ TEST(kvs_snapshot_rotate, snapshot_rotate_failure_renaming_hash){ std::filesystem::create_directory(filename_prefix + "_" + std::to_string(KVS_MAX_SNAPSHOTS) + ".hash"); auto rotate_result = result.value().snapshot_rotate(); EXPECT_FALSE(rotate_result); - EXPECT_EQ(static_cast(*rotate_result.error()), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(static_cast(*rotate_result.error()), ErrorCode::PhysicalStorageFailure); cleanup_environment(); } @@ -1589,7 +878,7 @@ TEST(kvs_snapshot_rotate, snapshot_rotate_failure_mutex){ std::unique_lock lock(result.value().kvs_mutex); auto rotate_result = result.value().snapshot_rotate(); EXPECT_FALSE(rotate_result); - EXPECT_EQ(static_cast(*rotate_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*rotate_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1656,7 +945,7 @@ TEST(kvs_flush, flush_failure_mutex){ std::unique_lock lock(result.value().kvs_mutex); auto flush_result = result.value().flush(); EXPECT_FALSE(flush_result); - EXPECT_EQ(static_cast(*flush_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*flush_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1675,7 +964,7 @@ TEST(kvs_flush, flush_failure_rotate_snapshots){ auto flush_result = result.value().flush(); EXPECT_FALSE(flush_result); - EXPECT_EQ(static_cast(*flush_result.error()), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(static_cast(*flush_result.error()), ErrorCode::PhysicalStorageFailure); cleanup_environment(); } @@ -1693,7 +982,7 @@ TEST(kvs_flush, flush_failure_kvsvalue_invalid){ auto flush_result_invalid = result.value().flush(); EXPECT_FALSE(flush_result_invalid); - EXPECT_EQ(flush_result_invalid.error(), MyErrorCode::InvalidValueType); + EXPECT_EQ(flush_result_invalid.error(), ErrorCode::InvalidValueType); cleanup_environment(); } @@ -1713,7 +1002,7 @@ TEST(kvs_flush, flush_failure_json_writer){ kvs->writer = std::move(mock_writer); auto result = kvs.value().flush(); EXPECT_FALSE(result); - EXPECT_EQ(result.error(), MyErrorCode::JsonGeneratorError); + EXPECT_EQ(result.error(), ErrorCode::JsonGeneratorError); cleanup_environment(); } @@ -1759,7 +1048,7 @@ TEST(kvs_snapshot_count, snapshot_count_invalid){ auto result = kvs.value().snapshot_count(); EXPECT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::PhysicalStorageFailure); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::PhysicalStorageFailure); } @@ -1817,12 +1106,12 @@ TEST(kvs_snapshot_restore, snapshot_restore_failure_invalid_snapshot_id){ /* Restore Snapshot ID 0 -> Current KVS*/ auto restore_result = result.value().snapshot_restore(0); ASSERT_FALSE(restore_result); - EXPECT_EQ(static_cast(*restore_result.error()), MyErrorCode::InvalidSnapshotId); + EXPECT_EQ(static_cast(*restore_result.error()), ErrorCode::InvalidSnapshotId); /* Restore Snapshot ID higher than snapshot_count (e.g. KVS_MAX_SNAPSHOTS +1) */ restore_result = result.value().snapshot_restore(KVS_MAX_SNAPSHOTS + 1); ASSERT_FALSE(restore_result); - EXPECT_EQ(static_cast(*restore_result.error()), MyErrorCode::InvalidSnapshotId); + EXPECT_EQ(static_cast(*restore_result.error()), ErrorCode::InvalidSnapshotId); cleanup_environment(); } @@ -1841,7 +1130,7 @@ TEST(kvs_snapshot_restore, snapshot_restore_failure_open_json){ auto restore_result = result.value().snapshot_restore(1); EXPECT_FALSE(restore_result); - EXPECT_EQ(static_cast(*restore_result.error()), MyErrorCode::ValidationFailed); /* passed by open_json*/ + EXPECT_EQ(static_cast(*restore_result.error()), ErrorCode::ValidationFailed); /* passed by open_json*/ cleanup_environment(); } @@ -1857,7 +1146,7 @@ TEST(kvs_snapshot_restore, snapshot_restore_failure_mutex){ std::unique_lock lock(result.value().kvs_mutex); auto restore_result = result.value().snapshot_restore(1); EXPECT_FALSE(restore_result); - EXPECT_EQ(static_cast(*restore_result.error()), MyErrorCode::MutexLockFailed); + EXPECT_EQ(static_cast(*restore_result.error()), ErrorCode::MutexLockFailed); cleanup_environment(); } @@ -1929,7 +1218,7 @@ TEST(kvs_get_filename, get_kvs_filename_failure){ /* Testfiles not available */ auto result = kvs.value().get_kvs_filename(SnapshotId(1)); EXPECT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::FileNotFound); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::FileNotFound); /* Filesystem exists error */ /* Mock Filesystem */ @@ -1980,7 +1269,7 @@ TEST(kvs_get_filename, get_hashname_failure){ /* Testfiles not available */ auto result = kvs.value().get_hash_filename(SnapshotId(1)); EXPECT_FALSE(result); - EXPECT_EQ(static_cast(*result.error()), MyErrorCode::FileNotFound); + EXPECT_EQ(static_cast(*result.error()), ErrorCode::FileNotFound); /* Filesystem exists error */ /* Mock Filesystem */ diff --git a/src/cpp/tests/test_kvs_builder.cpp b/src/cpp/tests/test_kvs_builder.cpp new file mode 100644 index 00000000..d93ccb09 --- /dev/null +++ b/src/cpp/tests/test_kvs_builder.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "test_kvs_general.hpp" + + +TEST(kvs_kvsbuilder, kvsbuilder_build) { + /* This test also checks the kvs open function with the KvsBuilder */ + + /* Test the KvsBuilder constructor */ + KvsBuilder builder(instance_id); + EXPECT_EQ(builder.instance_id.id, instance_id.id); + EXPECT_EQ(builder.need_defaults, false); + EXPECT_EQ(builder.need_kvs, false); + + /* Test the KvsBuilder methods */ + builder.need_defaults_flag(true); + EXPECT_EQ(builder.need_defaults, true); + builder.need_kvs_flag(true); + EXPECT_EQ(builder.need_kvs, true); + builder.dir("./kvsbuilder/"); + EXPECT_EQ(builder.directory, "./kvsbuilder/"); + + /* Test the KvsBuilder build method */ + /* We want to check, if OpenNeedDefaults::Required and OpenNeedKvs::Required is passed correctly + open() function should return an error, since the files are not available */ + auto result_build = builder.build(); + ASSERT_FALSE(result_build); + EXPECT_EQ(static_cast(*result_build.error()), ErrorCode::KvsFileReadError); /* This error occurs in open_json and is passed through open()*/ + builder.need_defaults_flag(false); + result_build = builder.build(); + ASSERT_FALSE(result_build); + EXPECT_EQ(static_cast(*result_build.error()), ErrorCode::KvsFileReadError); /* This error occurs in open_json and is passed through open()*/ + builder.need_kvs_flag(false); + result_build = builder.build(); + EXPECT_TRUE(result_build); + result_build.value().flush_on_exit = false; + EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvsbuilder/kvs_"+std::to_string(instance_id.id)); +} + +TEST(kvs_kvsbuilder, kvsbuilder_directory_check) { + + /* Test the KvsBuilder with all configurations for the current working directory */ + KvsBuilder builder(instance_id); + builder.dir(""); + auto result_build = builder.build(); + EXPECT_TRUE(result_build); + EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); + + builder.dir("./"); + result_build = builder.build(); + EXPECT_TRUE(result_build); + EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); + + builder.dir("."); + result_build = builder.build(); + EXPECT_TRUE(result_build); + EXPECT_EQ(result_build.value().filename_prefix.CStr(), "./kvs_"+std::to_string(instance_id.id)); + +} diff --git a/src/cpp/tests/test_kvs_error.cpp b/src/cpp/tests/test_kvs_error.cpp new file mode 100644 index 00000000..e9634221 --- /dev/null +++ b/src/cpp/tests/test_kvs_error.cpp @@ -0,0 +1,52 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "test_kvs_general.hpp" + + +TEST(kvs_MessageFor, MessageFor) { + struct { + ErrorCode code; + std::string_view expected_message; + } test_cases[] = { + {ErrorCode::UnmappedError, "Error that was not yet mapped"}, + {ErrorCode::FileNotFound, "File not found"}, + {ErrorCode::KvsFileReadError, "KVS file read error"}, + {ErrorCode::KvsHashFileReadError, "KVS hash file read error"}, + {ErrorCode::JsonParserError, "JSON parser error"}, + {ErrorCode::JsonGeneratorError, "JSON generator error"}, + {ErrorCode::PhysicalStorageFailure, "Physical storage failure"}, + {ErrorCode::IntegrityCorrupted, "Integrity corrupted"}, + {ErrorCode::ValidationFailed, "Validation failed"}, + {ErrorCode::EncryptionFailed, "Encryption failed"}, + {ErrorCode::ResourceBusy, "Resource is busy"}, + {ErrorCode::OutOfStorageSpace, "Out of storage space"}, + {ErrorCode::QuotaExceeded, "Quota exceeded"}, + {ErrorCode::AuthenticationFailed, "Authentication failed"}, + {ErrorCode::KeyNotFound, "Key not found"}, + {ErrorCode::KeyDefaultNotFound, "Key default value not found"}, + {ErrorCode::SerializationFailed, "Serialization failed"}, + {ErrorCode::InvalidSnapshotId, "Invalid snapshot ID"}, + {ErrorCode::ConversionFailed, "Conversion failed"}, + {ErrorCode::MutexLockFailed, "Mutex failed"}, + {ErrorCode::InvalidValueType, "Invalid value type"}, + }; + for (const auto& test : test_cases) { + SCOPED_TRACE(static_cast(test.code)); + score::result::ErrorCode code = static_cast(test.code); + EXPECT_EQ(my_error_domain.MessageFor(code), test.expected_message); + } + + score::result::ErrorCode invalid_code = static_cast(9999); + EXPECT_EQ(my_error_domain.MessageFor(invalid_code), "Unknown Error!"); + +} diff --git a/src/cpp/tests/test_kvs_general.cpp b/src/cpp/tests/test_kvs_general.cpp new file mode 100644 index 00000000..56bb26e4 --- /dev/null +++ b/src/cpp/tests/test_kvs_general.cpp @@ -0,0 +1,67 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "test_kvs_general.hpp" + +/* adler32 control instance */ +uint32_t adler32(const std::string& data) { + const uint32_t mod = 65521; + uint32_t a = 1, b = 0; + for (unsigned char c : data) { + a = (a + c) % mod; + b = (b + a) % mod; + } + return (b << 16) | a; +} + +/* Create Test environment with default data, which is needed in most testcases */ +void prepare_environment(){ + /* Prepare the test environment */ + mkdir(data_dir.c_str(), 0777); + + std::ofstream default_json_file(default_prefix + ".json"); + default_json_file << default_json; + default_json_file.close(); + + std::ofstream kvs_json_file(kvs_prefix + ".json"); + kvs_json_file << kvs_json; + kvs_json_file.close(); + + uint32_t default_hash = adler32(default_json); + uint32_t kvs_hash = adler32(kvs_json); + + std::ofstream default_hash_file(default_prefix + ".hash", std::ios::binary); + default_hash_file.put((default_hash >> 24) & 0xFF); + default_hash_file.put((default_hash >> 16) & 0xFF); + default_hash_file.put((default_hash >> 8) & 0xFF); + default_hash_file.put(default_hash & 0xFF); + default_hash_file.close(); + + std::ofstream kvs_hash_file(kvs_prefix + ".hash", std::ios::binary); + kvs_hash_file.put((kvs_hash >> 24) & 0xFF); + kvs_hash_file.put((kvs_hash >> 16) & 0xFF); + kvs_hash_file.put((kvs_hash >> 8) & 0xFF); + kvs_hash_file.put(kvs_hash & 0xFF); + kvs_hash_file.close(); +} + +void cleanup_environment() { + /* Cleanup the test environment */ + if (std::filesystem::exists(data_dir)) { + for (auto& p : std::filesystem::recursive_directory_iterator(data_dir)) { + std::filesystem::permissions(p, + std::filesystem::perms::owner_all | std::filesystem::perms::group_all | std::filesystem::perms::others_all, + std::filesystem::perm_options::replace); + } + std::filesystem::remove_all(data_dir); + } +} diff --git a/src/cpp/tests/test_kvs_general.hpp b/src/cpp/tests/test_kvs_general.hpp new file mode 100644 index 00000000..def6444c --- /dev/null +++ b/src/cpp/tests/test_kvs_general.hpp @@ -0,0 +1,84 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +//////////////////////////////////////////////////////////////////////////////// +/* The test_kvs_general files provide configuration data and methods needed for KVS tests */ +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Change Private Members and final to public to allow access to member variables (kvs and kvsbuilder) and derive from kvsvalue in unittests*/ +#define private public +#define final +#include "kvsbuilder.hpp" +#undef private +#undef final +#include "internal/kvs_helper.hpp" +#include "score/json/i_json_parser_mock.h" +#include "score/json/i_json_writer_mock.h" +#include "score/filesystem/filesystem_mock.h" +using namespace score::mw::per::kvs; + +//////////////////////////////////////////////////////////////////////////////// + +uint32_t adler32(const std::string& data); +void prepare_environment(); +void cleanup_environment(); + +//////////////////////////////////////////////////////////////////////////////// +/* Default data used in unittests*/ +//////////////////////////////////////////////////////////////////////////////// + +const std::uint32_t instance = 123; +const InstanceId instance_id{instance}; + +/* Notice: score::filesystem::Path could be constructed implicitly from std::string, but for readability, + the explicit construction from those strings are used in the testcode */ +const std::string data_dir = "./data_folder/"; +const std::string default_prefix = data_dir + "kvs_"+std::to_string(instance)+"_default"; +const std::string kvs_prefix = data_dir + "kvs_"+std::to_string(instance)+"_0"; +const std::string filename_prefix = data_dir + "kvs_"+std::to_string(instance); + +const std::string default_json = R"({ + "default": { + "t": "i32", + "v": 5 + } +})"; +const std::string kvs_json = R"({ + "kvs": { + "t": "i32", + "v": 2 + } +})"; + +//////////////////////////////////////////////////////////////////////////////// +/* Mock KvsValue for testing purposes (kvsvalue_to_any) */ +//////////////////////////////////////////////////////////////////////////////// + +class BrokenKvsValue : public KvsValue { +public: + BrokenKvsValue() : KvsValue(nullptr) { + /* Intentionally break the type by assigning an invalid value */ + *(Type*)&this->type = static_cast(999); + } +}; + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/cpp/tests/test_kvs_helper.cpp b/src/cpp/tests/test_kvs_helper.cpp new file mode 100644 index 00000000..4be1df80 --- /dev/null +++ b/src/cpp/tests/test_kvs_helper.cpp @@ -0,0 +1,554 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include "test_kvs_general.hpp" + + +TEST(kvs_calculate_hash_adler32, calculate_hash_adler32) { + /* Test the adler32 hash calculation + Parsing test is done automatically by the open tests */ + + std::string test_data = "Hello, World!"; + uint32_t calculated_hash = adler32(test_data); + EXPECT_EQ(calculated_hash, calculate_hash_adler32(test_data)); + + std::array buf{}; + std::istringstream stream(test_data); + stream.read(reinterpret_cast(buf.data()), buf.size()); + + std::array value = { + uint8_t((calculated_hash >> 24) & 0xFF), + uint8_t((calculated_hash >> 16) & 0xFF), + uint8_t((calculated_hash >> 8) & 0xFF), + uint8_t((calculated_hash ) & 0xFF) + }; + EXPECT_EQ(value, get_hash_bytes(test_data)); + +} + +TEST(kvs_calculate_hash_adler32, calculate_hash_adler32_large_data) { + // Create Teststring with more than 5552 characters to ensure that the hash is calculated correctly + std::string large_data(6000, 'A'); + uint32_t hash = calculate_hash_adler32(large_data); + + EXPECT_EQ(adler32(large_data), hash); +} + +TEST(kvs_check_hash, check_hash_valid) { + + std::string test_data = "Hello, World!"; + uint32_t hash = adler32(test_data); + std::array hash_bytes = { + uint8_t((hash >> 24) & 0xFF), + uint8_t((hash >> 16) & 0xFF), + uint8_t((hash >> 8) & 0xFF), + uint8_t((hash ) & 0xFF) + }; + std::string hash_string(reinterpret_cast(hash_bytes.data()), hash_bytes.size()); + std::istringstream hash_stream(hash_string); + + EXPECT_TRUE(check_hash(test_data, hash_stream)); +} + +TEST(kvs_check_hash, check_hash_invalid) { + + std::string test_data = "Hello, World!"; + uint32_t hash = adler32(test_data); + std::array hash_bytes = { + uint8_t((hash >> 24) & 0xFF), + uint8_t((hash >> 16) & 0xFF), + uint8_t((hash >> 8) & 0xFF), + uint8_t((hash ) & 0xFF) + }; + std::string hash_string(reinterpret_cast(hash_bytes.data()), hash_bytes.size()); + std::istringstream hash_stream(hash_string); + + std::string test_data_invalid = "Hello, invalid World!"; + + EXPECT_FALSE(check_hash(test_data_invalid, hash_stream)); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_bool) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("bool"))); + obj.emplace("v", score::json::Any(true)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::Boolean); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_i32) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("i32"))); + obj.emplace("v", score::json::Any(42.0)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::i32); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_u32) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("u32"))); + obj.emplace("v", score::json::Any(42.0)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::u32); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_i64) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("i64"))); + obj.emplace("v", score::json::Any(42.0)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::i64); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_u64) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("u64"))); + obj.emplace("v", score::json::Any(42.0)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::u64); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_f64) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("f64"))); + obj.emplace("v", score::json::Any(42.0)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::f64); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_string) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("str"))); + obj.emplace("v", score::json::Any(std::string("test"))); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::String); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_null) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("null"))); + obj.emplace("v", score::json::Any(score::json::Null())); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::Null); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_array) { + score::json::List list; + score::json::Object inner_obj_1; + inner_obj_1.emplace("t", score::json::Any(std::string("bool"))); + inner_obj_1.emplace("v", score::json::Any(true)); + list.push_back(score::json::Any(std::move(inner_obj_1))); + + score::json::Object inner_obj_2; + inner_obj_2.emplace("t", score::json::Any(std::string("f64"))); + inner_obj_2.emplace("v", score::json::Any(1.1)); + list.push_back(score::json::Any(std::move(inner_obj_2))); + + score::json::Object inner_obj_3; + inner_obj_3.emplace("t", score::json::Any(std::string("str"))); + inner_obj_3.emplace("v", score::json::Any(std::string("test"))); + list.push_back(score::json::Any(std::move(inner_obj_3))); + + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("arr"))); + obj.emplace("v", score::json::Any(std::move(list))); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::Array); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_object) { + score::json::Object inner_obj_1; + inner_obj_1.emplace("t", score::json::Any(std::string("bool"))); + inner_obj_1.emplace("v", score::json::Any(true)); + + score::json::Object inner_obj_2; + inner_obj_2.emplace("t", score::json::Any(std::string("f64"))); + inner_obj_2.emplace("v", score::json::Any(42.0)); + + score::json::Object combined_obj; + combined_obj.emplace("flag", score::json::Any(std::move(inner_obj_1))); + combined_obj.emplace("count", score::json::Any(std::move(inner_obj_2))); + + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("obj"))); + obj.emplace("v", score::json::Any(std::move(combined_obj))); + + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + ASSERT_TRUE(result); + EXPECT_EQ(result.value().getType(), KvsValue::Type::Object); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_format_invalid) { + score::json::Object obj_type; + obj_type.emplace("invalid", score::json::Any(std::string("bool"))); + obj_type.emplace("v", score::json::Any(true)); + score::json::Any any_obj_type(std::move(obj_type)); + auto result = any_to_kvsvalue(any_obj_type); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); + + score::json::Object obj_value; + obj_value.emplace("t", score::json::Any(std::string("bool"))); + obj_value.emplace("invalid", score::json::Any(true)); + score::json::Any any_obj_value(std::move(obj_value)); + result = any_to_kvsvalue(any_obj_value); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_no_object) { + score::json::Any any_bool(true); + auto result = any_to_kvsvalue(any_bool); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_type_no_string) { + score::json::Object obj; + obj.emplace("t", score::json::Any(42.0)); // Not a string + obj.emplace("v", score::json::Any(true)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_type_invalid) { + score::json::Object obj; + obj.emplace("t", "invalid"); + obj.emplace("v", score::json::Any(true)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_no_string_type) { + score::json::Object obj; + obj.emplace("t", score::json::Any(42.0)); // Not a string + obj.emplace("v", score::json::Any(true)); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_i32){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("i32"))); + obj.emplace("v", score::json::Any("invalid")); // Invalid value for I32 + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_u32){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("u32"))); + obj.emplace("v", score::json::Any("invalid")); // Invalid value for U32 + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_i64){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("i64"))); + obj.emplace("v", score::json::Any("invalid")); // Invalid value for I64 + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_u64){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("u64"))); + obj.emplace("v", score::json::Any("invalid")); // Invalid value for U64 + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_f64){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("f64"))); + obj.emplace("v", score::json::Any("invalid")); // Invalid value for F64 + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_boolean){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("bool"))); + obj.emplace("v", score::json::Any(42.0)); // Invalid value for Boolean + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_string){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("str"))); + obj.emplace("v", score::json::Any(42.0)); // Invalid value for String + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_null){ + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("null"))); + obj.emplace("v", score::json::Any(42.0)); // Invalid value for Null + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_array) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("arr"))); + obj.emplace("v", score::json::Any(42.0)); // Invalid value for Array + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_invalid_object) { + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("obj"))); + obj.emplace("v", score::json::Any(42.0)); // Invalid value for Object + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_array_with_invalid_element) { + score::json::List list; + + score::json::Object inner_obj1; + inner_obj1.emplace("t", score::json::Any(std::string("bool"))); + inner_obj1.emplace("v", score::json::Any(true)); + list.push_back(score::json::Any(std::move(inner_obj1))); + + score::json::Object inner_obj2; + inner_obj2.emplace("t", score::json::Any(std::string("InvalidType"))); + inner_obj2.emplace("v", score::json::Any(std::string("test"))); + list.push_back(score::json::Any(std::move(inner_obj2))); + + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("arr"))); + obj.emplace("v", score::json::Any(std::move(list))); + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_any_to_kvsvalue, any_to_kvsvalue_object_with_invalid_value) { + score::json::Object inner_obj1; + inner_obj1.emplace("t", score::json::Any(std::string("bool"))); + inner_obj1.emplace("v", score::json::Any(true)); + + score::json::Object inner_obj2; + inner_obj2.emplace("t", score::json::Any(std::string("InvalidType"))); + inner_obj2.emplace("v", score::json::Any(42.0)); + + score::json::Object value_obj; + value_obj.emplace("flag", score::json::Any(std::move(inner_obj1))); + value_obj.emplace("count", score::json::Any(std::move(inner_obj2))); + + score::json::Object obj; + obj.emplace("t", score::json::Any(std::string("obj"))); + obj.emplace("v", score::json::Any(std::move(value_obj))); + + score::json::Any any_obj(std::move(obj)); + auto result = any_to_kvsvalue(any_obj); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_null) { + KvsValue null_val(nullptr); + auto result = kvsvalue_to_any(null_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "null"); + EXPECT_TRUE(obj.at("v").As().has_value()); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_boolean) { + KvsValue bool_val(true); + auto result = kvsvalue_to_any(bool_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "bool"); + EXPECT_EQ(obj.at("v").As().value(), true); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_i32) { + KvsValue i32_val(static_cast(42)); + auto result = kvsvalue_to_any(i32_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "i32"); + EXPECT_EQ(obj.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_u32) { + KvsValue u32_val(static_cast(42)); + auto result = kvsvalue_to_any(u32_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "u32"); + EXPECT_EQ(obj.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_i64) { + KvsValue i64_val(static_cast(42)); + auto result = kvsvalue_to_any(i64_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "i64"); + EXPECT_EQ(obj.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_u64) { + KvsValue u64_val(static_cast(42)); + auto result = kvsvalue_to_any(u64_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "u64"); + EXPECT_EQ(obj.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_f64) { + KvsValue f64_val(42.0); + auto result = kvsvalue_to_any(f64_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "f64"); + EXPECT_EQ(obj.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_string) { + std::string str = "test"; + KvsValue string_val(str); + auto result = kvsvalue_to_any(string_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "str"); + EXPECT_EQ(obj.at("v").As().value().get(), "test"); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_array) { + KvsValue::Array array; + array.push_back(KvsValue(true)); + array.push_back(KvsValue(1.1)); + array.push_back(KvsValue(std::string("test"))); + KvsValue array_val(array); + auto result = kvsvalue_to_any(array_val); + ASSERT_TRUE(result); + const auto& obj = result.value().As().value().get(); + EXPECT_EQ(obj.at("t").As().value().get(), "arr"); + const auto& list = obj.at("v").As().value().get(); + EXPECT_EQ(list.size(), 3); + + const auto& elem0 = list[0].As().value().get(); + EXPECT_EQ(elem0.at("t").As().value().get(), "bool"); + EXPECT_EQ(elem0.at("v").As().value(), true); + + const auto& elem1 = list[1].As().value().get(); + EXPECT_EQ(elem1.at("t").As().value().get(), "f64"); + EXPECT_EQ(elem1.at("v").As().value(), 1.1); + + const auto& elem2 = list[2].As().value().get(); + EXPECT_EQ(elem2.at("t").As().value().get(), "str"); + EXPECT_EQ(elem2.at("v").As().value().get(), "test"); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_object) { + KvsValue::Object obj; + obj.emplace("flag", KvsValue(true)); // Boolean + obj.emplace("count", KvsValue(42.0)); // F64 + KvsValue obj_val(obj); + + auto result = kvsvalue_to_any(obj_val); + ASSERT_TRUE(result); + + const auto& outer_obj = result.value().As().value().get(); + EXPECT_EQ(outer_obj.at("t").As().value().get(), "obj"); + + const auto& inner_obj = outer_obj.at("v").As().value().get(); + + const auto& flag_entry = inner_obj.at("flag").As().value().get(); + EXPECT_EQ(flag_entry.at("t").As().value().get(), "bool"); + EXPECT_EQ(flag_entry.at("v").As().value(), true); + + const auto& count_entry = inner_obj.at("count").As().value().get(); + EXPECT_EQ(count_entry.at("t").As().value().get(), "f64"); + EXPECT_EQ(count_entry.at("v").As().value(), 42.0); +} + +TEST(kvs_kvsvalue_to_any, kvsvalue_to_any_invalid) { + /* InvalidType */ + BrokenKvsValue invalid; + auto result = kvsvalue_to_any(invalid); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); + + /* Invalid values in array and object */ + KvsValue::Array array; + array.push_back(KvsValue(42.0)); + array.push_back(invalid); + KvsValue array_invalid(array); + result = kvsvalue_to_any(array_invalid); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); + + KvsValue::Object obj; + obj.emplace("valid", KvsValue(42.0)); + obj.emplace("invalid", invalid); + KvsValue obj_invalid(obj); + result = kvsvalue_to_any(obj_invalid); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ErrorCode::InvalidValueType); +}