diff --git a/openfeature/memory_provider/BUILD b/openfeature/memory_provider/BUILD index 12a5b6f..99dbd0b 100644 --- a/openfeature/memory_provider/BUILD +++ b/openfeature/memory_provider/BUILD @@ -29,6 +29,7 @@ cc_library( "//openfeature:provider_status", "//openfeature:reason", "//openfeature:resolution_details", + "//openfeature:value", "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", ], diff --git a/openfeature/memory_provider/in_memory_provider.cpp b/openfeature/memory_provider/in_memory_provider.cpp index 5c44089..30b1885 100644 --- a/openfeature/memory_provider/in_memory_provider.cpp +++ b/openfeature/memory_provider/in_memory_provider.cpp @@ -55,6 +55,27 @@ std::unique_ptr InMemoryProvider::GetBooleanEvaluation( return Evaluate(key, default_value, ctx); } +std::unique_ptr InMemoryProvider::GetStringEvaluation( + std::string_view key, std::string_view default_value, + const EvaluationContext& ctx) { + return Evaluate(key, std::string(default_value), ctx); +} + +std::unique_ptr InMemoryProvider::GetIntegerEvaluation( + std::string_view key, int64_t default_value, const EvaluationContext& ctx) { + return Evaluate(key, default_value, ctx); +} + +std::unique_ptr InMemoryProvider::GetDoubleEvaluation( + std::string_view key, double default_value, const EvaluationContext& ctx) { + return Evaluate(key, default_value, ctx); +} + +std::unique_ptr InMemoryProvider::GetObjectEvaluation( + std::string_view key, Value default_value, const EvaluationContext& ctx) { + return Evaluate(key, default_value, ctx); +} + template std::unique_ptr> InMemoryProvider::Evaluate( std::string_view key, T default_value, const EvaluationContext& ctx) { diff --git a/openfeature/memory_provider/in_memory_provider.h b/openfeature/memory_provider/in_memory_provider.h index 966827b..e89fc7e 100644 --- a/openfeature/memory_provider/in_memory_provider.h +++ b/openfeature/memory_provider/in_memory_provider.h @@ -14,6 +14,7 @@ #include "openfeature/provider.h" #include "openfeature/provider_status.h" #include "openfeature/resolution_details.h" +#include "openfeature/value.h" namespace openfeature { @@ -45,6 +46,22 @@ class InMemoryProvider : public FeatureProvider { std::string_view key, bool default_value, const EvaluationContext& ctx) override; + std::unique_ptr GetStringEvaluation( + std::string_view key, std::string_view default_value, + const EvaluationContext& ctx) override; + + std::unique_ptr GetIntegerEvaluation( + std::string_view key, int64_t default_value, + const EvaluationContext& ctx) override; + + std::unique_ptr GetDoubleEvaluation( + std::string_view key, double default_value, + const EvaluationContext& ctx) override; + + std::unique_ptr GetObjectEvaluation( + std::string_view key, Value default_value, + const EvaluationContext& ctx) override; + private: template std::unique_ptr> Evaluate(std::string_view key, diff --git a/test/memory_provider/in_memory_provider_test.cpp b/test/memory_provider/in_memory_provider_test.cpp index e4240ab..ed0cf0b 100644 --- a/test/memory_provider/in_memory_provider_test.cpp +++ b/test/memory_provider/in_memory_provider_test.cpp @@ -15,6 +15,7 @@ #include "openfeature/memory_provider/flag.h" #include "openfeature/reason.h" #include "openfeature/resolution_details.h" +#include "openfeature/value.h" namespace openfeature { @@ -25,13 +26,14 @@ class InMemoryProviderTest : public ::testing::Test { protected: EvaluationContext empty_ctx_ = EvaluationContext::Builder().build(); - // Helper method to create a basic boolean flag for testing. - Flag CreateBoolFlag(std::unordered_map variants, - std::optional default_variant_opt, - Flag::ContextEvaluator evaluator = nullptr, - bool disabled = false) { - return Flag(std::move(variants), std::move(default_variant_opt), - std::move(evaluator), FlagMetadata{}, disabled); + // Helper method to create a generic flag for testing. + template + Flag CreateFlag(std::unordered_map variants, + std::optional default_variant_opt, + typename Flag::ContextEvaluator evaluator = nullptr, + bool disabled = false) { + return Flag(std::move(variants), std::move(default_variant_opt), + std::move(evaluator), FlagMetadata{}, disabled); } }; @@ -101,7 +103,7 @@ TEST_F(InMemoryProviderTest, FlagTypeMismatch) { TEST_F(InMemoryProviderTest, DisabledFlagReturnsDisabledReason) { InMemoryProvider provider({}); provider.UpdateFlag("disabled_flag", - CreateBoolFlag({{"on", true}}, "on", nullptr, true)); + CreateFlag({{"on", true}}, "on", nullptr, true)); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); std::unique_ptr res = @@ -115,7 +117,7 @@ TEST_F(InMemoryProviderTest, DisabledFlagReturnsDisabledReason) { TEST_F(InMemoryProviderTest, StaticEvaluationSuccess) { InMemoryProvider provider({}); provider.UpdateFlag("static_flag", - CreateBoolFlag({{"on", true}, {"off", false}}, "on")); + CreateFlag({{"on", true}, {"off", false}}, "on")); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); std::unique_ptr res = @@ -133,8 +135,9 @@ TEST_F(InMemoryProviderTest, ContextEvaluatorSuccess) { }; InMemoryProvider provider({}); - provider.UpdateFlag("dyn_flag", CreateBoolFlag({{"on", true}, {"off", false}}, - "on", evaluator)); + provider.UpdateFlag( + "dyn_flag", + CreateFlag({{"on", true}, {"off", false}}, "on", evaluator)); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); std::unique_ptr res = @@ -151,8 +154,9 @@ TEST_F(InMemoryProviderTest, ContextEvaluatorFailureFallsBackToDefaultVariant) { }; InMemoryProvider provider({}); - provider.UpdateFlag("dyn_flag", CreateBoolFlag({{"on", true}, {"off", false}}, - "off", evaluator)); + provider.UpdateFlag( + "dyn_flag", + CreateFlag({{"on", true}, {"off", false}}, "off", evaluator)); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); std::unique_ptr res = @@ -201,23 +205,23 @@ TEST_F(InMemoryProviderTest, UpdateFlagReplacesAndAddsNew) { InMemoryProvider provider({}); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); - provider.UpdateFlag("flag1", CreateBoolFlag({{"on", true}}, "on")); + provider.UpdateFlag("flag1", CreateFlag({{"on", true}}, "on")); EXPECT_TRUE( provider.GetBooleanEvaluation("flag1", false, empty_ctx_)->GetValue()); - provider.UpdateFlag("flag1", CreateBoolFlag({{"off", false}}, "off")); + provider.UpdateFlag("flag1", CreateFlag({{"off", false}}, "off")); EXPECT_FALSE( provider.GetBooleanEvaluation("flag1", true, empty_ctx_)->GetValue()); - provider.UpdateFlag("new_flag", CreateBoolFlag({{"added", true}}, "added")); + provider.UpdateFlag("new_flag", CreateFlag({{"added", true}}, "added")); EXPECT_TRUE( provider.GetBooleanEvaluation("new_flag", false, empty_ctx_)->GetValue()); } TEST_F(InMemoryProviderTest, UpdateFlagsAddsAndOverwritesExisting) { std::unordered_map initial; - initial["flag1"] = CreateBoolFlag({{"on", true}}, "on"); - initial["common_flag"] = CreateBoolFlag({{"initial", false}}, "initial"); + initial["flag1"] = CreateFlag({{"on", true}}, "on"); + initial["common_flag"] = CreateFlag({{"initial", false}}, "initial"); InMemoryProvider provider(std::move(initial)); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); @@ -227,8 +231,8 @@ TEST_F(InMemoryProviderTest, UpdateFlagsAddsAndOverwritesExisting) { ->GetValue()); std::unordered_map updated; - updated["flag2"] = CreateBoolFlag({{"off", false}}, "off"); - updated["common_flag"] = CreateBoolFlag({{"updated", true}}, "updated"); + updated["flag2"] = CreateFlag({{"off", false}}, "off"); + updated["common_flag"] = CreateFlag({{"updated", true}}, "updated"); provider.UpdateFlags(std::move(updated)); @@ -258,7 +262,7 @@ TEST_F(InMemoryProviderTest, UpdateFlagsAddsAndOverwritesExisting) { TEST_F(InMemoryProviderTest, NoDefaultVariantAndEvaluatorFailsOrMissing) { InMemoryProvider provider1({}); provider1.UpdateFlag("no_default_no_evaluator", - CreateBoolFlag({{"v1", true}}, std::nullopt, nullptr)); + CreateFlag({{"v1", true}}, std::nullopt, nullptr)); EXPECT_TRUE(provider1.Init(empty_ctx_).ok()); std::unique_ptr res1 = provider1.GetBooleanEvaluation( "no_default_no_evaluator", true, empty_ctx_); @@ -273,7 +277,7 @@ TEST_F(InMemoryProviderTest, NoDefaultVariantAndEvaluatorFailsOrMissing) { InMemoryProvider provider2({}); provider2.UpdateFlag( "no_default_failing_evaluator", - CreateBoolFlag({{"v1", true}}, std::nullopt, failing_evaluator)); + CreateFlag({{"v1", true}}, std::nullopt, failing_evaluator)); EXPECT_TRUE(provider2.Init(empty_ctx_).ok()); std::unique_ptr res2 = provider2.GetBooleanEvaluation( "no_default_failing_evaluator", false, empty_ctx_); @@ -300,8 +304,8 @@ TEST_F(InMemoryProviderTest, ContextEvaluatorUsesContext) { InMemoryProvider provider({}); provider.UpdateFlag("admin_flag", - CreateBoolFlag({{"on", true}, {"off", false}}, "off", - context_aware_evaluator)); + CreateFlag({{"on", true}, {"off", false}}, "off", + context_aware_evaluator)); EXPECT_TRUE(provider.Init(empty_ctx_).ok()); EvaluationContext admin_ctx = @@ -342,4 +346,65 @@ TEST_F(InMemoryProviderTest, ContextEvaluatorUsesContext) { EXPECT_THAT(res_wrong_type->GetVariant(), Optional(std::string("off"))); EXPECT_FALSE(res_wrong_type->GetErrorCode().has_value()); } + +TEST_F(InMemoryProviderTest, StringEvaluationSuccess) { + InMemoryProvider provider({}); + provider.UpdateFlag( + "string_flag", + CreateFlag({{"v1", "hello"}, {"v2", "world"}}, "v2")); + EXPECT_TRUE(provider.Init(empty_ctx_).ok()); + + std::unique_ptr res = + provider.GetStringEvaluation("string_flag", "default", empty_ctx_); + + ASSERT_NE(res, nullptr); + EXPECT_EQ(res->GetValue(), "world"); + EXPECT_EQ(res->GetReason(), Reason::kStatic); + EXPECT_THAT(res->GetVariant(), Optional(std::string("v2"))); +} + +TEST_F(InMemoryProviderTest, IntegerEvaluationSuccess) { + InMemoryProvider provider({}); + provider.UpdateFlag("int_flag", + CreateFlag({{"v1", 100}, {"v2", 200}}, "v1")); + EXPECT_TRUE(provider.Init(empty_ctx_).ok()); + + std::unique_ptr res = + provider.GetIntegerEvaluation("int_flag", 0, empty_ctx_); + + ASSERT_NE(res, nullptr); + EXPECT_EQ(res->GetValue(), 100); + EXPECT_EQ(res->GetReason(), Reason::kStatic); + EXPECT_THAT(res->GetVariant(), Optional(std::string("v1"))); +} + +TEST_F(InMemoryProviderTest, DoubleEvaluationSuccess) { + InMemoryProvider provider({}); + provider.UpdateFlag("double_flag", + CreateFlag({{"v1", 3.14}, {"v2", 2.71}}, "v2")); + EXPECT_TRUE(provider.Init(empty_ctx_).ok()); + + std::unique_ptr res = + provider.GetDoubleEvaluation("double_flag", 0.0, empty_ctx_); + + ASSERT_NE(res, nullptr); + EXPECT_DOUBLE_EQ(res->GetValue(), 2.71); + EXPECT_EQ(res->GetReason(), Reason::kStatic); + EXPECT_THAT(res->GetVariant(), Optional(std::string("v2"))); +} + +TEST_F(InMemoryProviderTest, ObjectEvaluationSuccess) { + InMemoryProvider provider({}); + provider.UpdateFlag("object_flag", + CreateFlag({{"v1", Value()}}, "v1")); + EXPECT_TRUE(provider.Init(empty_ctx_).ok()); + + std::unique_ptr res = + provider.GetObjectEvaluation("object_flag", Value(), empty_ctx_); + + ASSERT_NE(res, nullptr); + EXPECT_EQ(res->GetReason(), Reason::kStatic); + EXPECT_THAT(res->GetVariant(), Optional(std::string("v1"))); +} + } // namespace openfeature