diff --git a/openfeature/client_api.h b/openfeature/client_api.h index ceecaaa..b3c7e34 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_); diff --git a/test/client_api_test.cpp b/test/client_api_test.cpp index f718e39..19f36f9 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)); -} \ No newline at end of file +} + +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)); +}