diff --git a/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs b/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs index 1af9471..971d3c7 100644 --- a/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs +++ b/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs @@ -214,9 +214,9 @@ private void StatusChangeHandler(object sender, DataSourceStatus status) break; case DataSourceState.Off: default: - // If we had initialized every, then we could still initialize flags, but I think we need to let - // a consumer know we have encountered an unrecoverable problem with the connection. - _statusProvider.SetStatus(ProviderStatus.Fatal, ProviderShutdownMessage); + // The status is an error, and not fatal, because the LaunchDarkly client can continue to + // evaluate flags using the data it already has. + _statusProvider.SetStatus(ProviderStatus.Error, ProviderShutdownMessage); _initCompletion.TrySetException(new LaunchDarklyProviderInitException(ProviderShutdownMessage)); break; } diff --git a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ClientIntegrationTests.cs b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ClientIntegrationTests.cs index fa320b0..fd1ab08 100644 --- a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ClientIntegrationTests.cs +++ b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ClientIntegrationTests.cs @@ -119,6 +119,39 @@ public async Task ItHandlesFailedInitialization() Thread.Sleep(100); Assert.Equal(1, errorCount); } + + [Fact(Timeout = 5000)] + public async Task ItCanEvaluateFlagsAfterTheDataSourceHasBeenShutdown() + { + var mockClient = new Mock(); + mockClient.Setup(l => l.GetLogger()) + .Returns(Components.NoLogging.Build(null).LogAdapter.Logger(null)); + mockClient.Setup(l => l.Initialized).Returns(true); + mockClient.Setup(l => l.BoolVariationDetail("the-flag", It.IsAny(), false)) + .Returns(new Sdk.EvaluationDetail(true, 10, Sdk.EvaluationReason.FallthroughReason)); + + var mockDataSourceStatus = new Mock(); + mockDataSourceStatus.Setup(l => l.Status).Returns(new DataSourceStatus + { + State = DataSourceState.Valid + }); + mockClient.Setup(l => l.DataSourceStatusProvider).Returns(mockDataSourceStatus.Object); + + var mockFlagTracker = new Mock(); + mockClient.Setup(l => l.FlagTracker).Returns(mockFlagTracker.Object); + + var provider = new Provider(mockClient.Object); + await Api.Instance.SetProviderAsync(provider); + + mockDataSourceStatus.Raise(e => e.StatusChanged += null, + mockDataSourceStatus.Object, + new DataSourceStatus { State = DataSourceState.Off }); + Thread.Sleep(100); + + var client = Api.Instance.GetClient(); + Assert.True(await client.GetBooleanValueAsync("the-flag", false, + EvaluationContext.Builder().Set("targetingKey", "the-key").Build())); + } #endif } }