diff --git a/README.md b/README.md index 33cf5c5d..b4890714 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,13 @@ dotnet add package OpenFeature public async Task Example() { // Register your feature flag provider - await Api.Instance.SetProvider(new InMemoryProvider()); + await Api.Instance.SetProviderAsync(new InMemoryProvider()); // Create a new client FeatureClient client = Api.Instance.GetClient(); // Evaluate your feature flag - bool v2Enabled = await client.GetBooleanValue("v2_enabled", false); + bool v2Enabled = await client.GetBooleanValueAsync("v2_enabled", false); if ( v2Enabled ) { @@ -112,7 +112,7 @@ If the provider you're looking for hasn't been created yet, see the [develop a p Once you've added a provider as a dependency, it can be registered with OpenFeature like this: ```csharp -await Api.Instance.SetProvider(new MyProvider()); +await Api.Instance.SetProviderAsync(new MyProvider()); ``` In some situations, it may be beneficial to register multiple providers in the same application. @@ -143,7 +143,7 @@ builder = EvaluationContext.Builder(); builder.Set("region", "us-east-1"); EvaluationContext reqCtx = builder.Build(); -bool flagValue = await client.GetBooleanValue("some-flag", false, reqCtx); +bool flagValue = await client.GetBooleanValueAsync("some-flag", false, reqCtx); ``` @@ -164,7 +164,7 @@ var client = Api.Instance.GetClient(); client.AddHooks(new ExampleClientHook()); // add a hook for this evaluation only -var value = await client.GetBooleanValue("boolFlag", false, context, new FlagEvaluationOptions(new ExampleInvocationHook())); +var value = await client.GetBooleanValueAsync("boolFlag", false, context, new FlagEvaluationOptions(new ExampleInvocationHook())); ``` ### Logging @@ -224,7 +224,7 @@ EventHandlerDelegate callback = EventHandler; var myClient = Api.Instance.GetClient("my-client"); var provider = new ExampleProvider(); -await Api.Instance.SetProvider(myClient.GetMetadata().Name, provider); +await Api.Instance.SetProviderAsync(myClient.GetMetadata().Name, provider); myClient.AddHandler(ProviderEventTypes.ProviderReady, callback); ``` @@ -235,7 +235,7 @@ The OpenFeature API provides a close function to perform a cleanup of all regist ```csharp // Shut down all providers -await Api.Instance.Shutdown(); +await Api.Instance.ShutdownAsync(); ``` ## Extending @@ -254,27 +254,27 @@ public class MyProvider : FeatureProvider return new Metadata("My Provider"); } - public override Task> ResolveBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null) + public override Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { // resolve a boolean flag value } - public override Task> ResolveDoubleValue(string flagKey, double defaultValue, EvaluationContext context = null) + public override Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { // resolve a double flag value } - public override Task> ResolveIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null) + public override Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { // resolve an int flag value } - public override Task> ResolveStringValue(string flagKey, string defaultValue, EvaluationContext context = null) + public override Task> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { // resolve a string flag value } - public override Task> ResolveStructureValue(string flagKey, Value defaultValue, EvaluationContext context = null) + public override Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { // resolve an object flag value } @@ -286,30 +286,30 @@ public class MyProvider : FeatureProvider To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/dotnet-sdk-contrib) available under the OpenFeature organization. Implement your own hook by conforming to the `Hook interface`. -To satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined. +To satisfy the interface, all methods (`BeforeAsync`/`AfterAsync`/`FinallyAsync`/`ErrorAsync`) need to be defined. ```csharp public class MyHook : Hook { - public Task Before(HookContext context, - IReadOnlyDictionary hints = null) + public ValueTask BeforeAsync(HookContext context, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { // code to run before flag evaluation } - public virtual Task After(HookContext context, FlagEvaluationDetails details, - IReadOnlyDictionary hints = null) + public virtual ValueTask AfterAsync(HookContext context, FlagEvaluationDetails details, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { // code to run after successful flag evaluation } - public virtual Task Error(HookContext context, Exception error, - IReadOnlyDictionary hints = null) + public virtual ValueTask ErrorAsync(HookContext context, Exception error, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { // code to run if there's an error during before hooks or during flag evaluation } - public virtual Task Finally(HookContext context, IReadOnlyDictionary hints = null) + public virtual ValueTask FinallyAsync(HookContext context, IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { // code to run after all other stages, regardless of success/failure } diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 440242da..b31d6ee7 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -43,10 +43,11 @@ private Api() { } /// /// The provider cannot be set to null. Attempting to set the provider to null has no effect. /// Implementation of - public async Task SetProvider(FeatureProvider featureProvider) + /// The . + public async ValueTask SetProviderAsync(FeatureProvider featureProvider, CancellationToken cancellationToken = default) { this.EventExecutor.RegisterDefaultFeatureProvider(featureProvider); - await this._repository.SetProvider(featureProvider, this.GetContext()).ConfigureAwait(false); + await this._repository.SetProviderAsync(featureProvider, this.GetContext(), cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -56,10 +57,11 @@ public async Task SetProvider(FeatureProvider featureProvider) /// /// Name of client /// Implementation of - public async Task SetProvider(string clientName, FeatureProvider featureProvider) + /// The . + public async ValueTask SetProviderAsync(string clientName, FeatureProvider featureProvider, CancellationToken cancellationToken = default) { this.EventExecutor.RegisterClientFeatureProvider(clientName, featureProvider); - await this._repository.SetProvider(clientName, featureProvider, this.GetContext()).ConfigureAwait(false); + await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -222,10 +224,11 @@ public EvaluationContext GetContext() /// Once shut down is complete, API is reset and ready to use again. /// /// - public async Task Shutdown() + /// The . + public async ValueTask ShutdownAsync(CancellationToken cancellationToken = default) { - await this._repository.Shutdown().ConfigureAwait(false); - await this.EventExecutor.Shutdown().ConfigureAwait(false); + await this._repository.ShutdownAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + await this.EventExecutor.ShutdownAsync(cancellationToken).ConfigureAwait(false); } /// diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index 8a6df9a4..212e0908 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -11,7 +11,7 @@ namespace OpenFeature { - internal delegate Task ShutdownDelegate(); + internal delegate ValueTask ShutdownDelegate(CancellationToken cancellationToken); internal class EventExecutor { @@ -327,9 +327,9 @@ private void InvokeEventHandler(EventHandlerDelegate eventHandler, Event e) } } - public async Task Shutdown() + public async ValueTask ShutdownAsync(CancellationToken cancellationToken = default) { - await this._shutdownDelegate().ConfigureAwait(false); + await this._shutdownDelegate(cancellationToken).ConfigureAwait(false); } internal void SetShutdownDelegate(ShutdownDelegate del) @@ -338,13 +338,13 @@ internal void SetShutdownDelegate(ShutdownDelegate del) } // Method to signal shutdown - private async Task SignalShutdownAsync() + private async ValueTask SignalShutdownAsync(CancellationToken cancellationToken) { // Enqueue a shutdown signal - await this.EventChannel.Writer.WriteAsync(new ShutdownSignal()).ConfigureAwait(false); + await this.EventChannel.Writer.WriteAsync(new ShutdownSignal(), cancellationToken).ConfigureAwait(false); // Wait for the processing loop to acknowledge the shutdown - await this._shutdownSemaphore.WaitAsync().ConfigureAwait(false); + await this._shutdownSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/src/OpenFeature/FeatureProvider.cs b/src/OpenFeature/FeatureProvider.cs index 000ab0fb..3453c8a3 100644 --- a/src/OpenFeature/FeatureProvider.cs +++ b/src/OpenFeature/FeatureProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using OpenFeature.Constant; @@ -43,9 +44,10 @@ public abstract class FeatureProvider /// Feature flag key /// Default value /// + /// The . /// - public abstract Task> ResolveBooleanValue(string flagKey, bool defaultValue, - EvaluationContext context = null); + public abstract Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default); /// /// Resolves a string feature flag @@ -53,9 +55,10 @@ public abstract Task> ResolveBooleanValue(string flagKey /// Feature flag key /// Default value /// + /// The . /// - public abstract Task> ResolveStringValue(string flagKey, string defaultValue, - EvaluationContext context = null); + public abstract Task> ResolveStringValueAsync(string flagKey, string defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default); /// /// Resolves a integer feature flag @@ -63,9 +66,10 @@ public abstract Task> ResolveStringValue(string flagKe /// Feature flag key /// Default value /// + /// The . /// - public abstract Task> ResolveIntegerValue(string flagKey, int defaultValue, - EvaluationContext context = null); + public abstract Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default); /// /// Resolves a double feature flag @@ -73,9 +77,10 @@ public abstract Task> ResolveIntegerValue(string flagKey, /// Feature flag key /// Default value /// + /// The . /// - public abstract Task> ResolveDoubleValue(string flagKey, double defaultValue, - EvaluationContext context = null); + public abstract Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default); /// /// Resolves a structured feature flag @@ -83,9 +88,10 @@ public abstract Task> ResolveDoubleValue(string flagKe /// Feature flag key /// Default value /// + /// The . /// - public abstract Task> ResolveStructureValue(string flagKey, Value defaultValue, - EvaluationContext context = null); + public abstract Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default); /// /// Get the status of the provider. @@ -95,7 +101,7 @@ public abstract Task> ResolveStructureValue(string flag /// If a provider does not override this method, then its status will be assumed to be /// . If a provider implements this method, and supports initialization, /// then it should start in the status . If the status is - /// , then the Api will call the when the + /// , then the Api will call the when the /// provider is set. /// public virtual ProviderStatus GetStatus() => ProviderStatus.Ready; @@ -107,6 +113,7 @@ public abstract Task> ResolveStructureValue(string flag /// /// /// + /// The . /// A task that completes when the initialization process is complete. /// /// @@ -118,10 +125,10 @@ public abstract Task> ResolveStructureValue(string flag /// the method after initialization is complete. /// /// - public virtual Task Initialize(EvaluationContext context) + public virtual ValueTask InitializeAsync(EvaluationContext context, CancellationToken cancellationToken = default) { // Intentionally left blank. - return Task.CompletedTask; + return new ValueTask(); } /// @@ -129,10 +136,11 @@ public virtual Task Initialize(EvaluationContext context) /// Providers can overwrite this method, if they have special shutdown actions needed. /// /// A task that completes when the shutdown process is complete. - public virtual Task Shutdown() + /// The . + public virtual ValueTask ShutdownAsync(CancellationToken cancellationToken = default) { // Intentionally left blank. - return Task.CompletedTask; + return new ValueTask(); } /// diff --git a/src/OpenFeature/Hook.cs b/src/OpenFeature/Hook.cs index c35c3cb4..abf522ee 100644 --- a/src/OpenFeature/Hook.cs +++ b/src/OpenFeature/Hook.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using OpenFeature.Model; @@ -26,12 +27,13 @@ public abstract class Hook /// /// Provides context of innovation /// Caller provided data + /// The . /// Flag value type (bool|number|string|object) /// Modified EvaluationContext that is used for the flag evaluation - public virtual Task Before(HookContext context, - IReadOnlyDictionary hints = null) + public virtual ValueTask BeforeAsync(HookContext context, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.FromResult(EvaluationContext.Empty); + return new ValueTask(EvaluationContext.Empty); } /// @@ -40,11 +42,12 @@ public virtual Task Before(HookContext context, /// Provides context of innovation /// Flag evaluation information /// Caller provided data + /// The . /// Flag value type (bool|number|string|object) - public virtual Task After(HookContext context, FlagEvaluationDetails details, - IReadOnlyDictionary hints = null) + public virtual ValueTask AfterAsync(HookContext context, FlagEvaluationDetails details, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } /// @@ -53,11 +56,12 @@ public virtual Task After(HookContext context, FlagEvaluationDetails de /// Provides context of innovation /// Exception representing what went wrong /// Caller provided data + /// The . /// Flag value type (bool|number|string|object) - public virtual Task Error(HookContext context, Exception error, - IReadOnlyDictionary hints = null) + public virtual ValueTask ErrorAsync(HookContext context, Exception error, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } /// @@ -65,10 +69,11 @@ public virtual Task Error(HookContext context, Exception error, /// /// Provides context of innovation /// Caller provided data + /// The . /// Flag value type (bool|number|string|object) - public virtual Task Finally(HookContext context, IReadOnlyDictionary hints = null) + public virtual ValueTask FinallyAsync(HookContext context, IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } } } diff --git a/src/OpenFeature/IFeatureClient.cs b/src/OpenFeature/IFeatureClient.cs index 1d2e6dfb..7391dc9d 100644 --- a/src/OpenFeature/IFeatureClient.cs +++ b/src/OpenFeature/IFeatureClient.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; -using OpenFeature.Constant; using OpenFeature.Model; namespace OpenFeature @@ -60,8 +60,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag value. - Task GetBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task GetBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a boolean feature flag @@ -70,8 +71,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag details - Task> GetBooleanDetails(string flagKey, bool defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task> GetBooleanDetailsAsync(string flagKey, bool defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a string feature flag @@ -80,8 +82,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag value. - Task GetStringValue(string flagKey, string defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task GetStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a string feature flag @@ -90,8 +93,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag details - Task> GetStringDetails(string flagKey, string defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task> GetStringDetailsAsync(string flagKey, string defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a integer feature flag @@ -100,8 +104,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag value. - Task GetIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task GetIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a integer feature flag @@ -110,8 +115,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag details - Task> GetIntegerDetails(string flagKey, int defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task> GetIntegerDetailsAsync(string flagKey, int defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a double feature flag @@ -120,8 +126,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag value. - Task GetDoubleValue(string flagKey, double defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task GetDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a double feature flag @@ -130,8 +137,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag details - Task> GetDoubleDetails(string flagKey, double defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task> GetDoubleDetailsAsync(string flagKey, double defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a structure object feature flag @@ -140,8 +148,9 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag value. - Task GetObjectValue(string flagKey, Value defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task GetObjectValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); /// /// Resolves a structure object feature flag @@ -150,7 +159,8 @@ public interface IFeatureClient : IEventBus /// Default value /// Evaluation Context /// Flag Evaluation Options + /// The . /// Resolved flag details - Task> GetObjectDetails(string flagKey, Value defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null); + Task> GetObjectDetailsAsync(string flagKey, Value defaultValue, EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default); } } diff --git a/src/OpenFeature/NoOpProvider.cs b/src/OpenFeature/NoOpProvider.cs index 16ba38f4..fe67a601 100644 --- a/src/OpenFeature/NoOpProvider.cs +++ b/src/OpenFeature/NoOpProvider.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using OpenFeature.Constant; using OpenFeature.Model; @@ -13,27 +14,27 @@ public override Metadata GetMetadata() return this._metadata; } - public override Task> ResolveBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null) + public override Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(NoOpResponse(flagKey, defaultValue)); } - public override Task> ResolveStringValue(string flagKey, string defaultValue, EvaluationContext context = null) + public override Task> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(NoOpResponse(flagKey, defaultValue)); } - public override Task> ResolveIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null) + public override Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(NoOpResponse(flagKey, defaultValue)); } - public override Task> ResolveDoubleValue(string flagKey, double defaultValue, EvaluationContext context = null) + public override Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(NoOpResponse(flagKey, defaultValue)); } - public override Task> ResolveStructureValue(string flagKey, Value defaultValue, EvaluationContext context = null) + public override Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(NoOpResponse(flagKey, defaultValue)); } diff --git a/src/OpenFeature/OpenFeatureClient.cs b/src/OpenFeature/OpenFeatureClient.cs index d979dae1..e32b53b0 100644 --- a/src/OpenFeature/OpenFeatureClient.cs +++ b/src/OpenFeature/OpenFeatureClient.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -36,9 +37,9 @@ public sealed class FeatureClient : IFeatureClient /// /// The type of the resolution method /// A tuple containing a resolution method and the provider it came from. - private (Func>>, FeatureProvider) + private (Func>>, FeatureProvider) ExtractProvider( - Func>>> method) + Func>>> method) { // Alias the provider reference so getting the method and returning the provider are // guaranteed to be the same object. @@ -136,70 +137,71 @@ public void AddHooks(IEnumerable hooks) public void ClearHooks() => this._hooks.Clear(); /// - public async Task GetBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null, - FlagEvaluationOptions config = null) => - (await this.GetBooleanDetails(flagKey, defaultValue, context, config).ConfigureAwait(false)).Value; + public async Task GetBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, + FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + (await this.GetBooleanDetailsAsync(flagKey, defaultValue, context, config, cancellationToken).ConfigureAwait(false)).Value; /// - public async Task> GetBooleanDetails(string flagKey, bool defaultValue, - EvaluationContext context = null, FlagEvaluationOptions config = null) => - await this.EvaluateFlag(this.ExtractProvider(provider => provider.ResolveBooleanValue), + public async Task> GetBooleanDetailsAsync(string flagKey, bool defaultValue, + EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + await this.EvaluateFlagAsync(this.ExtractProvider(provider => provider.ResolveBooleanValueAsync), FlagValueType.Boolean, flagKey, - defaultValue, context, config).ConfigureAwait(false); + defaultValue, context, config, cancellationToken).ConfigureAwait(false); /// - public async Task GetStringValue(string flagKey, string defaultValue, EvaluationContext context = null, - FlagEvaluationOptions config = null) => - (await this.GetStringDetails(flagKey, defaultValue, context, config).ConfigureAwait(false)).Value; + public async Task GetStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, + FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + (await this.GetStringDetailsAsync(flagKey, defaultValue, context, config, cancellationToken).ConfigureAwait(false)).Value; /// - public async Task> GetStringDetails(string flagKey, string defaultValue, - EvaluationContext context = null, FlagEvaluationOptions config = null) => - await this.EvaluateFlag(this.ExtractProvider(provider => provider.ResolveStringValue), + public async Task> GetStringDetailsAsync(string flagKey, string defaultValue, + EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + await this.EvaluateFlagAsync(this.ExtractProvider(provider => provider.ResolveStringValueAsync), FlagValueType.String, flagKey, - defaultValue, context, config).ConfigureAwait(false); + defaultValue, context, config, cancellationToken).ConfigureAwait(false); /// - public async Task GetIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null, - FlagEvaluationOptions config = null) => - (await this.GetIntegerDetails(flagKey, defaultValue, context, config).ConfigureAwait(false)).Value; + public async Task GetIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, + FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + (await this.GetIntegerDetailsAsync(flagKey, defaultValue, context, config, cancellationToken).ConfigureAwait(false)).Value; /// - public async Task> GetIntegerDetails(string flagKey, int defaultValue, - EvaluationContext context = null, FlagEvaluationOptions config = null) => - await this.EvaluateFlag(this.ExtractProvider(provider => provider.ResolveIntegerValue), + public async Task> GetIntegerDetailsAsync(string flagKey, int defaultValue, + EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + await this.EvaluateFlagAsync(this.ExtractProvider(provider => provider.ResolveIntegerValueAsync), FlagValueType.Number, flagKey, - defaultValue, context, config).ConfigureAwait(false); + defaultValue, context, config, cancellationToken).ConfigureAwait(false); /// - public async Task GetDoubleValue(string flagKey, double defaultValue, + public async Task GetDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, - FlagEvaluationOptions config = null) => - (await this.GetDoubleDetails(flagKey, defaultValue, context, config).ConfigureAwait(false)).Value; + FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + (await this.GetDoubleDetailsAsync(flagKey, defaultValue, context, config, cancellationToken).ConfigureAwait(false)).Value; /// - public async Task> GetDoubleDetails(string flagKey, double defaultValue, - EvaluationContext context = null, FlagEvaluationOptions config = null) => - await this.EvaluateFlag(this.ExtractProvider(provider => provider.ResolveDoubleValue), + public async Task> GetDoubleDetailsAsync(string flagKey, double defaultValue, + EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + await this.EvaluateFlagAsync(this.ExtractProvider(provider => provider.ResolveDoubleValueAsync), FlagValueType.Number, flagKey, - defaultValue, context, config).ConfigureAwait(false); + defaultValue, context, config, cancellationToken).ConfigureAwait(false); /// - public async Task GetObjectValue(string flagKey, Value defaultValue, EvaluationContext context = null, - FlagEvaluationOptions config = null) => - (await this.GetObjectDetails(flagKey, defaultValue, context, config).ConfigureAwait(false)).Value; + public async Task GetObjectValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, + FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + (await this.GetObjectDetailsAsync(flagKey, defaultValue, context, config, cancellationToken).ConfigureAwait(false)).Value; /// - public async Task> GetObjectDetails(string flagKey, Value defaultValue, - EvaluationContext context = null, FlagEvaluationOptions config = null) => - await this.EvaluateFlag(this.ExtractProvider(provider => provider.ResolveStructureValue), + public async Task> GetObjectDetailsAsync(string flagKey, Value defaultValue, + EvaluationContext context = null, FlagEvaluationOptions config = null, CancellationToken cancellationToken = default) => + await this.EvaluateFlagAsync(this.ExtractProvider(provider => provider.ResolveStructureValueAsync), FlagValueType.Object, flagKey, - defaultValue, context, config).ConfigureAwait(false); + defaultValue, context, config, cancellationToken).ConfigureAwait(false); - private async Task> EvaluateFlag( - (Func>>, FeatureProvider) providerInfo, + private async Task> EvaluateFlagAsync( + (Func>>, FeatureProvider) providerInfo, FlagValueType flagValueType, string flagKey, T defaultValue, EvaluationContext context = null, - FlagEvaluationOptions options = null) + FlagEvaluationOptions options = null, + CancellationToken cancellationToken = default) { var resolveValueDelegate = providerInfo.Item1; var provider = providerInfo.Item2; @@ -242,13 +244,13 @@ private async Task> EvaluateFlag( FlagEvaluationDetails evaluation; try { - var contextFromHooks = await this.TriggerBeforeHooks(allHooks, hookContext, options).ConfigureAwait(false); + var contextFromHooks = await this.TriggerBeforeHooksAsync(allHooks, hookContext, options, cancellationToken).ConfigureAwait(false); evaluation = - (await resolveValueDelegate.Invoke(flagKey, defaultValue, contextFromHooks.EvaluationContext).ConfigureAwait(false)) + (await resolveValueDelegate.Invoke(flagKey, defaultValue, contextFromHooks.EvaluationContext, cancellationToken).ConfigureAwait(false)) .ToFlagEvaluationDetails(); - await this.TriggerAfterHooks(allHooksReversed, hookContext, evaluation, options).ConfigureAwait(false); + await this.TriggerAfterHooksAsync(allHooksReversed, hookContext, evaluation, options, cancellationToken).ConfigureAwait(false); } catch (FeatureProviderException ex) { @@ -256,32 +258,32 @@ private async Task> EvaluateFlag( ex.ErrorType.GetDescription()); evaluation = new FlagEvaluationDetails(flagKey, defaultValue, ex.ErrorType, Reason.Error, string.Empty, ex.Message); - await this.TriggerErrorHooks(allHooksReversed, hookContext, ex, options).ConfigureAwait(false); + await this.TriggerErrorHooksAsync(allHooksReversed, hookContext, ex, options, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { this._logger.LogError(ex, "Error while evaluating flag {FlagKey}", flagKey); var errorCode = ex is InvalidCastException ? ErrorType.TypeMismatch : ErrorType.General; evaluation = new FlagEvaluationDetails(flagKey, defaultValue, errorCode, Reason.Error, string.Empty); - await this.TriggerErrorHooks(allHooksReversed, hookContext, ex, options).ConfigureAwait(false); + await this.TriggerErrorHooksAsync(allHooksReversed, hookContext, ex, options, cancellationToken).ConfigureAwait(false); } finally { - await this.TriggerFinallyHooks(allHooksReversed, hookContext, options).ConfigureAwait(false); + await this.TriggerFinallyHooksAsync(allHooksReversed, hookContext, options, cancellationToken).ConfigureAwait(false); } return evaluation; } - private async Task> TriggerBeforeHooks(IReadOnlyList hooks, HookContext context, - FlagEvaluationOptions options) + private async ValueTask> TriggerBeforeHooksAsync(IReadOnlyList hooks, HookContext context, + FlagEvaluationOptions options, CancellationToken cancellationToken = default) { var evalContextBuilder = EvaluationContext.Builder(); evalContextBuilder.Merge(context.EvaluationContext); foreach (var hook in hooks) { - var resp = await hook.Before(context, options?.HookHints).ConfigureAwait(false); + var resp = await hook.BeforeAsync(context, options?.HookHints, cancellationToken).ConfigureAwait(false); if (resp != null) { evalContextBuilder.Merge(resp); @@ -297,23 +299,23 @@ private async Task> TriggerBeforeHooks(IReadOnlyList hoo return context.WithNewEvaluationContext(evalContextBuilder.Build()); } - private async Task TriggerAfterHooks(IReadOnlyList hooks, HookContext context, - FlagEvaluationDetails evaluationDetails, FlagEvaluationOptions options) + private async ValueTask TriggerAfterHooksAsync(IReadOnlyList hooks, HookContext context, + FlagEvaluationDetails evaluationDetails, FlagEvaluationOptions options, CancellationToken cancellationToken = default) { foreach (var hook in hooks) { - await hook.After(context, evaluationDetails, options?.HookHints).ConfigureAwait(false); + await hook.AfterAsync(context, evaluationDetails, options?.HookHints, cancellationToken).ConfigureAwait(false); } } - private async Task TriggerErrorHooks(IReadOnlyList hooks, HookContext context, Exception exception, - FlagEvaluationOptions options) + private async ValueTask TriggerErrorHooksAsync(IReadOnlyList hooks, HookContext context, Exception exception, + FlagEvaluationOptions options, CancellationToken cancellationToken = default) { foreach (var hook in hooks) { try { - await hook.Error(context, exception, options?.HookHints).ConfigureAwait(false); + await hook.ErrorAsync(context, exception, options?.HookHints, cancellationToken).ConfigureAwait(false); } catch (Exception e) { @@ -322,14 +324,14 @@ private async Task TriggerErrorHooks(IReadOnlyList hooks, HookContext(IReadOnlyList hooks, HookContext context, - FlagEvaluationOptions options) + private async ValueTask TriggerFinallyHooksAsync(IReadOnlyList hooks, HookContext context, + FlagEvaluationOptions options, CancellationToken cancellationToken = default) { foreach (var hook in hooks) { try { - await hook.Finally(context, options?.HookHints).ConfigureAwait(false); + await hook.FinallyAsync(context, options?.HookHints, cancellationToken).ConfigureAwait(false); } catch (Exception e) { diff --git a/src/OpenFeature/ProviderRepository.cs b/src/OpenFeature/ProviderRepository.cs index ab2bdb30..fa940bc8 100644 --- a/src/OpenFeature/ProviderRepository.cs +++ b/src/OpenFeature/ProviderRepository.cs @@ -54,13 +54,15 @@ internal sealed class ProviderRepository /// initialization /// /// called after a provider is shutdown, can be used to remove event handlers - public async Task SetProvider( + /// The . + public async ValueTask SetProviderAsync( FeatureProvider featureProvider, EvaluationContext context, Action afterSet = null, Action afterInitialization = null, Action afterError = null, - Action afterShutdown = null) + Action afterShutdown = null, + CancellationToken cancellationToken = default) { // Cannot unset the feature provider. if (featureProvider == null) @@ -84,7 +86,7 @@ public async Task SetProvider( // We want to allow shutdown to happen concurrently with initialization, and the caller to not // wait for it. #pragma warning disable CS4014 - this.ShutdownIfUnused(oldProvider, afterShutdown, afterError); + this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError, cancellationToken); #pragma warning restore CS4014 } finally @@ -92,15 +94,16 @@ public async Task SetProvider( this._providersLock.ExitWriteLock(); } - await InitProvider(this._defaultProvider, context, afterInitialization, afterError) + await InitProviderAsync(this._defaultProvider, context, afterInitialization, afterError, cancellationToken) .ConfigureAwait(false); } - private static async Task InitProvider( + private static async ValueTask InitProviderAsync( FeatureProvider newProvider, EvaluationContext context, Action afterInitialization, - Action afterError) + Action afterError, + CancellationToken cancellationToken) { if (newProvider == null) { @@ -110,7 +113,7 @@ private static async Task InitProvider( { try { - await newProvider.Initialize(context).ConfigureAwait(false); + await newProvider.InitializeAsync(context, cancellationToken).ConfigureAwait(false); afterInitialization?.Invoke(newProvider); } catch (Exception ex) @@ -144,13 +147,15 @@ private static async Task InitProvider( /// initialization /// /// called after a provider is shutdown, can be used to remove event handlers - public async Task SetProvider(string clientName, + /// The . + public async ValueTask SetProviderAsync(string clientName, FeatureProvider featureProvider, EvaluationContext context, Action afterSet = null, Action afterInitialization = null, Action afterError = null, - Action afterShutdown = null) + Action afterShutdown = null, + CancellationToken cancellationToken = default) { // Cannot set a provider for a null clientName. if (clientName == null) @@ -179,7 +184,7 @@ public async Task SetProvider(string clientName, // We want to allow shutdown to happen concurrently with initialization, and the caller to not // wait for it. #pragma warning disable CS4014 - this.ShutdownIfUnused(oldProvider, afterShutdown, afterError); + this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError, cancellationToken); #pragma warning restore CS4014 } finally @@ -187,16 +192,17 @@ public async Task SetProvider(string clientName, this._providersLock.ExitWriteLock(); } - await InitProvider(featureProvider, context, afterInitialization, afterError).ConfigureAwait(false); + await InitProviderAsync(featureProvider, context, afterInitialization, afterError, cancellationToken).ConfigureAwait(false); } /// /// Shutdown the feature provider if it is unused. This must be called within a write lock of the _providersLock. /// - private async Task ShutdownIfUnused( + private async ValueTask ShutdownIfUnusedAsync( FeatureProvider targetProvider, Action afterShutdown, - Action afterError) + Action afterError, + CancellationToken cancellationToken) { if (ReferenceEquals(this._defaultProvider, targetProvider)) { @@ -208,7 +214,7 @@ private async Task ShutdownIfUnused( return; } - await SafeShutdownProvider(targetProvider, afterShutdown, afterError).ConfigureAwait(false); + await SafeShutdownProviderAsync(targetProvider, afterShutdown, afterError, cancellationToken).ConfigureAwait(false); } /// @@ -220,13 +226,14 @@ private async Task ShutdownIfUnused( /// it would not be meaningful to emit an error. /// /// - private static async Task SafeShutdownProvider(FeatureProvider targetProvider, + private static async ValueTask SafeShutdownProviderAsync(FeatureProvider targetProvider, Action afterShutdown, - Action afterError) + Action afterError, + CancellationToken cancellationToken) { try { - await targetProvider.Shutdown().ConfigureAwait(false); + await targetProvider.ShutdownAsync(cancellationToken).ConfigureAwait(false); afterShutdown?.Invoke(targetProvider); } catch (Exception ex) @@ -260,7 +267,7 @@ public FeatureProvider GetProvider(string clientName) : this.GetProvider(); } - public async Task Shutdown(Action afterError = null) + public async ValueTask ShutdownAsync(Action afterError = null, CancellationToken cancellationToken = default) { var providers = new HashSet(); this._providersLock.EnterWriteLock(); @@ -284,7 +291,7 @@ public async Task Shutdown(Action afterError = null) foreach (var targetProvider in providers) { // We don't need to take any actions after shutdown. - await SafeShutdownProvider(targetProvider, null, afterError).ConfigureAwait(false); + await SafeShutdownProviderAsync(targetProvider, null, afterError, cancellationToken).ConfigureAwait(false); } } } diff --git a/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs b/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs index 03f6082a..7f2e5b30 100644 --- a/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs +++ b/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs @@ -45,62 +45,62 @@ public OpenFeatureClientBenchmarks() [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetBooleanValue(_flagName, _defaultBoolValue); + await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue); [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetBooleanValue(_flagName, _defaultBoolValue, EvaluationContext.Empty); + await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetBooleanValue(_flagName, _defaultBoolValue, EvaluationContext.Empty, _emptyFlagOptions); + await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue, EvaluationContext.Empty, _emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetStringValue(_flagName, _defaultStringValue); + await _client.GetStringValueAsync(_flagName, _defaultStringValue); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetStringValue(_flagName, _defaultStringValue, EvaluationContext.Empty); + await _client.GetStringValueAsync(_flagName, _defaultStringValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithoutEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetStringValue(_flagName, _defaultStringValue, EvaluationContext.Empty, _emptyFlagOptions); + await _client.GetStringValueAsync(_flagName, _defaultStringValue, EvaluationContext.Empty, _emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetIntegerValue(_flagName, _defaultIntegerValue); + await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetIntegerValue(_flagName, _defaultIntegerValue, EvaluationContext.Empty); + await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetIntegerValue(_flagName, _defaultIntegerValue, EvaluationContext.Empty, _emptyFlagOptions); + await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue, EvaluationContext.Empty, _emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetDoubleValue(_flagName, _defaultDoubleValue); + await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetDoubleValue(_flagName, _defaultDoubleValue, EvaluationContext.Empty); + await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetDoubleValue(_flagName, _defaultDoubleValue, EvaluationContext.Empty, _emptyFlagOptions); + await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue, EvaluationContext.Empty, _emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetObjectValue(_flagName, _defaultStructureValue); + await _client.GetObjectValueAsync(_flagName, _defaultStructureValue); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetObjectValue(_flagName, _defaultStructureValue, EvaluationContext.Empty); + await _client.GetObjectValueAsync(_flagName, _defaultStructureValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetObjectValue(_flagName, _defaultStructureValue, EvaluationContext.Empty, _emptyFlagOptions); + await _client.GetObjectValueAsync(_flagName, _defaultStructureValue, EvaluationContext.Empty, _emptyFlagOptions); } } diff --git a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs index 4847bfb2..1061a7dc 100644 --- a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs +++ b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs @@ -42,7 +42,7 @@ public EvaluationStepDefinitions(ScenarioContext scenarioContext) { _scenarioContext = scenarioContext; var flagdProvider = new FlagdProvider(); - Api.Instance.SetProvider(flagdProvider).Wait(); + Api.Instance.SetProviderAsync(flagdProvider).GetAwaiter().GetResult(); client = Api.Instance.GetClient(); } @@ -55,7 +55,7 @@ public void Givenaproviderisregisteredwithcachedisabled() [When(@"a boolean flag with key ""(.*)"" is evaluated with default value ""(.*)""")] public void Whenabooleanflagwithkeyisevaluatedwithdefaultvalue(string flagKey, bool defaultValue) { - this.booleanFlagValue = client.GetBooleanValue(flagKey, defaultValue); + this.booleanFlagValue = client.GetBooleanValueAsync(flagKey, defaultValue); } [Then(@"the resolved boolean value should be ""(.*)""")] @@ -67,7 +67,7 @@ public void Thentheresolvedbooleanvalueshouldbe(bool expectedValue) [When(@"a string flag with key ""(.*)"" is evaluated with default value ""(.*)""")] public void Whenastringflagwithkeyisevaluatedwithdefaultvalue(string flagKey, string defaultValue) { - this.stringFlagValue = client.GetStringValue(flagKey, defaultValue); + this.stringFlagValue = client.GetStringValueAsync(flagKey, defaultValue); } [Then(@"the resolved string value should be ""(.*)""")] @@ -79,7 +79,7 @@ public void Thentheresolvedstringvalueshouldbe(string expected) [When(@"an integer flag with key ""(.*)"" is evaluated with default value (.*)")] public void Whenanintegerflagwithkeyisevaluatedwithdefaultvalue(string flagKey, int defaultValue) { - this.intFlagValue = client.GetIntegerValue(flagKey, defaultValue); + this.intFlagValue = client.GetIntegerValueAsync(flagKey, defaultValue); } [Then(@"the resolved integer value should be (.*)")] @@ -91,7 +91,7 @@ public void Thentheresolvedintegervalueshouldbe(int expected) [When(@"a float flag with key ""(.*)"" is evaluated with default value (.*)")] public void Whenafloatflagwithkeyisevaluatedwithdefaultvalue(string flagKey, double defaultValue) { - this.doubleFlagValue = client.GetDoubleValue(flagKey, defaultValue); + this.doubleFlagValue = client.GetDoubleValueAsync(flagKey, defaultValue); } [Then(@"the resolved float value should be (.*)")] @@ -103,7 +103,7 @@ public void Thentheresolvedfloatvalueshouldbe(double expected) [When(@"an object flag with key ""(.*)"" is evaluated with a null default value")] public void Whenanobjectflagwithkeyisevaluatedwithanulldefaultvalue(string flagKey) { - this.objectFlagValue = client.GetObjectValue(flagKey, new Value()); + this.objectFlagValue = client.GetObjectValueAsync(flagKey, new Value()); } [Then(@"the resolved object value should be contain fields ""(.*)"", ""(.*)"", and ""(.*)"", with values ""(.*)"", ""(.*)"" and (.*), respectively")] @@ -118,7 +118,7 @@ public void Thentheresolvedobjectvalueshouldbecontainfieldsandwithvaluesandrespe [When(@"a boolean flag with key ""(.*)"" is evaluated with details and default value ""(.*)""")] public void Whenabooleanflagwithkeyisevaluatedwithdetailsanddefaultvalue(string flagKey, bool defaultValue) { - this.booleanFlagDetails = client.GetBooleanDetails(flagKey, defaultValue); + this.booleanFlagDetails = client.GetBooleanDetailsAsync(flagKey, defaultValue); } [Then(@"the resolved boolean details value should be ""(.*)"", the variant should be ""(.*)"", and the reason should be ""(.*)""")] @@ -133,7 +133,7 @@ public void Thentheresolvedbooleandetailsvalueshouldbethevariantshouldbeandthere [When(@"a string flag with key ""(.*)"" is evaluated with details and default value ""(.*)""")] public void Whenastringflagwithkeyisevaluatedwithdetailsanddefaultvalue(string flagKey, string defaultValue) { - this.stringFlagDetails = client.GetStringDetails(flagKey, defaultValue); + this.stringFlagDetails = client.GetStringDetailsAsync(flagKey, defaultValue); } [Then(@"the resolved string details value should be ""(.*)"", the variant should be ""(.*)"", and the reason should be ""(.*)""")] @@ -148,7 +148,7 @@ public void Thentheresolvedstringdetailsvalueshouldbethevariantshouldbeandtherea [When(@"an integer flag with key ""(.*)"" is evaluated with details and default value (.*)")] public void Whenanintegerflagwithkeyisevaluatedwithdetailsanddefaultvalue(string flagKey, int defaultValue) { - this.intFlagDetails = client.GetIntegerDetails(flagKey, defaultValue); + this.intFlagDetails = client.GetIntegerDetailsAsync(flagKey, defaultValue); } [Then(@"the resolved integer details value should be (.*), the variant should be ""(.*)"", and the reason should be ""(.*)""")] @@ -163,7 +163,7 @@ public void Thentheresolvedintegerdetailsvalueshouldbethevariantshouldbeandthere [When(@"a float flag with key ""(.*)"" is evaluated with details and default value (.*)")] public void Whenafloatflagwithkeyisevaluatedwithdetailsanddefaultvalue(string flagKey, double defaultValue) { - this.doubleFlagDetails = client.GetDoubleDetails(flagKey, defaultValue); + this.doubleFlagDetails = client.GetDoubleDetailsAsync(flagKey, defaultValue); } [Then(@"the resolved float details value should be (.*), the variant should be ""(.*)"", and the reason should be ""(.*)""")] @@ -178,7 +178,7 @@ public void Thentheresolvedfloatdetailsvalueshouldbethevariantshouldbeandthereas [When(@"an object flag with key ""(.*)"" is evaluated with details and a null default value")] public void Whenanobjectflagwithkeyisevaluatedwithdetailsandanulldefaultvalue(string flagKey) { - this.objectFlagDetails = client.GetObjectDetails(flagKey, new Value()); + this.objectFlagDetails = client.GetObjectDetailsAsync(flagKey, new Value()); } [Then(@"the resolved object details value should be contain fields ""(.*)"", ""(.*)"", and ""(.*)"", with values ""(.*)"", ""(.*)"" and (.*), respectively")] @@ -213,7 +213,7 @@ public void Givenaflagwithkeyisevaluatedwithdefaultvalue(string flagKey, string { contextAwareFlagKey = flagKey; contextAwareDefaultValue = defaultValue; - contextAwareValue = client.GetStringValue(flagKey, contextAwareDefaultValue, context).Result; + contextAwareValue = client.GetStringValueAsync(flagKey, contextAwareDefaultValue, context).Result; } [Then(@"the resolved string response should be ""(.*)""")] @@ -225,7 +225,7 @@ public void Thentheresolvedstringresponseshouldbe(string expected) [Then(@"the resolved flag value is ""(.*)"" when the context is empty")] public void Giventheresolvedflagvalueiswhenthecontextisempty(string expected) { - string emptyContextValue = client.GetStringValue(contextAwareFlagKey, contextAwareDefaultValue, new EvaluationContext(new Structure(ImmutableDictionary.Empty))).Result; + string emptyContextValue = client.GetStringValueAsync(contextAwareFlagKey, contextAwareDefaultValue, new EvaluationContext(new Structure(ImmutableDictionary.Empty))).Result; Assert.Equal(expected, emptyContextValue); } @@ -234,7 +234,7 @@ public void Whenanonexistentstringflagwithkeyisevaluatedwithdetailsandadefaultva { this.notFoundFlagKey = flagKey; this.notFoundDefaultValue = defaultValue; - this.notFoundDetails = client.GetStringDetails(this.notFoundFlagKey, this.notFoundDefaultValue).Result; + this.notFoundDetails = client.GetStringDetailsAsync(this.notFoundFlagKey, this.notFoundDefaultValue).Result; } [Then(@"the default string value should be returned")] @@ -255,7 +255,7 @@ public void Whenastringflagwithkeyisevaluatedasanintegerwithdetailsandadefaultva { this.typeErrorFlagKey = flagKey; this.typeErrorDefaultValue = defaultValue; - this.typeErrorDetails = client.GetIntegerDetails(this.typeErrorFlagKey, this.typeErrorDefaultValue).Result; + this.typeErrorDetails = client.GetIntegerDetailsAsync(this.typeErrorFlagKey, this.typeErrorDefaultValue).Result; } [Then(@"the default integer value should be returned")] diff --git a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs index a70921f7..30328e1c 100644 --- a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs +++ b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs @@ -7,7 +7,7 @@ public ClearOpenFeatureInstanceFixture() { Api.Instance.SetContext(null); Api.Instance.ClearHooks(); - Api.Instance.SetProvider(new NoOpFeatureProvider()).Wait(); + Api.Instance.SetProviderAsync(new NoOpFeatureProvider()).GetAwaiter().GetResult(); } } } diff --git a/test/OpenFeature.Tests/FeatureProviderTests.cs b/test/OpenFeature.Tests/FeatureProviderTests.cs index 8d679f94..53a67443 100644 --- a/test/OpenFeature.Tests/FeatureProviderTests.cs +++ b/test/OpenFeature.Tests/FeatureProviderTests.cs @@ -44,27 +44,27 @@ public async Task Provider_Must_Resolve_Flag_Values() var boolResolutionDetails = new ResolutionDetails(flagName, defaultBoolValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveBooleanValue(flagName, defaultBoolValue)).Should() + (await provider.ResolveBooleanValueAsync(flagName, defaultBoolValue)).Should() .BeEquivalentTo(boolResolutionDetails); var integerResolutionDetails = new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveIntegerValue(flagName, defaultIntegerValue)).Should() + (await provider.ResolveIntegerValueAsync(flagName, defaultIntegerValue)).Should() .BeEquivalentTo(integerResolutionDetails); var doubleResolutionDetails = new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveDoubleValue(flagName, defaultDoubleValue)).Should() + (await provider.ResolveDoubleValueAsync(flagName, defaultDoubleValue)).Should() .BeEquivalentTo(doubleResolutionDetails); var stringResolutionDetails = new ResolutionDetails(flagName, defaultStringValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveStringValue(flagName, defaultStringValue)).Should() + (await provider.ResolveStringValueAsync(flagName, defaultStringValue)).Should() .BeEquivalentTo(stringResolutionDetails); var structureResolutionDetails = new ResolutionDetails(flagName, defaultStructureValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveStructureValue(flagName, defaultStructureValue)).Should() + (await provider.ResolveStructureValueAsync(flagName, defaultStructureValue)).Should() .BeEquivalentTo(structureResolutionDetails); } @@ -84,59 +84,59 @@ public async Task Provider_Must_ErrorType() var providerMock = Substitute.For(); const string testMessage = "An error message"; - providerMock.ResolveBooleanValue(flagName, defaultBoolValue, Arg.Any()) + providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue, Arg.Any()) .Returns(new ResolutionDetails(flagName, defaultBoolValue, ErrorType.General, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveIntegerValue(flagName, defaultIntegerValue, Arg.Any()) + providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue, Arg.Any()) .Returns(new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.ParseError, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveDoubleValue(flagName, defaultDoubleValue, Arg.Any()) + providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue, Arg.Any()) .Returns(new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.InvalidContext, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStringValue(flagName, defaultStringValue, Arg.Any()) + providerMock.ResolveStringValueAsync(flagName, defaultStringValue, Arg.Any()) .Returns(new ResolutionDetails(flagName, defaultStringValue, ErrorType.TypeMismatch, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStructureValue(flagName, defaultStructureValue, Arg.Any()) + providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue, Arg.Any()) .Returns(new ResolutionDetails(flagName, defaultStructureValue, ErrorType.FlagNotFound, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStructureValue(flagName2, defaultStructureValue, Arg.Any()) + providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue, Arg.Any()) .Returns(new ResolutionDetails(flagName2, defaultStructureValue, ErrorType.ProviderNotReady, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveBooleanValue(flagName2, defaultBoolValue, Arg.Any()) + providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue, Arg.Any()) .Returns(new ResolutionDetails(flagName2, defaultBoolValue, ErrorType.TargetingKeyMissing, NoOpProvider.ReasonNoOp, NoOpProvider.Variant)); - var boolRes = await providerMock.ResolveBooleanValue(flagName, defaultBoolValue); + var boolRes = await providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue); boolRes.ErrorType.Should().Be(ErrorType.General); boolRes.ErrorMessage.Should().Be(testMessage); - var intRes = await providerMock.ResolveIntegerValue(flagName, defaultIntegerValue); + var intRes = await providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue); intRes.ErrorType.Should().Be(ErrorType.ParseError); intRes.ErrorMessage.Should().Be(testMessage); - var doubleRes = await providerMock.ResolveDoubleValue(flagName, defaultDoubleValue); + var doubleRes = await providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue); doubleRes.ErrorType.Should().Be(ErrorType.InvalidContext); doubleRes.ErrorMessage.Should().Be(testMessage); - var stringRes = await providerMock.ResolveStringValue(flagName, defaultStringValue); + var stringRes = await providerMock.ResolveStringValueAsync(flagName, defaultStringValue); stringRes.ErrorType.Should().Be(ErrorType.TypeMismatch); stringRes.ErrorMessage.Should().Be(testMessage); - var structRes1 = await providerMock.ResolveStructureValue(flagName, defaultStructureValue); + var structRes1 = await providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue); structRes1.ErrorType.Should().Be(ErrorType.FlagNotFound); structRes1.ErrorMessage.Should().Be(testMessage); - var structRes2 = await providerMock.ResolveStructureValue(flagName2, defaultStructureValue); + var structRes2 = await providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue); structRes2.ErrorType.Should().Be(ErrorType.ProviderNotReady); structRes2.ErrorMessage.Should().Be(testMessage); - var boolRes2 = await providerMock.ResolveBooleanValue(flagName2, defaultBoolValue); + var boolRes2 = await providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue); boolRes2.ErrorType.Should().Be(ErrorType.TargetingKeyMissing); boolRes2.ErrorMessage.Should().BeNull(); } diff --git a/test/OpenFeature.Tests/OpenFeatureClientTests.cs b/test/OpenFeature.Tests/OpenFeatureClientTests.cs index 30bee168..8ceb1c3a 100644 --- a/test/OpenFeature.Tests/OpenFeatureClientTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureClientTests.cs @@ -75,28 +75,28 @@ public async Task OpenFeatureClient_Should_Allow_Flag_Evaluation() var defaultStructureValue = fixture.Create(); var emptyFlagOptions = new FlagEvaluationOptions(ImmutableList.Empty, ImmutableDictionary.Empty); - await Api.Instance.SetProvider(new NoOpFeatureProvider()); + await Api.Instance.SetProviderAsync(new NoOpFeatureProvider()); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetBooleanValue(flagName, defaultBoolValue)).Should().Be(defaultBoolValue); - (await client.GetBooleanValue(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().Be(defaultBoolValue); - (await client.GetBooleanValue(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultBoolValue); + (await client.GetBooleanValueAsync(flagName, defaultBoolValue)).Should().Be(defaultBoolValue); + (await client.GetBooleanValueAsync(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().Be(defaultBoolValue); + (await client.GetBooleanValueAsync(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultBoolValue); - (await client.GetIntegerValue(flagName, defaultIntegerValue)).Should().Be(defaultIntegerValue); - (await client.GetIntegerValue(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().Be(defaultIntegerValue); - (await client.GetIntegerValue(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultIntegerValue); + (await client.GetIntegerValueAsync(flagName, defaultIntegerValue)).Should().Be(defaultIntegerValue); + (await client.GetIntegerValueAsync(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().Be(defaultIntegerValue); + (await client.GetIntegerValueAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultIntegerValue); - (await client.GetDoubleValue(flagName, defaultDoubleValue)).Should().Be(defaultDoubleValue); - (await client.GetDoubleValue(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().Be(defaultDoubleValue); - (await client.GetDoubleValue(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultDoubleValue); + (await client.GetDoubleValueAsync(flagName, defaultDoubleValue)).Should().Be(defaultDoubleValue); + (await client.GetDoubleValueAsync(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().Be(defaultDoubleValue); + (await client.GetDoubleValueAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultDoubleValue); - (await client.GetStringValue(flagName, defaultStringValue)).Should().Be(defaultStringValue); - (await client.GetStringValue(flagName, defaultStringValue, EvaluationContext.Empty)).Should().Be(defaultStringValue); - (await client.GetStringValue(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultStringValue); + (await client.GetStringValueAsync(flagName, defaultStringValue)).Should().Be(defaultStringValue); + (await client.GetStringValueAsync(flagName, defaultStringValue, EvaluationContext.Empty)).Should().Be(defaultStringValue); + (await client.GetStringValueAsync(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().Be(defaultStringValue); - (await client.GetObjectValue(flagName, defaultStructureValue)).Should().BeEquivalentTo(defaultStructureValue); - (await client.GetObjectValue(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(defaultStructureValue); - (await client.GetObjectValue(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(defaultStructureValue); + (await client.GetObjectValueAsync(flagName, defaultStructureValue)).Should().BeEquivalentTo(defaultStructureValue); + (await client.GetObjectValueAsync(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(defaultStructureValue); + (await client.GetObjectValueAsync(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(defaultStructureValue); } [Fact] @@ -121,33 +121,33 @@ public async Task OpenFeatureClient_Should_Allow_Details_Flag_Evaluation() var defaultStructureValue = fixture.Create(); var emptyFlagOptions = new FlagEvaluationOptions(ImmutableList.Empty, ImmutableDictionary.Empty); - await Api.Instance.SetProvider(new NoOpFeatureProvider()); + await Api.Instance.SetProviderAsync(new NoOpFeatureProvider()); var client = Api.Instance.GetClient(clientName, clientVersion); var boolFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultBoolValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await client.GetBooleanDetails(flagName, defaultBoolValue)).Should().BeEquivalentTo(boolFlagEvaluationDetails); - (await client.GetBooleanDetails(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().BeEquivalentTo(boolFlagEvaluationDetails); - (await client.GetBooleanDetails(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(boolFlagEvaluationDetails); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue)).Should().BeEquivalentTo(boolFlagEvaluationDetails); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue, EvaluationContext.Empty)).Should().BeEquivalentTo(boolFlagEvaluationDetails); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(boolFlagEvaluationDetails); var integerFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultIntegerValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await client.GetIntegerDetails(flagName, defaultIntegerValue)).Should().BeEquivalentTo(integerFlagEvaluationDetails); - (await client.GetIntegerDetails(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().BeEquivalentTo(integerFlagEvaluationDetails); - (await client.GetIntegerDetails(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(integerFlagEvaluationDetails); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue)).Should().BeEquivalentTo(integerFlagEvaluationDetails); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue, EvaluationContext.Empty)).Should().BeEquivalentTo(integerFlagEvaluationDetails); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(integerFlagEvaluationDetails); var doubleFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultDoubleValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await client.GetDoubleDetails(flagName, defaultDoubleValue)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); - (await client.GetDoubleDetails(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); - (await client.GetDoubleDetails(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue, EvaluationContext.Empty)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); var stringFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStringValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await client.GetStringDetails(flagName, defaultStringValue)).Should().BeEquivalentTo(stringFlagEvaluationDetails); - (await client.GetStringDetails(flagName, defaultStringValue, EvaluationContext.Empty)).Should().BeEquivalentTo(stringFlagEvaluationDetails); - (await client.GetStringDetails(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(stringFlagEvaluationDetails); + (await client.GetStringDetailsAsync(flagName, defaultStringValue)).Should().BeEquivalentTo(stringFlagEvaluationDetails); + (await client.GetStringDetailsAsync(flagName, defaultStringValue, EvaluationContext.Empty)).Should().BeEquivalentTo(stringFlagEvaluationDetails); + (await client.GetStringDetailsAsync(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(stringFlagEvaluationDetails); var structureFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStructureValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await client.GetObjectDetails(flagName, defaultStructureValue)).Should().BeEquivalentTo(structureFlagEvaluationDetails); - (await client.GetObjectDetails(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(structureFlagEvaluationDetails); - (await client.GetObjectDetails(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(structureFlagEvaluationDetails); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue)).Should().BeEquivalentTo(structureFlagEvaluationDetails); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue, EvaluationContext.Empty)).Should().BeEquivalentTo(structureFlagEvaluationDetails); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions)).Should().BeEquivalentTo(structureFlagEvaluationDetails); } [Fact] @@ -168,17 +168,17 @@ public async Task OpenFeatureClient_Should_Return_DefaultValue_When_Type_Mismatc var mockedLogger = Substitute.For>(); // This will fail to case a String to TestStructure - mockedFeatureProvider.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Throws(); + mockedFeatureProvider.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Throws(); mockedFeatureProvider.GetMetadata().Returns(new Metadata(fixture.Create())); mockedFeatureProvider.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(mockedFeatureProvider); + await Api.Instance.SetProviderAsync(mockedFeatureProvider); var client = Api.Instance.GetClient(clientName, clientVersion, mockedLogger); - var evaluationDetails = await client.GetObjectDetails(flagName, defaultValue); + var evaluationDetails = await client.GetObjectDetailsAsync(flagName, defaultValue); evaluationDetails.ErrorType.Should().Be(ErrorType.TypeMismatch); - _ = mockedFeatureProvider.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); + _ = mockedFeatureProvider.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); mockedLogger.Received(1).Log( LogLevel.Error, @@ -198,16 +198,16 @@ public async Task Should_Resolve_BooleanValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveBooleanValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetBooleanValue(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetBooleanValueAsync(flagName, defaultValue)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveBooleanValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -220,16 +220,16 @@ public async Task Should_Resolve_StringValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStringValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveStringValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetStringValue(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetStringValueAsync(flagName, defaultValue)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveStringValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStringValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -242,16 +242,16 @@ public async Task Should_Resolve_IntegerValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveIntegerValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetIntegerValue(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetIntegerValueAsync(flagName, defaultValue)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveIntegerValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -264,16 +264,16 @@ public async Task Should_Resolve_DoubleValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveDoubleValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetDoubleValue(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetDoubleValueAsync(flagName, defaultValue)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveDoubleValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -286,16 +286,16 @@ public async Task Should_Resolve_StructureValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - (await client.GetObjectValue(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetObjectValueAsync(flagName, defaultValue)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -309,18 +309,18 @@ public async Task When_Error_Is_Returned_From_Provider_Should_Return_Error() const string testMessage = "Couldn't parse flag data."; var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, "ERROR", null, testMessage))); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, "ERROR", null, testMessage))); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - var response = await client.GetObjectDetails(flagName, defaultValue); + var response = await client.GetObjectDetailsAsync(flagName, defaultValue); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] @@ -334,26 +334,26 @@ public async Task When_Exception_Occurs_During_Evaluation_Should_Return_Error() const string testMessage = "Couldn't parse flag data."; var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValue(flagName, defaultValue, Arg.Any()).Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); - await Api.Instance.SetProvider(featureProviderMock); + await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(clientName, clientVersion); - var response = await client.GetObjectDetails(flagName, defaultValue); + var response = await client.GetObjectDetailsAsync(flagName, defaultValue); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - _ = featureProviderMock.Received(1).ResolveStructureValue(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); } [Fact] public async Task Should_Use_No_Op_When_Provider_Is_Null() { - await Api.Instance.SetProvider(null); + await Api.Instance.SetProviderAsync(null); var client = new FeatureClient("test", "test"); - (await client.GetIntegerValue("some-key", 12)).Should().Be(12); + (await client.GetIntegerValueAsync("some-key", 12)).Should().Be(12); } [Fact] diff --git a/test/OpenFeature.Tests/OpenFeatureEventTests.cs b/test/OpenFeature.Tests/OpenFeatureEventTests.cs index 8c183e63..5431d636 100644 --- a/test/OpenFeature.Tests/OpenFeatureEventTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureEventTests.cs @@ -43,7 +43,7 @@ public async Task Event_Executor_Should_Propagate_Events_ToGlobal_Handler() eventHandler.Received().Invoke(Arg.Is(payload => payload == myEvent.EventPayload)); // shut down the event executor - await eventExecutor.Shutdown(); + await eventExecutor.ShutdownAsync(); // the next event should not be propagated to the event handler var newEventPayload = new ProviderEventPayload { Type = ProviderEventTypes.ProviderStale }; @@ -70,11 +70,11 @@ public async Task API_Level_Event_Handlers_Should_Be_Registered() Api.Instance.AddHandler(ProviderEventTypes.ProviderStale, eventHandler); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); - testProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); - testProvider.SendEvent(ProviderEventTypes.ProviderError); - testProvider.SendEvent(ProviderEventTypes.ProviderStale); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderError); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderStale); Thread.Sleep(1000); eventHandler @@ -118,7 +118,7 @@ public async Task API_Level_Event_Handlers_Should_Be_Informed_About_Ready_State_ var eventHandler = Substitute.For(); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); @@ -143,7 +143,7 @@ public async Task API_Level_Event_Handlers_Should_Be_Informed_About_Error_State_ var eventHandler = Substitute.For(); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); testProvider.SetStatus(ProviderStatus.Error); @@ -169,7 +169,7 @@ public async Task API_Level_Event_Handlers_Should_Be_Informed_About_Stale_State_ var eventHandler = Substitute.For(); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); testProvider.SetStatus(ProviderStatus.Stale); @@ -198,14 +198,14 @@ public async Task API_Level_Event_Handlers_Should_Be_Exchangeable() Api.Instance.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, eventHandler); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); - testProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); var newTestProvider = new TestProvider(); - await Api.Instance.SetProvider(newTestProvider); + await Api.Instance.SetProviderAsync(newTestProvider); - newTestProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); + await newTestProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); Thread.Sleep(1000); eventHandler.Received(2).Invoke(Arg.Is(payload => payload.ProviderName == testProvider.GetMetadata().Name && payload.Type == ProviderEventTypes.ProviderReady)); @@ -224,13 +224,13 @@ public async Task API_Level_Event_Handlers_Should_Be_Removable() Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); Thread.Sleep(1000); Api.Instance.RemoveHandler(ProviderEventTypes.ProviderReady, eventHandler); var newTestProvider = new TestProvider(); - await Api.Instance.SetProvider(newTestProvider); + await Api.Instance.SetProviderAsync(newTestProvider); eventHandler.Received(1).Invoke(Arg.Is(payload => payload.ProviderName == testProvider.GetMetadata().Name)); } @@ -255,7 +255,7 @@ public async Task API_Level_Event_Handlers_Should_Be_Executed_When_Other_Handler Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); var testProvider = new TestProvider(fixture.Create()); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); failingEventHandler.Received().Invoke(Arg.Is(payload => payload.ProviderName == testProvider.GetMetadata().Name)); eventHandler.Received().Invoke(Arg.Is(payload => payload.ProviderName == testProvider.GetMetadata().Name)); @@ -274,7 +274,7 @@ public async Task Client_Level_Event_Handlers_Should_Be_Registered() var myClient = Api.Instance.GetClient(fixture.Create()); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(myClient.GetMetadata().Name, testProvider); + await Api.Instance.SetProviderAsync(myClient.GetMetadata().Name, testProvider); myClient.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); @@ -303,7 +303,7 @@ public async Task Client_Level_Event_Handlers_Should_Be_Executed_When_Other_Hand myClient.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(myClient.GetMetadata().Name, testProvider); + await Api.Instance.SetProviderAsync(myClient.GetMetadata().Name, testProvider); Thread.Sleep(1000); @@ -330,9 +330,9 @@ public async Task Client_Level_Event_Handlers_Should_Be_Registered_To_Default_Pr var clientProvider = new TestProvider(fixture.Create()); // set the default provider on API level, but not specifically to the client - await Api.Instance.SetProvider(apiProvider); + await Api.Instance.SetProviderAsync(apiProvider); // set the other provider specifically for the client - await Api.Instance.SetProvider(myClientWithBoundProvider.GetMetadata().Name, clientProvider); + await Api.Instance.SetProviderAsync(myClientWithBoundProvider.GetMetadata().Name, clientProvider); myClientWithNoBoundProvider.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); myClientWithBoundProvider.AddHandler(ProviderEventTypes.ProviderReady, clientEventHandler); @@ -362,11 +362,11 @@ public async Task Client_Level_Event_Handlers_Should_Be_Receive_Events_From_Name var clientProvider = new TestProvider(fixture.Create()); // set the default provider - await Api.Instance.SetProvider(defaultProvider); + await Api.Instance.SetProviderAsync(defaultProvider); client.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, clientEventHandler); - defaultProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); + await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); Thread.Sleep(1000); @@ -374,11 +374,11 @@ public async Task Client_Level_Event_Handlers_Should_Be_Receive_Events_From_Name clientEventHandler.Received(1).Invoke(Arg.Is(payload => payload.ProviderName == defaultProvider.GetMetadata().Name && payload.Type == ProviderEventTypes.ProviderConfigurationChanged)); // set the other provider specifically for the client - await Api.Instance.SetProvider(client.GetMetadata().Name, clientProvider); + await Api.Instance.SetProviderAsync(client.GetMetadata().Name, clientProvider); // now, send another event for the default handler - defaultProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); - clientProvider.SendEvent(ProviderEventTypes.ProviderConfigurationChanged); + await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await clientProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); Thread.Sleep(1000); @@ -403,7 +403,7 @@ public async Task Client_Level_Event_Handlers_Should_Be_Informed_About_Ready_Sta var myClient = Api.Instance.GetClient(fixture.Create()); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(myClient.GetMetadata().Name, testProvider); + await Api.Instance.SetProviderAsync(myClient.GetMetadata().Name, testProvider); // add the event handler after the provider has already transitioned into the ready state myClient.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); @@ -428,14 +428,14 @@ public async Task Client_Level_Event_Handlers_Should_Be_Removable() myClient.AddHandler(ProviderEventTypes.ProviderReady, eventHandler); var testProvider = new TestProvider(); - await Api.Instance.SetProvider(myClient.GetMetadata().Name, testProvider); + await Api.Instance.SetProviderAsync(myClient.GetMetadata().Name, testProvider); // wait for the first event to be received Thread.Sleep(1000); myClient.RemoveHandler(ProviderEventTypes.ProviderReady, eventHandler); // send another event from the provider - this one should not be received - testProvider.SendEvent(ProviderEventTypes.ProviderReady); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderReady); // wait a bit and make sure we only have received the first event, but nothing after removing the event handler Thread.Sleep(1000); diff --git a/test/OpenFeature.Tests/OpenFeatureHookTests.cs b/test/OpenFeature.Tests/OpenFeatureHookTests.cs index b3aee4d8..0e3effc1 100644 --- a/test/OpenFeature.Tests/OpenFeatureHookTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureHookTests.cs @@ -35,57 +35,57 @@ public async Task Hooks_Should_Be_Called_In_Order() var providerHook = Substitute.For(); // Sequence - apiHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); - clientHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); - invocationHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); - providerHook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); - providerHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - invocationHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - clientHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - apiHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - providerHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - invocationHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - clientHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); - apiHook.Finally(Arg.Any>(), Arg.Any>()).Returns(Task.CompletedTask); + apiHook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + clientHook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + invocationHook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + providerHook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + providerHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + invocationHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + clientHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + apiHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + providerHook.FinallyAsync(Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + invocationHook.FinallyAsync(Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + clientHook.FinallyAsync(Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); + apiHook.FinallyAsync(Arg.Any>(), Arg.Any>()).Returns(new ValueTask()); var testProvider = new TestProvider(); testProvider.AddHook(providerHook); Api.Instance.AddHooks(apiHook); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); var client = Api.Instance.GetClient(clientName, clientVersion); client.AddHooks(clientHook); - await client.GetBooleanValue(flagName, defaultValue, EvaluationContext.Empty, + await client.GetBooleanValueAsync(flagName, defaultValue, EvaluationContext.Empty, new FlagEvaluationOptions(invocationHook, ImmutableDictionary.Empty)); Received.InOrder(() => { - apiHook.Before(Arg.Any>(), Arg.Any>()); - clientHook.Before(Arg.Any>(), Arg.Any>()); - invocationHook.Before(Arg.Any>(), Arg.Any>()); - providerHook.Before(Arg.Any>(), Arg.Any>()); - providerHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - invocationHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - clientHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - apiHook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - providerHook.Finally(Arg.Any>(), Arg.Any>()); - invocationHook.Finally(Arg.Any>(), Arg.Any>()); - clientHook.Finally(Arg.Any>(), Arg.Any>()); - apiHook.Finally(Arg.Any>(), Arg.Any>()); + apiHook.BeforeAsync(Arg.Any>(), Arg.Any>()); + clientHook.BeforeAsync(Arg.Any>(), Arg.Any>()); + invocationHook.BeforeAsync(Arg.Any>(), Arg.Any>()); + providerHook.BeforeAsync(Arg.Any>(), Arg.Any>()); + providerHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + invocationHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + clientHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + apiHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + providerHook.FinallyAsync(Arg.Any>(), Arg.Any>()); + invocationHook.FinallyAsync(Arg.Any>(), Arg.Any>()); + clientHook.FinallyAsync(Arg.Any>(), Arg.Any>()); + apiHook.FinallyAsync(Arg.Any>(), Arg.Any>()); }); - _ = apiHook.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = clientHook.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = invocationHook.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = providerHook.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = providerHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = invocationHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = clientHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = apiHook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = providerHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); - _ = invocationHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); - _ = clientHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); - _ = apiHook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + _ = apiHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = apiHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = providerHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); + _ = invocationHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); + _ = clientHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); + _ = apiHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); } [Fact] @@ -139,15 +139,15 @@ public async Task Evaluation_Context_Must_Be_Mutable_Before_Hook() FlagValueType.Boolean, new ClientMetadata("test", "1.0.0"), new Metadata(NoOpProvider.NoOpProviderName), evaluationContext); - hook1.Before(Arg.Any>(), Arg.Any>()).Returns(evaluationContext); - hook2.Before(hookContext, Arg.Any>()).Returns(evaluationContext); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(evaluationContext); + hook2.BeforeAsync(hookContext, Arg.Any>()).Returns(evaluationContext); var client = Api.Instance.GetClient("test", "1.0.0"); - await client.GetBooleanValue("test", false, EvaluationContext.Empty, + await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, new FlagEvaluationOptions(ImmutableList.Create(hook1, hook2), ImmutableDictionary.Empty)); - _ = hook1.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = hook2.Received(1).Before(Arg.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), Arg.Any>()); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = hook2.Received(1).BeforeAsync(Arg.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), Arg.Any>()); } [Fact] @@ -195,19 +195,19 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order() provider.GetProviderHooks().Returns(ImmutableList.Empty); - provider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", true)); + provider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", true)); - await Api.Instance.SetProvider(provider); + await Api.Instance.SetProviderAsync(provider); var hook = Substitute.For(); - hook.Before(Arg.Any>(), Arg.Any>()).Returns(hookContext); + hook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(hookContext); var client = Api.Instance.GetClient("test", "1.0.0", null, clientContext); - await client.GetBooleanValue("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook), ImmutableDictionary.Empty)); + await client.GetBooleanValueAsync("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook), ImmutableDictionary.Empty)); // after proper merging, all properties should equal true - _ = provider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Is(y => + _ = provider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Is(y => (y.GetValue(propGlobal).AsBoolean ?? false) && (y.GetValue(propClient).AsBoolean ?? false) && (y.GetValue(propGlobalToOverwrite).AsBoolean ?? false) @@ -238,10 +238,10 @@ public async Task Hook_Should_Return_No_Errors() var hookContext = new HookContext("test", false, FlagValueType.Boolean, new ClientMetadata(null, null), new Metadata(null), EvaluationContext.Empty); - await hook.Before(hookContext, hookHints); - await hook.After(hookContext, new FlagEvaluationDetails("test", false, ErrorType.None, "testing", "testing"), hookHints); - await hook.Finally(hookContext, hookHints); - await hook.Error(hookContext, new Exception(), hookHints); + await hook.BeforeAsync(hookContext, hookHints); + await hook.AfterAsync(hookContext, new FlagEvaluationDetails("test", false, ErrorType.None, "testing", "testing"), hookHints); + await hook.FinallyAsync(hookContext, hookHints); + await hook.ErrorAsync(hookContext, new Exception(), hookHints); hookContext.ClientMetadata.Name.Should().BeNull(); hookContext.ClientMetadata.Version.Should().BeNull(); @@ -264,29 +264,29 @@ public async Task Hook_Should_Execute_In_Correct_Order() featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - hook.Before(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); - _ = hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = hook.Finally(Arg.Any>(), Arg.Any>()); + hook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); + _ = hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = hook.FinallyAsync(Arg.Any>(), Arg.Any>()); - await Api.Instance.SetProvider(featureProvider); + await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); client.AddHooks(hook); - await client.GetBooleanValue("test", false); + await client.GetBooleanValueAsync("test", false); Received.InOrder(() => { - hook.Before(Arg.Any>(), Arg.Any>()); - featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); - hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - hook.Finally(Arg.Any>(), Arg.Any>()); + hook.BeforeAsync(Arg.Any>(), Arg.Any>()); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + hook.FinallyAsync(Arg.Any>(), Arg.Any>()); }); - _ = hook.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = hook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - _ = hook.Received(1).Finally(Arg.Any>(), Arg.Any>()); - _ = featureProvider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); + _ = hook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = hook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + _ = hook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); + _ = featureProvider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -301,10 +301,10 @@ public async Task Register_Hooks_Should_Be_Available_At_All_Levels() var testProvider = new TestProvider(); testProvider.AddHook(hook4); Api.Instance.AddHooks(hook1); - await Api.Instance.SetProvider(testProvider); + await Api.Instance.SetProviderAsync(testProvider); var client = Api.Instance.GetClient(); client.AddHooks(hook2); - await client.GetBooleanValue("test", false, null, + await client.GetBooleanValueAsync("test", false, null, new FlagEvaluationOptions(hook3, ImmutableDictionary.Empty)); Assert.Single(Api.Instance.GetHooks()); @@ -324,39 +324,39 @@ public async Task Finally_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - hook1.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); - hook2.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); - hook2.After(Arg.Any>(), Arg.Any>(), null).Returns(Task.CompletedTask); - hook1.After(Arg.Any>(), Arg.Any>(), null).Returns(Task.CompletedTask); - hook2.Finally(Arg.Any>(), null).Returns(Task.CompletedTask); - hook1.Finally(Arg.Any>(), null).Throws(new Exception()); - - await Api.Instance.SetProvider(featureProvider); + hook1.BeforeAsync(Arg.Any>(), null).Returns(EvaluationContext.Empty); + hook2.BeforeAsync(Arg.Any>(), null).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", false)); + hook2.AfterAsync(Arg.Any>(), Arg.Any>(), null).Returns(new ValueTask()); + hook1.AfterAsync(Arg.Any>(), Arg.Any>(), null).Returns(new ValueTask()); + hook2.FinallyAsync(Arg.Any>(), null).Returns(new ValueTask()); + hook1.FinallyAsync(Arg.Any>(), null).Throws(new Exception()); + + await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); client.AddHooks(new[] { hook1, hook2 }); client.GetHooks().Count().Should().Be(2); - await client.GetBooleanValue("test", false); + await client.GetBooleanValueAsync("test", false); Received.InOrder(() => { - hook1.Before(Arg.Any>(), null); - hook2.Before(Arg.Any>(), null); - featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); - hook2.After(Arg.Any>(), Arg.Any>(), null); - hook1.After(Arg.Any>(), Arg.Any>(), null); - hook2.Finally(Arg.Any>(), null); - hook1.Finally(Arg.Any>(), null); + hook1.BeforeAsync(Arg.Any>(), null); + hook2.BeforeAsync(Arg.Any>(), null); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()); + hook2.AfterAsync(Arg.Any>(), Arg.Any>(), null); + hook1.AfterAsync(Arg.Any>(), Arg.Any>(), null); + hook2.FinallyAsync(Arg.Any>(), null); + hook1.FinallyAsync(Arg.Any>(), null); }); - _ = hook1.Received(1).Before(Arg.Any>(), null); - _ = hook2.Received(1).Before(Arg.Any>(), null); - _ = featureProvider.Received(1).ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); - _ = hook2.Received(1).After(Arg.Any>(), Arg.Any>(), null); - _ = hook1.Received(1).After(Arg.Any>(), Arg.Any>(), null); - _ = hook2.Received(1).Finally(Arg.Any>(), null); - _ = hook1.Received(1).Finally(Arg.Any>(), null); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), null); + _ = hook2.Received(1).BeforeAsync(Arg.Any>(), null); + _ = featureProvider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()); + _ = hook2.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), null); + _ = hook1.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), null); + _ = hook2.Received(1).FinallyAsync(Arg.Any>(), null); + _ = hook1.Received(1).FinallyAsync(Arg.Any>(), null); } [Fact] @@ -371,31 +371,31 @@ public async Task Error_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() featureProvider1.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - hook1.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); - hook2.Before(Arg.Any>(), null).Returns(EvaluationContext.Empty); - featureProvider1.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new Exception()); - hook2.Error(Arg.Any>(), Arg.Any(), null).Returns(Task.CompletedTask); - hook1.Error(Arg.Any>(), Arg.Any(), null).Returns(Task.CompletedTask); + hook1.BeforeAsync(Arg.Any>(), null).Returns(EvaluationContext.Empty); + hook2.BeforeAsync(Arg.Any>(), null).Returns(EvaluationContext.Empty); + featureProvider1.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new Exception()); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null).Returns(new ValueTask()); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null).Returns(new ValueTask()); - await Api.Instance.SetProvider(featureProvider1); + await Api.Instance.SetProviderAsync(featureProvider1); var client = Api.Instance.GetClient(); client.AddHooks(new[] { hook1, hook2 }); - await client.GetBooleanValue("test", false); + await client.GetBooleanValueAsync("test", false); Received.InOrder(() => { - hook1.Before(Arg.Any>(), null); - hook2.Before(Arg.Any>(), null); - featureProvider1.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()); - hook2.Error(Arg.Any>(), Arg.Any(), null); - hook1.Error(Arg.Any>(), Arg.Any(), null); + hook1.BeforeAsync(Arg.Any>(), null); + hook2.BeforeAsync(Arg.Any>(), null); + featureProvider1.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null); }); - _ = hook1.Received(1).Before(Arg.Any>(), null); - _ = hook2.Received(1).Before(Arg.Any>(), null); - _ = hook1.Received(1).Error(Arg.Any>(), Arg.Any(), null); - _ = hook2.Received(1).Error(Arg.Any>(), Arg.Any(), null); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), null); + _ = hook2.Received(1).BeforeAsync(Arg.Any>(), null); + _ = hook1.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null); + _ = hook2.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null); } [Fact] @@ -410,27 +410,27 @@ public async Task Error_Occurs_During_Before_After_Evaluation_Should_Not_Invoke_ featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - hook1.Before(Arg.Any>(), Arg.Any>()).ThrowsAsync(new Exception()); - _ = hook1.Error(Arg.Any>(), Arg.Any(), null); - _ = hook2.Error(Arg.Any>(), Arg.Any(), null); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>()).Throws(new Exception()); + _ = hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null); + _ = hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null); - await Api.Instance.SetProvider(featureProvider); + await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); client.AddHooks(new[] { hook1, hook2 }); - await client.GetBooleanValue("test", false); + await client.GetBooleanValueAsync("test", false); Received.InOrder(() => { - hook1.Before(Arg.Any>(), Arg.Any>()); - hook2.Error(Arg.Any>(), Arg.Any(), null); - hook1.Error(Arg.Any>(), Arg.Any(), null); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>()); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null); }); - _ = hook1.Received(1).Before(Arg.Any>(), Arg.Any>()); - _ = hook2.DidNotReceive().Before(Arg.Any>(), Arg.Any>()); - _ = hook1.Received(1).Error(Arg.Any>(), Arg.Any(), null); - _ = hook2.Received(1).Error(Arg.Any>(), Arg.Any(), null); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = hook2.DidNotReceive().BeforeAsync(Arg.Any>(), Arg.Any>()); + _ = hook1.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null); + _ = hook2.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null); } [Fact] @@ -447,29 +447,29 @@ public async Task Hook_Hints_May_Be_Optional() featureProvider.GetProviderHooks() .Returns(ImmutableList.Empty); - hook.Before(Arg.Any>(), Arg.Any>()) + hook.BeforeAsync(Arg.Any>(), Arg.Any>()) .Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValue("test", false, Arg.Any()) + featureProvider.ResolveBooleanValueAsync("test", false, Arg.Any()) .Returns(new ResolutionDetails("test", false)); - hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()) - .Returns(Task.FromResult(Task.CompletedTask)); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()) + .Returns(new ValueTask()); - hook.Finally(Arg.Any>(), Arg.Any>()) - .Returns(Task.CompletedTask); + hook.FinallyAsync(Arg.Any>(), Arg.Any>()) + .Returns(new ValueTask()); - await Api.Instance.SetProvider(featureProvider); + await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); - await client.GetBooleanValue("test", false, EvaluationContext.Empty, flagOptions); + await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, flagOptions); Received.InOrder(() => { - hook.Received().Before(Arg.Any>(), Arg.Any>()); - featureProvider.Received().ResolveBooleanValue("test", false, Arg.Any()); - hook.Received().After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - hook.Received().Finally(Arg.Any>(), Arg.Any>()); + hook.Received().BeforeAsync(Arg.Any>(), Arg.Any>()); + featureProvider.Received().ResolveBooleanValueAsync("test", false, Arg.Any()); + hook.Received().AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + hook.Received().FinallyAsync(Arg.Any>(), Arg.Any>()); }); } @@ -485,26 +485,26 @@ public async Task When_Error_Occurs_In_Before_Hook_Should_Return_Default_Value() featureProvider.GetMetadata().Returns(new Metadata(null)); // Sequence - hook.Before(Arg.Any>(), Arg.Any>()).ThrowsAsync(exceptionToThrow); - hook.Error(Arg.Any>(), Arg.Any(), null).Returns(Task.CompletedTask); - hook.Finally(Arg.Any>(), null).Returns(Task.CompletedTask); + hook.BeforeAsync(Arg.Any>(), Arg.Any>()).Throws(exceptionToThrow); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), null).Returns(new ValueTask()); + hook.FinallyAsync(Arg.Any>(), null).Returns(new ValueTask()); var client = Api.Instance.GetClient(); client.AddHooks(hook); - var resolvedFlag = await client.GetBooleanValue("test", true); + var resolvedFlag = await client.GetBooleanValueAsync("test", true); Received.InOrder(() => { - hook.Before(Arg.Any>(), Arg.Any>()); - hook.Error(Arg.Any>(), Arg.Any(), null); - hook.Finally(Arg.Any>(), null); + hook.BeforeAsync(Arg.Any>(), Arg.Any>()); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), null); + hook.FinallyAsync(Arg.Any>(), null); }); resolvedFlag.Should().BeTrue(); - _ = hook.Received(1).Before(Arg.Any>(), null); - _ = hook.Received(1).Error(Arg.Any>(), exceptionToThrow, null); - _ = hook.Received(1).Finally(Arg.Any>(), null); + _ = hook.Received(1).BeforeAsync(Arg.Any>(), null); + _ = hook.Received(1).ErrorAsync(Arg.Any>(), exceptionToThrow, null); + _ = hook.Received(1).FinallyAsync(Arg.Any>(), null); } [Fact] @@ -522,36 +522,36 @@ public async Task When_Error_Occurs_In_After_Hook_Should_Invoke_Error_Hook() featureProvider.GetProviderHooks() .Returns(ImmutableList.Empty); - hook.Before(Arg.Any>(), Arg.Any>()) + hook.BeforeAsync(Arg.Any>(), Arg.Any>()) .Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValue(Arg.Any(), Arg.Any(), Arg.Any()) + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(new ResolutionDetails("test", false)); - hook.After(Arg.Any>(), Arg.Any>(), Arg.Any>()) - .ThrowsAsync(exceptionToThrow); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()) + .Throws(exceptionToThrow); - hook.Error(Arg.Any>(), Arg.Any(), Arg.Any>()) - .Returns(Task.CompletedTask); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>()) + .Returns(new ValueTask()); - hook.Finally(Arg.Any>(), Arg.Any>()) - .Returns(Task.CompletedTask); + hook.FinallyAsync(Arg.Any>(), Arg.Any>()) + .Returns(new ValueTask()); - await Api.Instance.SetProvider(featureProvider); + await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); - var resolvedFlag = await client.GetBooleanValue("test", true, config: flagOptions); + var resolvedFlag = await client.GetBooleanValueAsync("test", true, config: flagOptions); resolvedFlag.Should().BeTrue(); Received.InOrder(() => { - hook.Received(1).Before(Arg.Any>(), Arg.Any>()); - hook.Received(1).After(Arg.Any>(), Arg.Any>(), Arg.Any>()); - hook.Received(1).Finally(Arg.Any>(), Arg.Any>()); + hook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); + hook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()); + hook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>()); }); - await featureProvider.DidNotReceive().ResolveBooleanValue("test", false, Arg.Any()); + await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any()); } [Fact] diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index 3e8b4d3d..9a38daaa 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NSubstitute; @@ -11,10 +12,7 @@ namespace OpenFeature.Tests { public class OpenFeatureTests : ClearOpenFeatureInstanceFixture { - static async Task EmptyShutdown() - { - await Task.FromResult(0).ConfigureAwait(false); - } + static ValueTask EmptyShutdown(CancellationToken cancellationToken) => new ValueTask(); [Fact] [Specification("1.1.1", "The `API`, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.")] @@ -33,14 +31,14 @@ public async Task OpenFeature_Should_Initialize_Provider() var providerMockDefault = Substitute.For(); providerMockDefault.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider(providerMockDefault).ConfigureAwait(false); - await providerMockDefault.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); + await Api.Instance.SetProviderAsync(providerMockDefault).ConfigureAwait(false); + await providerMockDefault.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); var providerMockNamed = Substitute.For(); providerMockNamed.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider("the-name", providerMockNamed).ConfigureAwait(false); - await providerMockNamed.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); + await Api.Instance.SetProviderAsync("the-name", providerMockNamed).ConfigureAwait(false); + await providerMockNamed.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); } [Fact] @@ -51,28 +49,28 @@ public async Task OpenFeature_Should_Shutdown_Unused_Provider() var providerA = Substitute.For(); providerA.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider(providerA).ConfigureAwait(false); - await providerA.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); + await Api.Instance.SetProviderAsync(providerA).ConfigureAwait(false); + await providerA.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); var providerB = Substitute.For(); providerB.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider(providerB).ConfigureAwait(false); - await providerB.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); - await providerA.Received(1).Shutdown().ConfigureAwait(false); + await Api.Instance.SetProviderAsync(providerB).ConfigureAwait(false); + await providerB.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); + await providerA.Received(1).ShutdownAsync().ConfigureAwait(false); var providerC = Substitute.For(); providerC.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider("named", providerC).ConfigureAwait(false); - await providerC.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); + await Api.Instance.SetProviderAsync("named", providerC).ConfigureAwait(false); + await providerC.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); var providerD = Substitute.For(); providerD.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider("named", providerD).ConfigureAwait(false); - await providerD.Received(1).Initialize(Api.Instance.GetContext()).ConfigureAwait(false); - await providerC.Received(1).Shutdown().ConfigureAwait(false); + await Api.Instance.SetProviderAsync("named", providerD).ConfigureAwait(false); + await providerD.Received(1).InitializeAsync(Api.Instance.GetContext()).ConfigureAwait(false); + await providerC.Received(1).ShutdownAsync().ConfigureAwait(false); } [Fact] @@ -88,23 +86,23 @@ public async Task OpenFeature_Should_Support_Shutdown() var providerB = Substitute.For(); providerB.GetStatus().Returns(ProviderStatus.NotReady); - await Api.Instance.SetProvider(providerA).ConfigureAwait(false); - await Api.Instance.SetProvider("named", providerB).ConfigureAwait(false); + await Api.Instance.SetProviderAsync(providerA).ConfigureAwait(false); + await Api.Instance.SetProviderAsync("named", providerB).ConfigureAwait(false); - await Api.Instance.Shutdown().ConfigureAwait(false); + await Api.Instance.ShutdownAsync().ConfigureAwait(false); - await providerA.Received(1).Shutdown().ConfigureAwait(false); - await providerB.Received(1).Shutdown().ConfigureAwait(false); + await providerA.Received(1).ShutdownAsync().ConfigureAwait(false); + await providerB.Received(1).ShutdownAsync().ConfigureAwait(false); } [Fact] [Specification("1.1.3", "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.")] - public void OpenFeature_Should_Not_Change_Named_Providers_When_Setting_Default_Provider() + public async Task OpenFeature_Should_Not_Change_Named_Providers_When_Setting_Default_Provider() { var openFeature = Api.Instance; - openFeature.SetProvider(new NoOpFeatureProvider()); - openFeature.SetProvider(TestProvider.DefaultName, new TestProvider()); + await openFeature.SetProviderAsync(new NoOpFeatureProvider()).ConfigureAwait(false); + await openFeature.SetProviderAsync(TestProvider.DefaultName, new TestProvider()).ConfigureAwait(false); var defaultClient = openFeature.GetProviderMetadata(); var namedClient = openFeature.GetProviderMetadata(TestProvider.DefaultName); @@ -115,11 +113,11 @@ public void OpenFeature_Should_Not_Change_Named_Providers_When_Setting_Default_P [Fact] [Specification("1.1.3", "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.")] - public void OpenFeature_Should_Set_Default_Provide_When_No_Name_Provided() + public async Task OpenFeature_Should_Set_Default_Provide_When_No_Name_Provided() { var openFeature = Api.Instance; - openFeature.SetProvider(new TestProvider()); + await openFeature.SetProviderAsync(new TestProvider()).ConfigureAwait(false); var defaultClient = openFeature.GetProviderMetadata(); @@ -128,26 +126,26 @@ public void OpenFeature_Should_Set_Default_Provide_When_No_Name_Provided() [Fact] [Specification("1.1.3", "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.")] - public void OpenFeature_Should_Assign_Provider_To_Existing_Client() + public async Task OpenFeature_Should_Assign_Provider_To_Existing_Client() { const string name = "new-client"; var openFeature = Api.Instance; - openFeature.SetProvider(name, new TestProvider()); - openFeature.SetProvider(name, new NoOpFeatureProvider()); + await openFeature.SetProviderAsync(name, new TestProvider()).ConfigureAwait(false); + await openFeature.SetProviderAsync(name, new NoOpFeatureProvider()).ConfigureAwait(false); openFeature.GetProviderMetadata(name).Name.Should().Be(NoOpProvider.NoOpProviderName); } [Fact] [Specification("1.1.3", "The `API` MUST provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping.")] - public void OpenFeature_Should_Allow_Multiple_Client_Names_Of_Same_Instance() + public async Task OpenFeature_Should_Allow_Multiple_Client_Names_Of_Same_Instance() { var openFeature = Api.Instance; var provider = new TestProvider(); - openFeature.SetProvider("a", provider); - openFeature.SetProvider("b", provider); + await openFeature.SetProviderAsync("a", provider).ConfigureAwait(false); + await openFeature.SetProviderAsync("b", provider).ConfigureAwait(false); var clientA = openFeature.GetProvider("a"); var clientB = openFeature.GetProvider("b"); @@ -188,7 +186,7 @@ public void OpenFeature_Should_Add_Hooks() [Specification("1.1.5", "The API MUST provide a function for retrieving the metadata field of the configured `provider`.")] public void OpenFeature_Should_Get_Metadata() { - Api.Instance.SetProvider(new NoOpFeatureProvider()).Wait(); + Api.Instance.SetProviderAsync(new NoOpFeatureProvider()).GetAwaiter().GetResult(); var openFeature = Api.Instance; var metadata = openFeature.GetProviderMetadata(); @@ -234,12 +232,12 @@ public void Should_Always_Have_Provider() } [Fact] - public void OpenFeature_Should_Allow_Multiple_Client_Mapping() + public async Task OpenFeature_Should_Allow_Multiple_Client_Mapping() { var openFeature = Api.Instance; - openFeature.SetProvider("client1", new TestProvider()); - openFeature.SetProvider("client2", new NoOpFeatureProvider()); + await openFeature.SetProviderAsync("client1", new TestProvider()).ConfigureAwait(false); + await openFeature.SetProviderAsync("client2", new NoOpFeatureProvider()).ConfigureAwait(false); var client1 = openFeature.GetClient("client1"); var client2 = openFeature.GetClient("client2"); @@ -247,8 +245,8 @@ public void OpenFeature_Should_Allow_Multiple_Client_Mapping() client1.GetMetadata().Name.Should().Be("client1"); client2.GetMetadata().Name.Should().Be("client2"); - client1.GetBooleanValue("test", false).Result.Should().BeTrue(); - client2.GetBooleanValue("test", false).Result.Should().BeFalse(); + client1.GetBooleanValueAsync("test", false).Result.Should().BeTrue(); + client2.GetBooleanValueAsync("test", false).Result.Should().BeFalse(); } } } diff --git a/test/OpenFeature.Tests/ProviderRepositoryTests.cs b/test/OpenFeature.Tests/ProviderRepositoryTests.cs index 6d2ff310..f99155be 100644 --- a/test/OpenFeature.Tests/ProviderRepositoryTests.cs +++ b/test/OpenFeature.Tests/ProviderRepositoryTests.cs @@ -21,7 +21,7 @@ public void Default_Provider_Is_Set_Without_Await() var repository = new ProviderRepository(); var provider = new NoOpFeatureProvider(); var context = new EvaluationContextBuilder().Build(); - repository.SetProvider(provider, context); + repository.SetProviderAsync(provider, context); Assert.Equal(provider, repository.GetProvider()); } @@ -33,7 +33,7 @@ public void AfterSet_Is_Invoked_For_Setting_Default_Provider() var context = new EvaluationContextBuilder().Build(); var callCount = 0; // The setting of the provider is synchronous, so the afterSet should be as well. - repository.SetProvider(provider, context, afterSet: (theProvider) => + repository.SetProviderAsync(provider, context, afterSet: (theProvider) => { callCount++; Assert.Equal(provider, theProvider); @@ -48,9 +48,9 @@ public async Task Initialization_Provider_Method_Is_Invoked_For_Setting_Default_ var providerMock = Substitute.For(); providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(providerMock, context); - providerMock.Received(1).Initialize(context); - providerMock.DidNotReceive().Shutdown(); + await repository.SetProviderAsync(providerMock, context); + providerMock.Received(1).InitializeAsync(context); + providerMock.DidNotReceive().ShutdownAsync(); } [Fact] @@ -61,7 +61,7 @@ public async Task AfterInitialization_Is_Invoked_For_Setting_Default_Provider() providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); var callCount = 0; - await repository.SetProvider(providerMock, context, afterInitialization: (theProvider) => + await repository.SetProviderAsync(providerMock, context, afterInitialization: (theProvider) => { Assert.Equal(providerMock, theProvider); callCount++; @@ -76,10 +76,10 @@ public async Task AfterError_Is_Invoked_If_Initialization_Errors_Default_Provide var providerMock = Substitute.For(); providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - providerMock.When(x => x.Initialize(context)).Throw(new Exception("BAD THINGS")); + providerMock.When(x => x.InitializeAsync(context)).Throw(new Exception("BAD THINGS")); var callCount = 0; Exception receivedError = null; - await repository.SetProvider(providerMock, context, afterError: (theProvider, error) => + await repository.SetProviderAsync(providerMock, context, afterError: (theProvider, error) => { Assert.Equal(providerMock, theProvider); callCount++; @@ -99,8 +99,8 @@ public async Task Initialize_Is_Not_Called_For_Ready_Provider(ProviderStatus sta var providerMock = Substitute.For(); providerMock.GetStatus().Returns(status); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(providerMock, context); - providerMock.DidNotReceive().Initialize(context); + await repository.SetProviderAsync(providerMock, context); + providerMock.DidNotReceive().InitializeAsync(context); } [Theory] @@ -114,7 +114,7 @@ public async Task AfterInitialize_Is_Not_Called_For_Ready_Provider(ProviderStatu providerMock.GetStatus().Returns(status); var context = new EvaluationContextBuilder().Build(); var callCount = 0; - await repository.SetProvider(providerMock, context, afterInitialization: provider => { callCount++; }); + await repository.SetProviderAsync(providerMock, context, afterInitialization: provider => { callCount++; }); Assert.Equal(0, callCount); } @@ -129,10 +129,10 @@ public async Task Replaced_Default_Provider_Is_Shutdown() provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); - await repository.SetProvider(provider2, context); - provider1.Received(1).Shutdown(); - provider2.DidNotReceive().Shutdown(); + await repository.SetProviderAsync(provider1, context); + await repository.SetProviderAsync(provider2, context); + provider1.Received(1).ShutdownAsync(); + provider2.DidNotReceive().ShutdownAsync(); } [Fact] @@ -146,9 +146,9 @@ public async Task AfterShutdown_Is_Called_For_Shutdown_Provider() provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); + await repository.SetProviderAsync(provider1, context); var callCount = 0; - await repository.SetProvider(provider2, context, afterShutdown: provider => + await repository.SetProviderAsync(provider2, context, afterShutdown: provider => { Assert.Equal(provider, provider1); callCount++; @@ -161,17 +161,17 @@ public async Task AfterError_Is_Called_For_Shutdown_That_Throws() { var repository = new ProviderRepository(); var provider1 = Substitute.For(); - provider1.Shutdown().Throws(new Exception("SHUTDOWN ERROR")); + provider1.ShutdownAsync().Throws(new Exception("SHUTDOWN ERROR")); provider1.GetStatus().Returns(ProviderStatus.NotReady); var provider2 = Substitute.For(); provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); + await repository.SetProviderAsync(provider1, context); var callCount = 0; Exception errorThrown = null; - await repository.SetProvider(provider2, context, afterError: (provider, ex) => + await repository.SetProviderAsync(provider2, context, afterError: (provider, ex) => { Assert.Equal(provider, provider1); errorThrown = ex; @@ -187,7 +187,7 @@ public void Named_Provider_Provider_Is_Set_Without_Await() var repository = new ProviderRepository(); var provider = new NoOpFeatureProvider(); var context = new EvaluationContextBuilder().Build(); - repository.SetProvider("the-name", provider, context); + repository.SetProviderAsync("the-name", provider, context); Assert.Equal(provider, repository.GetProvider("the-name")); } @@ -199,7 +199,7 @@ public void AfterSet_Is_Invoked_For_Setting_Named_Provider() var context = new EvaluationContextBuilder().Build(); var callCount = 0; // The setting of the provider is synchronous, so the afterSet should be as well. - repository.SetProvider("the-name", provider, context, afterSet: (theProvider) => + repository.SetProviderAsync("the-name", provider, context, afterSet: (theProvider) => { callCount++; Assert.Equal(provider, theProvider); @@ -214,9 +214,9 @@ public async Task Initialization_Provider_Method_Is_Invoked_For_Setting_Named_Pr var providerMock = Substitute.For(); providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("the-name", providerMock, context); - providerMock.Received(1).Initialize(context); - providerMock.DidNotReceive().Shutdown(); + await repository.SetProviderAsync("the-name", providerMock, context); + providerMock.Received(1).InitializeAsync(context); + providerMock.DidNotReceive().ShutdownAsync(); } [Fact] @@ -227,7 +227,7 @@ public async Task AfterInitialization_Is_Invoked_For_Setting_Named_Provider() providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); var callCount = 0; - await repository.SetProvider("the-name", providerMock, context, afterInitialization: (theProvider) => + await repository.SetProviderAsync("the-name", providerMock, context, afterInitialization: (theProvider) => { Assert.Equal(providerMock, theProvider); callCount++; @@ -242,10 +242,10 @@ public async Task AfterError_Is_Invoked_If_Initialization_Errors_Named_Provider( var providerMock = Substitute.For(); providerMock.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - providerMock.When(x => x.Initialize(context)).Throw(new Exception("BAD THINGS")); + providerMock.When(x => x.InitializeAsync(context)).Throw(new Exception("BAD THINGS")); var callCount = 0; Exception receivedError = null; - await repository.SetProvider("the-provider", providerMock, context, afterError: (theProvider, error) => + await repository.SetProviderAsync("the-provider", providerMock, context, afterError: (theProvider, error) => { Assert.Equal(providerMock, theProvider); callCount++; @@ -265,8 +265,8 @@ public async Task Initialize_Is_Not_Called_For_Ready_Named_Provider(ProviderStat var providerMock = Substitute.For(); providerMock.GetStatus().Returns(status); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("the-name", providerMock, context); - providerMock.DidNotReceive().Initialize(context); + await repository.SetProviderAsync("the-name", providerMock, context); + providerMock.DidNotReceive().InitializeAsync(context); } [Theory] @@ -280,7 +280,7 @@ public async Task AfterInitialize_Is_Not_Called_For_Ready_Named_Provider(Provide providerMock.GetStatus().Returns(status); var context = new EvaluationContextBuilder().Build(); var callCount = 0; - await repository.SetProvider("the-name", providerMock, context, + await repository.SetProviderAsync("the-name", providerMock, context, afterInitialization: provider => { callCount++; }); Assert.Equal(0, callCount); } @@ -296,10 +296,10 @@ public async Task Replaced_Named_Provider_Is_Shutdown() provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("the-name", provider1, context); - await repository.SetProvider("the-name", provider2, context); - provider1.Received(1).Shutdown(); - provider2.DidNotReceive().Shutdown(); + await repository.SetProviderAsync("the-name", provider1, context); + await repository.SetProviderAsync("the-name", provider2, context); + provider1.Received(1).ShutdownAsync(); + provider2.DidNotReceive().ShutdownAsync(); } [Fact] @@ -313,9 +313,9 @@ public async Task AfterShutdown_Is_Called_For_Shutdown_Named_Provider() provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("the-provider", provider1, context); + await repository.SetProviderAsync("the-provider", provider1, context); var callCount = 0; - await repository.SetProvider("the-provider", provider2, context, afterShutdown: provider => + await repository.SetProviderAsync("the-provider", provider2, context, afterShutdown: provider => { Assert.Equal(provider, provider1); callCount++; @@ -328,17 +328,17 @@ public async Task AfterError_Is_Called_For_Shutdown_Named_Provider_That_Throws() { var repository = new ProviderRepository(); var provider1 = Substitute.For(); - provider1.Shutdown().Throws(new Exception("SHUTDOWN ERROR")); + provider1.ShutdownAsync().Throws(new Exception("SHUTDOWN ERROR")); provider1.GetStatus().Returns(ProviderStatus.NotReady); var provider2 = Substitute.For(); provider2.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("the-name", provider1, context); + await repository.SetProviderAsync("the-name", provider1, context); var callCount = 0; Exception errorThrown = null; - await repository.SetProvider("the-name", provider2, context, afterError: (provider, ex) => + await repository.SetProviderAsync("the-name", provider2, context, afterError: (provider, ex) => { Assert.Equal(provider, provider1); errorThrown = ex; @@ -360,12 +360,12 @@ public async Task In_Use_Provider_Named_And_Default_Is_Not_Shutdown() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); - await repository.SetProvider("A", provider1, context); + await repository.SetProviderAsync(provider1, context); + await repository.SetProviderAsync("A", provider1, context); // Provider one is replaced for "A", but not default. - await repository.SetProvider("A", provider2, context); + await repository.SetProviderAsync("A", provider2, context); - provider1.DidNotReceive().Shutdown(); + provider1.DidNotReceive().ShutdownAsync(); } [Fact] @@ -380,12 +380,12 @@ public async Task In_Use_Provider_Two_Named_Is_Not_Shutdown() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("B", provider1, context); - await repository.SetProvider("A", provider1, context); + await repository.SetProviderAsync("B", provider1, context); + await repository.SetProviderAsync("A", provider1, context); // Provider one is replaced for "A", but not "B". - await repository.SetProvider("A", provider2, context); + await repository.SetProviderAsync("A", provider2, context); - provider1.DidNotReceive().Shutdown(); + provider1.DidNotReceive().ShutdownAsync(); } [Fact] @@ -400,13 +400,13 @@ public async Task When_All_Instances_Are_Removed_Shutdown_Is_Called() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("B", provider1, context); - await repository.SetProvider("A", provider1, context); + await repository.SetProviderAsync("B", provider1, context); + await repository.SetProviderAsync("A", provider1, context); - await repository.SetProvider("A", provider2, context); - await repository.SetProvider("B", provider2, context); + await repository.SetProviderAsync("A", provider2, context); + await repository.SetProviderAsync("B", provider2, context); - provider1.Received(1).Shutdown(); + provider1.Received(1).ShutdownAsync(); } [Fact] @@ -421,8 +421,8 @@ public async Task Can_Get_Providers_By_Name() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("A", provider1, context); - await repository.SetProvider("B", provider2, context); + await repository.SetProviderAsync("A", provider1, context); + await repository.SetProviderAsync("B", provider2, context); Assert.Equal(provider1, repository.GetProvider("A")); Assert.Equal(provider2, repository.GetProvider("B")); @@ -440,8 +440,8 @@ public async Task Replaced_Named_Provider_Gets_Latest_Set() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider("A", provider1, context); - await repository.SetProvider("A", provider2, context); + await repository.SetProviderAsync("A", provider1, context); + await repository.SetProviderAsync("A", provider2, context); Assert.Equal(provider2, repository.GetProvider("A")); } @@ -461,17 +461,17 @@ public async Task Can_Shutdown_All_Providers() var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); - await repository.SetProvider("provider1", provider1, context); - await repository.SetProvider("provider2", provider2, context); - await repository.SetProvider("provider2a", provider2, context); - await repository.SetProvider("provider3", provider3, context); + await repository.SetProviderAsync(provider1, context); + await repository.SetProviderAsync("provider1", provider1, context); + await repository.SetProviderAsync("provider2", provider2, context); + await repository.SetProviderAsync("provider2a", provider2, context); + await repository.SetProviderAsync("provider3", provider3, context); - await repository.Shutdown(); + await repository.ShutdownAsync(); - provider1.Received(1).Shutdown(); - provider2.Received(1).Shutdown(); - provider3.Received(1).Shutdown(); + provider1.Received(1).ShutdownAsync(); + provider2.Received(1).ShutdownAsync(); + provider3.Received(1).ShutdownAsync(); } [Fact] @@ -480,27 +480,27 @@ public async Task Errors_During_Shutdown_Propagate() var repository = new ProviderRepository(); var provider1 = Substitute.For(); provider1.GetStatus().Returns(ProviderStatus.NotReady); - provider1.Shutdown().Throws(new Exception("SHUTDOWN ERROR 1")); + provider1.ShutdownAsync().Throws(new Exception("SHUTDOWN ERROR 1")); var provider2 = Substitute.For(); provider2.GetStatus().Returns(ProviderStatus.NotReady); - provider2.Shutdown().Throws(new Exception("SHUTDOWN ERROR 2")); + provider2.ShutdownAsync().Throws(new Exception("SHUTDOWN ERROR 2")); var provider3 = Substitute.For(); provider3.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider1, context); - await repository.SetProvider("provider1", provider1, context); - await repository.SetProvider("provider2", provider2, context); - await repository.SetProvider("provider2a", provider2, context); - await repository.SetProvider("provider3", provider3, context); + await repository.SetProviderAsync(provider1, context); + await repository.SetProviderAsync("provider1", provider1, context); + await repository.SetProviderAsync("provider2", provider2, context); + await repository.SetProviderAsync("provider2a", provider2, context); + await repository.SetProviderAsync("provider3", provider3, context); var callCountShutdown1 = 0; var callCountShutdown2 = 0; var totalCallCount = 0; - await repository.Shutdown(afterError: (provider, exception) => + await repository.ShutdownAsync(afterError: (provider, exception) => { totalCallCount++; if (provider == provider1) @@ -519,9 +519,9 @@ await repository.Shutdown(afterError: (provider, exception) => Assert.Equal(1, callCountShutdown1); Assert.Equal(1, callCountShutdown2); - provider1.Received(1).Shutdown(); - provider2.Received(1).Shutdown(); - provider3.Received(1).Shutdown(); + provider1.Received(1).ShutdownAsync(); + provider2.Received(1).ShutdownAsync(); + provider3.Received(1).ShutdownAsync(); } [Fact] @@ -531,12 +531,12 @@ public async Task Setting_Same_Default_Provider_Has_No_Effect() var provider = Substitute.For(); provider.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider, context); - await repository.SetProvider(provider, context); + await repository.SetProviderAsync(provider, context); + await repository.SetProviderAsync(provider, context); Assert.Equal(provider, repository.GetProvider()); - provider.Received(1).Initialize(context); - provider.DidNotReceive().Shutdown(); + provider.Received(1).InitializeAsync(context); + provider.DidNotReceive().ShutdownAsync(); } [Fact] @@ -546,12 +546,12 @@ public async Task Setting_Null_Default_Provider_Has_No_Effect() var provider = Substitute.For(); provider.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(provider, context); - await repository.SetProvider(null, context); + await repository.SetProviderAsync(provider, context); + await repository.SetProviderAsync(null, context); Assert.Equal(provider, repository.GetProvider()); - provider.Received(1).Initialize(context); - provider.DidNotReceive().Shutdown(); + provider.Received(1).InitializeAsync(context); + provider.DidNotReceive().ShutdownAsync(); } [Fact] @@ -566,10 +566,10 @@ public async Task Setting_Null_Named_Provider_Removes_It() defaultProvider.GetStatus().Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProvider(defaultProvider, context); + await repository.SetProviderAsync(defaultProvider, context); - await repository.SetProvider("named-provider", namedProvider, context); - await repository.SetProvider("named-provider", null, context); + await repository.SetProviderAsync("named-provider", namedProvider, context); + await repository.SetProviderAsync("named-provider", null, context); Assert.Equal(defaultProvider, repository.GetProvider("named-provider")); } @@ -582,15 +582,15 @@ public async Task Setting_Named_Provider_With_Null_Name_Has_No_Effect() var defaultProvider = Substitute.For(); defaultProvider.GetStatus().Returns(ProviderStatus.NotReady); - await repository.SetProvider(defaultProvider, context); + await repository.SetProviderAsync(defaultProvider, context); var namedProvider = Substitute.For(); namedProvider.GetStatus().Returns(ProviderStatus.NotReady); - await repository.SetProvider(null, namedProvider, context); + await repository.SetProviderAsync(null, namedProvider, context); - namedProvider.DidNotReceive().Initialize(context); - namedProvider.DidNotReceive().Shutdown(); + namedProvider.DidNotReceive().InitializeAsync(context); + namedProvider.DidNotReceive().ShutdownAsync(); Assert.Equal(defaultProvider, repository.GetProvider(null)); } diff --git a/test/OpenFeature.Tests/TestImplementations.cs b/test/OpenFeature.Tests/TestImplementations.cs index 9683e7ef..0bda65f4 100644 --- a/test/OpenFeature.Tests/TestImplementations.cs +++ b/test/OpenFeature.Tests/TestImplementations.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading; using System.Threading.Tasks; using OpenFeature.Constant; using OpenFeature.Model; @@ -11,25 +12,25 @@ public class TestHookNoOverride : Hook { } public class TestHook : Hook { - public override Task Before(HookContext context, IReadOnlyDictionary hints = null) + public override ValueTask BeforeAsync(HookContext context, IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.FromResult(EvaluationContext.Empty); + return new ValueTask(EvaluationContext.Empty); } - public override Task After(HookContext context, FlagEvaluationDetails details, - IReadOnlyDictionary hints = null) + public override ValueTask AfterAsync(HookContext context, FlagEvaluationDetails details, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } - public override Task Error(HookContext context, Exception error, IReadOnlyDictionary hints = null) + public override ValueTask ErrorAsync(HookContext context, Exception error, IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } - public override Task Finally(HookContext context, IReadOnlyDictionary hints = null) + public override ValueTask FinallyAsync(HookContext context, IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) { - return Task.CompletedTask; + return new ValueTask(); } } @@ -64,32 +65,32 @@ public override Metadata GetMetadata() return new Metadata(this.Name); } - public override Task> ResolveBooleanValue(string flagKey, bool defaultValue, - EvaluationContext context = null) + public override Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(new ResolutionDetails(flagKey, !defaultValue)); } - public override Task> ResolveStringValue(string flagKey, string defaultValue, - EvaluationContext context = null) + public override Task> ResolveStringValueAsync(string flagKey, string defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(new ResolutionDetails(flagKey, defaultValue)); } - public override Task> ResolveIntegerValue(string flagKey, int defaultValue, - EvaluationContext context = null) + public override Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(new ResolutionDetails(flagKey, defaultValue)); } - public override Task> ResolveDoubleValue(string flagKey, double defaultValue, - EvaluationContext context = null) + public override Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(new ResolutionDetails(flagKey, defaultValue)); } - public override Task> ResolveStructureValue(string flagKey, Value defaultValue, - EvaluationContext context = null) + public override Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) { return Task.FromResult(new ResolutionDetails(flagKey, defaultValue)); } @@ -104,16 +105,16 @@ public void SetStatus(ProviderStatus status) this._status = status; } - public override Task Initialize(EvaluationContext context) + public override async ValueTask InitializeAsync(EvaluationContext context, CancellationToken cancellationToken = default) { this._status = ProviderStatus.Ready; - this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = ProviderEventTypes.ProviderReady, ProviderName = this.GetMetadata().Name }); - return base.Initialize(context); + await this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = ProviderEventTypes.ProviderReady, ProviderName = this.GetMetadata().Name }, cancellationToken).ConfigureAwait(false); + await base.InitializeAsync(context, cancellationToken).ConfigureAwait(false); } - internal void SendEvent(ProviderEventTypes eventType) + internal ValueTask SendEventAsync(ProviderEventTypes eventType, CancellationToken cancellationToken = default) { - this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name }); + return this.EventChannel.Writer.WriteAsync(new ProviderEventPayload { Type = eventType, ProviderName = this.GetMetadata().Name }, cancellationToken); } } }