From a47e6f449acd11a973a85b77a8f68556f149ad75 Mon Sep 17 00:00:00 2001 From: NeaguGeorgiana23 Date: Mon, 11 May 2026 07:16:16 +0000 Subject: [PATCH 1/4] Correct logic for evaluation blocked for ERROR and STALE providers Signed-off-by: NeaguGeorgiana23 --- openfeature/client_api.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openfeature/client_api.h b/openfeature/client_api.h index cb4f338..c52944e 100644 --- a/openfeature/client_api.h +++ b/openfeature/client_api.h @@ -113,11 +113,17 @@ template ClientAPI::EvaluateFlag( ValueType default_value, const std::optional& ctx, ProviderCallable provider_call) { - if (GetProviderStatus() != ProviderStatus::kReady) { + ProviderStatus status = GetProviderStatus(); + if (status == ProviderStatus::kNotReady) { return std::make_unique( default_value, Reason::kError, std::nullopt, FlagMetadata(), ErrorCode::kProviderNotReady, "Provider is not ready"); } + if (status == ProviderStatus::kFatal) { + return std::make_unique( + default_value, Reason::kError, std::nullopt, FlagMetadata(), + ErrorCode::kProviderFatal, "Provider is in fatal error state"); + } std::shared_ptr provider = provider_repository_.GetProvider(domain_); From 06c2c12b2c0caee2b21103d0c8743398506768fd Mon Sep 17 00:00:00 2001 From: NeaguGeorgiana23 Date: Mon, 11 May 2026 07:26:13 +0000 Subject: [PATCH 2/4] Fix linter Signed-off-by: NeaguGeorgiana23 --- openfeature/client_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfeature/client_api.h b/openfeature/client_api.h index c52944e..e1d7f29 100644 --- a/openfeature/client_api.h +++ b/openfeature/client_api.h @@ -113,7 +113,7 @@ template ClientAPI::EvaluateFlag( ValueType default_value, const std::optional& ctx, ProviderCallable provider_call) { - ProviderStatus status = GetProviderStatus(); + ProviderStatus status = GetProviderStatus(); if (status == ProviderStatus::kNotReady) { return std::make_unique( default_value, Reason::kError, std::nullopt, FlagMetadata(), From 37b67822daef592883ba813c7686e7ccf2117a6a Mon Sep 17 00:00:00 2001 From: NeaguGeorgiana23 Date: Tue, 23 Jun 2026 11:51:15 +0000 Subject: [PATCH 3/4] add test cases Signed-off-by: NeaguGeorgiana23 --- test/client_api_test.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/client_api_test.cpp b/test/client_api_test.cpp index f718e39..3d27dd8 100644 --- a/test/client_api_test.cpp +++ b/test/client_api_test.cpp @@ -23,6 +23,7 @@ using ::openfeature::ProviderRepository; using ::openfeature::ProviderStatus; using ::openfeature::Reason; using ::openfeature::Value; +using ::openfeature::ErrorCode; using ::testing::_; using ::testing::DoAll; using ::testing::NiceMock; @@ -318,4 +319,65 @@ TEST_F(ClientAPITest, EvaluateFlagHandlesProviderUnknownException) { EXPECT_FALSE(client.GetBooleanValue("flag", false)); EXPECT_TRUE(client.GetBooleanValue("flag", true)); +} + +TEST_F(ClientAPITest, EvaluateFlagBlocksWhenProviderNotReady) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation(_, _, _)).Times(0); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kNotReady); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", true)); + EXPECT_FALSE(client.GetBooleanValue("flag", false)); +} + +TEST_F(ClientAPITest, EvaluateFlagBlocksWhenProviderFatal) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation(_, _, _)).Times(0); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kFatal); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", true)); +} + +TEST_F(ClientAPITest, EvaluateFlagProceedsWhenProviderInErrorState) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation("flag", false, _)) + .WillOnce(Return(std::make_unique( + true, Reason::kCached, std::nullopt, FlagMetadata()))); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kError); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", false)); +} + +TEST_F(ClientAPITest, EvaluateFlagProceedsWhenProviderInStaleState) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation("flag", false, _)) + .WillOnce(Return(std::make_unique( + true, Reason::kCached, std::nullopt, FlagMetadata()))); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kStale); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", false)); } \ No newline at end of file From d41de904917e195450e6edee0a0194fa279a6da9 Mon Sep 17 00:00:00 2001 From: NeaguGeorgiana23 Date: Tue, 23 Jun 2026 11:51:15 +0000 Subject: [PATCH 4/4] add test cases Signed-off-by: NeaguGeorgiana23 --- test/client_api_test.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/client_api_test.cpp b/test/client_api_test.cpp index f718e39..a4673d4 100644 --- a/test/client_api_test.cpp +++ b/test/client_api_test.cpp @@ -14,6 +14,7 @@ using ::openfeature::BoolResolutionDetails; using ::openfeature::ClientAPI; +using ::openfeature::ErrorCode; using ::openfeature::EvaluationContext; using ::openfeature::FlagMetadata; using ::openfeature::GlobalContextManager; @@ -318,4 +319,65 @@ TEST_F(ClientAPITest, EvaluateFlagHandlesProviderUnknownException) { EXPECT_FALSE(client.GetBooleanValue("flag", false)); EXPECT_TRUE(client.GetBooleanValue("flag", true)); +} + +TEST_F(ClientAPITest, EvaluateFlagBlocksWhenProviderNotReady) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation(_, _, _)).Times(0); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kNotReady); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", true)); + EXPECT_FALSE(client.GetBooleanValue("flag", false)); +} + +TEST_F(ClientAPITest, EvaluateFlagBlocksWhenProviderFatal) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation(_, _, _)).Times(0); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kFatal); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", true)); +} + +TEST_F(ClientAPITest, EvaluateFlagProceedsWhenProviderInErrorState) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation("flag", false, _)) + .WillOnce(Return(std::make_unique( + true, Reason::kCached, std::nullopt, FlagMetadata()))); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kError); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", false)); +} + +TEST_F(ClientAPITest, EvaluateFlagProceedsWhenProviderInStaleState) { + auto mock_provider = std::make_shared>(); + EXPECT_CALL(*mock_provider, GetBooleanEvaluation("flag", false, _)) + .WillOnce(Return(std::make_unique( + true, Reason::kCached, std::nullopt, FlagMetadata()))); + repo_.SetProvider("test-domain", mock_provider, + EvaluationContext::Builder().build(), true); + + auto status_manager = repo_.GetFeatureProviderStatusManager("test-domain"); + ASSERT_NE(status_manager, nullptr); + status_manager->SetStatus(ProviderStatus::kStale); + ClientAPI client(repo_, "test-domain"); + + EXPECT_TRUE(client.GetBooleanValue("flag", false)); } \ No newline at end of file