diff --git a/providers/flagd/src/provider.cpp b/providers/flagd/src/provider.cpp index 05dd084..3b9963b 100644 --- a/providers/flagd/src/provider.cpp +++ b/providers/flagd/src/provider.cpp @@ -27,6 +27,21 @@ FlagdProvider::FlagdProvider(FlagdProviderConfig config) evaluator_ = std::make_unique(sync_); } +FlagdProvider::FlagdProvider(std::shared_ptr sync, + FlagdProviderConfig config) + : configuration_(std::move(config)), + sync_(std::move(sync)), + evaluator_(std::make_unique(sync_)), + is_ready_(false) {} + +FlagdProvider::FlagdProvider(std::shared_ptr sync, + std::unique_ptr evaluator, + FlagdProviderConfig config) + : configuration_(std::move(config)), + sync_(std::move(sync)), + evaluator_(std::move(evaluator)), + is_ready_(false) {} + FlagdProvider::~FlagdProvider() { if (is_ready_) { absl::Status status = FlagdProvider::Shutdown(); diff --git a/providers/flagd/src/provider.h b/providers/flagd/src/provider.h index 63b2579..7c85b9e 100644 --- a/providers/flagd/src/provider.h +++ b/providers/flagd/src/provider.h @@ -16,6 +16,11 @@ namespace flagd { class FlagdProvider : public openfeature::FeatureProvider { public: explicit FlagdProvider(FlagdProviderConfig config = FlagdProviderConfig()); + explicit FlagdProvider(std::shared_ptr sync, + FlagdProviderConfig config = FlagdProviderConfig()); + FlagdProvider(std::shared_ptr sync, + std::unique_ptr evaluator, + FlagdProviderConfig config = FlagdProviderConfig()); ~FlagdProvider() override; diff --git a/providers/flagd/tests/BUILD b/providers/flagd/tests/BUILD new file mode 100644 index 0000000..5c76289 --- /dev/null +++ b/providers/flagd/tests/BUILD @@ -0,0 +1,10 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "provider_test", + srcs = ["provider_test.cpp"], + deps = [ + "//providers/flagd/src:flagd_provider", + "@googletest//:gtest_main", + ], +) diff --git a/providers/flagd/tests/evaluator/BUILD b/providers/flagd/tests/evaluator/BUILD index 2400ae7..6f2ae7a 100644 --- a/providers/flagd/tests/evaluator/BUILD +++ b/providers/flagd/tests/evaluator/BUILD @@ -5,7 +5,7 @@ cc_test( srcs = ["evaluator_test.cpp"], deps = [ "//providers/flagd/src/evaluator", - "//providers/flagd/src/sync:sync", + "//providers/flagd/src/sync", "@googletest//:gtest_main", "@nlohmann_json//:json", ], diff --git a/providers/flagd/tests/provider_test.cpp b/providers/flagd/tests/provider_test.cpp new file mode 100644 index 0000000..f99f19c --- /dev/null +++ b/providers/flagd/tests/provider_test.cpp @@ -0,0 +1,150 @@ +#include "flagd/provider.h" + +#include +#include + +#include + +#include "flagd/evaluator/evaluator.h" +#include "flagd/sync/sync.h" + +namespace flagd { + +class MockSync : public FlagSync { + public: + MOCK_METHOD(absl::Status, Init, (const openfeature::EvaluationContext& ctx), + (override)); + MOCK_METHOD(absl::Status, Shutdown, (), (override)); +}; + +class MockEvaluator : public Evaluator { + public: + MOCK_METHOD(std::unique_ptr, + ResolveBoolean, + (std::string_view flag_key, bool default_value, + const openfeature::EvaluationContext& ctx), + (override)); + MOCK_METHOD(std::unique_ptr, + ResolveString, + (std::string_view flag_key, std::string_view default_value, + const openfeature::EvaluationContext& ctx), + (override)); + MOCK_METHOD(std::unique_ptr, + ResolveInteger, + (std::string_view flag_key, int64_t default_value, + const openfeature::EvaluationContext& ctx), + (override)); + MOCK_METHOD(std::unique_ptr, + ResolveDouble, + (std::string_view flag_key, double default_value, + const openfeature::EvaluationContext& ctx), + (override)); + MOCK_METHOD(std::unique_ptr, + ResolveObject, + (std::string_view flag_key, openfeature::Value default_value, + const openfeature::EvaluationContext& ctx), + (override)); +}; + +TEST(ProviderTest, MetadataReturnsExpectedValues) { + FlagdProvider provider; + openfeature::Metadata metadata = provider.GetMetadata(); + EXPECT_EQ(metadata.name, "flagd"); +} + +TEST(ProviderTest, ReturnsNotReadyBeforeInit) { + auto mock_sync = std::make_shared(); + auto mock_evaluator = std::make_unique(); + + // We don't expect any calls to the evaluator when not ready + EXPECT_CALL(*mock_evaluator, ResolveBoolean).Times(0); + + FlagdProvider provider(mock_sync, std::move(mock_evaluator)); + + std::unique_ptr result = + provider.GetBooleanEvaluation( + "some-flag", false, + openfeature::EvaluationContext::Builder().build()); + + EXPECT_EQ(result->GetErrorCode(), openfeature::ErrorCode::kProviderNotReady); + EXPECT_EQ(result->GetReason(), openfeature::Reason::kError); +} + +TEST(ProviderTest, ReturnsReadyAfterInit) { + auto mock_sync = std::make_shared(); + auto mock_evaluator = std::make_unique(); + + EXPECT_CALL(*mock_sync, Init).WillOnce(testing::Return(absl::OkStatus())); + + EXPECT_CALL(*mock_evaluator, ResolveBoolean) + .WillOnce([](std::string_view flag_key, bool default_value, + const openfeature::EvaluationContext& ctx) { + return std::make_unique( + true, openfeature::Reason::kStatic, "on", + openfeature::FlagMetadata()); + }); + + FlagdProvider provider(mock_sync, std::move(mock_evaluator)); + (void)provider.Init(openfeature::EvaluationContext::Builder().build()); + + std::unique_ptr result = + provider.GetBooleanEvaluation( + "some-flag", false, + openfeature::EvaluationContext::Builder().build()); + + EXPECT_EQ(result->GetValue(), true); + EXPECT_EQ(result->GetReason(), openfeature::Reason::kStatic); +} + +TEST(ProviderTest, DelegationWorks) { + auto mock_sync = std::make_shared(); + auto mock_evaluator = std::make_unique(); + + EXPECT_CALL(*mock_sync, Init).WillOnce(testing::Return(absl::OkStatus())); + + std::string expected_flag = "my-flag"; + bool expected_default = true; + + EXPECT_CALL(*mock_evaluator, + ResolveBoolean(testing::Eq(expected_flag), + testing::Eq(expected_default), testing::_)) + .WillOnce([](std::string_view flag_key, bool default_value, + const openfeature::EvaluationContext& ctx) { + return std::make_unique( + default_value, openfeature::Reason::kDefault, std::nullopt, + openfeature::FlagMetadata()); + }); + + FlagdProvider provider(mock_sync, std::move(mock_evaluator)); + (void)provider.Init(openfeature::EvaluationContext::Builder().build()); + + std::unique_ptr result = + provider.GetBooleanEvaluation( + expected_flag, expected_default, + openfeature::EvaluationContext::Builder().build()); + + EXPECT_EQ(result->GetValue(), expected_default); + EXPECT_EQ(result->GetReason(), openfeature::Reason::kDefault); +} + +TEST(ProviderTest, ShutdownMakesProviderNotReady) { + auto mock_sync = std::make_shared(); + auto mock_evaluator = std::make_unique(); + + EXPECT_CALL(*mock_sync, Init).WillOnce(testing::Return(absl::OkStatus())); + EXPECT_CALL(*mock_sync, Shutdown).WillOnce(testing::Return(absl::OkStatus())); + + FlagdProvider provider(mock_sync, std::move(mock_evaluator)); + (void)provider.Init(openfeature::EvaluationContext::Builder().build()); + (void)provider.Shutdown(); + + std::unique_ptr result = + provider.GetBooleanEvaluation( + "some-flag", false, + openfeature::EvaluationContext::Builder().build()); + + EXPECT_EQ(result->GetErrorCode(), openfeature::ErrorCode::kProviderNotReady); + EXPECT_EQ(result->GetReason(), openfeature::Reason::kError); +} + +} // namespace flagd