From c2058686301e92b41006d34c2f1bc0e8f36c3d35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:54:19 +0000 Subject: [PATCH 1/4] chore(deps): update dependency xunit.runner.visualstudio to v3 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1dbc878a..d61e40f5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -29,7 +29,7 @@ - + From f042faa6f89a3cfae33e5a614181df65357a9fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:28:02 +0000 Subject: [PATCH 2/4] Use the correct xUnit version. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- Directory.Packages.props | 12 ++++++------ .../OpenFeature.DependencyInjection.Tests.csproj | 2 +- .../OpenFeature.E2ETests/OpenFeature.E2ETests.csproj | 2 +- .../OpenFeature.IntegrationTests.csproj | 2 +- test/OpenFeature.Tests/OpenFeature.Tests.csproj | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d61e40f5..41d7af66 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,9 +1,9 @@ - + true - + @@ -15,7 +15,7 @@ - + @@ -28,13 +28,13 @@ - + - + - + diff --git a/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj b/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj index 9937e1bc..8c27f710 100644 --- a/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj +++ b/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj @@ -23,7 +23,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj b/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj index d91b338e..14388e2a 100644 --- a/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj +++ b/test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj @@ -21,7 +21,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj b/test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj index 8287b2ec..26beea4b 100644 --- a/test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj +++ b/test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj @@ -13,7 +13,7 @@ - + diff --git a/test/OpenFeature.Tests/OpenFeature.Tests.csproj b/test/OpenFeature.Tests/OpenFeature.Tests.csproj index bfadbf9b..07fe14cc 100644 --- a/test/OpenFeature.Tests/OpenFeature.Tests.csproj +++ b/test/OpenFeature.Tests/OpenFeature.Tests.csproj @@ -19,7 +19,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 34a73590e6fb2515c4c071223975c9f26165b953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:22:22 +0000 Subject: [PATCH 3/4] Changing to use cancellation token. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- .../FeatureLifecycleManagerTests.cs | 6 +- .../FeatureFlagIntegrationTest.cs | 11 +- .../ClearOpenFeatureInstanceFixture.cs | 9 + .../OpenFeature.Tests/FeatureProviderTests.cs | 45 ++- .../OpenFeatureClientTests.cs | 121 ++++--- .../OpenFeatureEventTests.cs | 20 +- .../OpenFeature.Tests/OpenFeatureHookTests.cs | 308 +++++++++--------- test/OpenFeature.Tests/OpenFeatureTests.cs | 24 +- .../ProviderRepositoryTests.cs | 111 ++++--- .../Providers/Memory/InMemoryProviderTests.cs | 31 +- 10 files changed, 346 insertions(+), 340 deletions(-) diff --git a/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs b/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs index b0176bc4..b2aeb105 100644 --- a/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs +++ b/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs @@ -12,9 +12,11 @@ public class FeatureLifecycleManagerTests { private readonly FeatureLifecycleManager _systemUnderTest; private readonly IServiceProvider _mockServiceProvider; + private readonly CancellationToken _testCancellationToken; public FeatureLifecycleManagerTests() { + this._testCancellationToken = TestContext.Current.CancellationToken; Api.Instance.SetContext(null); Api.Instance.ClearHooks(); @@ -41,7 +43,7 @@ public async Task EnsureInitializedAsync_ShouldLogAndSetProvider_WhenProviderExi _mockServiceProvider.GetService(typeof(FeatureProvider)).Returns(featureProvider); // Act - await _systemUnderTest.EnsureInitializedAsync().ConfigureAwait(true); + await _systemUnderTest.EnsureInitializedAsync(_testCancellationToken).ConfigureAwait(true); // Assert Api.Instance.GetProvider().Should().BeSameAs(featureProvider); @@ -54,7 +56,7 @@ public async Task EnsureInitializedAsync_ShouldThrowException_WhenProviderDoesNo _mockServiceProvider.GetService(typeof(FeatureProvider)).Returns(null as FeatureProvider); // Act - var act = () => _systemUnderTest.EnsureInitializedAsync().AsTask(); + var act = () => _systemUnderTest.EnsureInitializedAsync(_testCancellationToken).AsTask(); // Assert var exception = await Assert.ThrowsAsync(act).ConfigureAwait(true); diff --git a/test/OpenFeature.IntegrationTests/FeatureFlagIntegrationTest.cs b/test/OpenFeature.IntegrationTests/FeatureFlagIntegrationTest.cs index 559bf4bb..4b07e97b 100644 --- a/test/OpenFeature.IntegrationTests/FeatureFlagIntegrationTest.cs +++ b/test/OpenFeature.IntegrationTests/FeatureFlagIntegrationTest.cs @@ -14,6 +14,13 @@ namespace OpenFeature.IntegrationTests; public class FeatureFlagIntegrationTest { + public FeatureFlagIntegrationTest() + { + this._testCancellationToken = TestContext.Current.CancellationToken; + } + + private readonly CancellationToken _testCancellationToken; + // TestUserId is "off", other users are "on" private const string FeatureA = "feature-a"; private const string TestUserId = "123"; @@ -50,8 +57,8 @@ public async Task VerifyFeatureFlagBehaviorAcrossServiceLifetimesAsync(string us var requestUri = $"/features/{userId}/flags/{FeatureA}"; // Act - var response = await client.GetAsync(requestUri).ConfigureAwait(true); - var responseContent = await response.Content.ReadFromJsonAsync>().ConfigureAwait(true); ; + var response = await client.GetAsync(requestUri, _testCancellationToken).ConfigureAwait(true); + var responseContent = await response.Content.ReadFromJsonAsync>(cancellationToken: _testCancellationToken).ConfigureAwait(true); ; // Assert response.IsSuccessStatusCode.Should().BeTrue("Expected HTTP status code 200 OK."); diff --git a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs index 5a620214..1152f456 100644 --- a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs +++ b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs @@ -1,9 +1,18 @@ using System; +using System.Threading; +using Xunit; namespace OpenFeature.Tests; public class ClearOpenFeatureInstanceFixture : IDisposable { + protected readonly CancellationToken TestCancellationToken; + + protected ClearOpenFeatureInstanceFixture() + { + this.TestCancellationToken = TestContext.Current.CancellationToken; + } + // Make sure the singleton is cleared between tests public void Dispose() { diff --git a/test/OpenFeature.Tests/FeatureProviderTests.cs b/test/OpenFeature.Tests/FeatureProviderTests.cs index 53a67443..dc116240 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.ResolveBooleanValueAsync(flagName, defaultBoolValue)).Should() + (await provider.ResolveBooleanValueAsync(flagName, defaultBoolValue, cancellationToken: this.TestCancellationToken)).Should() .BeEquivalentTo(boolResolutionDetails); var integerResolutionDetails = new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveIntegerValueAsync(flagName, defaultIntegerValue)).Should() + (await provider.ResolveIntegerValueAsync(flagName, defaultIntegerValue, cancellationToken: this.TestCancellationToken)).Should() .BeEquivalentTo(integerResolutionDetails); var doubleResolutionDetails = new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveDoubleValueAsync(flagName, defaultDoubleValue)).Should() + (await provider.ResolveDoubleValueAsync(flagName, defaultDoubleValue, cancellationToken: this.TestCancellationToken)).Should() .BeEquivalentTo(doubleResolutionDetails); var stringResolutionDetails = new ResolutionDetails(flagName, defaultStringValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveStringValueAsync(flagName, defaultStringValue)).Should() + (await provider.ResolveStringValueAsync(flagName, defaultStringValue, cancellationToken: this.TestCancellationToken)).Should() .BeEquivalentTo(stringResolutionDetails); var structureResolutionDetails = new ResolutionDetails(flagName, defaultStructureValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (await provider.ResolveStructureValueAsync(flagName, defaultStructureValue)).Should() + (await provider.ResolveStructureValueAsync(flagName, defaultStructureValue, cancellationToken: this.TestCancellationToken)).Should() .BeEquivalentTo(structureResolutionDetails); } @@ -84,59 +84,52 @@ public async Task Provider_Must_ErrorType() var providerMock = Substitute.For(); const string testMessage = "An error message"; - providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName, defaultBoolValue, ErrorType.General, + providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultBoolValue, ErrorType.General, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.ParseError, + providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultIntegerValue, ErrorType.ParseError, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.InvalidContext, + providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultDoubleValue, ErrorType.InvalidContext, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStringValueAsync(flagName, defaultStringValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName, defaultStringValue, ErrorType.TypeMismatch, + providerMock.ResolveStringValueAsync(flagName, defaultStringValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultStringValue, ErrorType.TypeMismatch, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName, defaultStructureValue, ErrorType.FlagNotFound, + providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultStructureValue, ErrorType.FlagNotFound, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName2, defaultStructureValue, ErrorType.ProviderNotReady, + providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName2, defaultStructureValue, ErrorType.ProviderNotReady, NoOpProvider.ReasonNoOp, NoOpProvider.Variant, testMessage)); - providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue, Arg.Any()) - .Returns(new ResolutionDetails(flagName2, defaultBoolValue, ErrorType.TargetingKeyMissing, + providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName2, defaultBoolValue, ErrorType.TargetingKeyMissing, NoOpProvider.ReasonNoOp, NoOpProvider.Variant)); - var boolRes = await providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue); + var boolRes = await providerMock.ResolveBooleanValueAsync(flagName, defaultBoolValue, cancellationToken: this.TestCancellationToken); boolRes.ErrorType.Should().Be(ErrorType.General); boolRes.ErrorMessage.Should().Be(testMessage); - var intRes = await providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue); + var intRes = await providerMock.ResolveIntegerValueAsync(flagName, defaultIntegerValue, cancellationToken: this.TestCancellationToken); intRes.ErrorType.Should().Be(ErrorType.ParseError); intRes.ErrorMessage.Should().Be(testMessage); - var doubleRes = await providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue); + var doubleRes = await providerMock.ResolveDoubleValueAsync(flagName, defaultDoubleValue, cancellationToken: this.TestCancellationToken); doubleRes.ErrorType.Should().Be(ErrorType.InvalidContext); doubleRes.ErrorMessage.Should().Be(testMessage); - var stringRes = await providerMock.ResolveStringValueAsync(flagName, defaultStringValue); + var stringRes = await providerMock.ResolveStringValueAsync(flagName, defaultStringValue, cancellationToken: this.TestCancellationToken); stringRes.ErrorType.Should().Be(ErrorType.TypeMismatch); stringRes.ErrorMessage.Should().Be(testMessage); - var structRes1 = await providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue); + var structRes1 = await providerMock.ResolveStructureValueAsync(flagName, defaultStructureValue, cancellationToken: this.TestCancellationToken); structRes1.ErrorType.Should().Be(ErrorType.FlagNotFound); structRes1.ErrorMessage.Should().Be(testMessage); - var structRes2 = await providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue); + var structRes2 = await providerMock.ResolveStructureValueAsync(flagName2, defaultStructureValue, cancellationToken: this.TestCancellationToken); structRes2.ErrorType.Should().Be(ErrorType.ProviderNotReady); structRes2.ErrorMessage.Should().Be(testMessage); - var boolRes2 = await providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue); + var boolRes2 = await providerMock.ResolveBooleanValueAsync(flagName2, defaultBoolValue, cancellationToken: this.TestCancellationToken); 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 13d3fa93..3b9a23ac 100644 --- a/test/OpenFeature.Tests/OpenFeatureClientTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureClientTests.cs @@ -81,25 +81,25 @@ public async Task OpenFeatureClient_Should_Allow_Flag_Evaluation() await Api.Instance.SetProviderAsync(new NoOpFeatureProvider()); var client = Api.Instance.GetClient(domain, clientVersion); - (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.GetBooleanValueAsync(flagName, defaultBoolValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultBoolValue); + (await client.GetBooleanValueAsync(flagName, defaultBoolValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().Be(defaultBoolValue); + (await client.GetBooleanValueAsync(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().Be(defaultBoolValue); - (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.GetIntegerValueAsync(flagName, defaultIntegerValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultIntegerValue); + (await client.GetIntegerValueAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().Be(defaultIntegerValue); + (await client.GetIntegerValueAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().Be(defaultIntegerValue); - (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.GetDoubleValueAsync(flagName, defaultDoubleValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultDoubleValue); + (await client.GetDoubleValueAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().Be(defaultDoubleValue); + (await client.GetDoubleValueAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().Be(defaultDoubleValue); - (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.GetStringValueAsync(flagName, defaultStringValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultStringValue); + (await client.GetStringValueAsync(flagName, defaultStringValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().Be(defaultStringValue); + (await client.GetStringValueAsync(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().Be(defaultStringValue); - (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); + (await client.GetObjectValueAsync(flagName, defaultStructureValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(defaultStructureValue); + (await client.GetObjectValueAsync(flagName, defaultStructureValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(defaultStructureValue); + (await client.GetObjectValueAsync(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(defaultStructureValue); } [Fact] @@ -128,29 +128,29 @@ public async Task OpenFeatureClient_Should_Allow_Details_Flag_Evaluation() var client = Api.Instance.GetClient(domain, clientVersion); var boolFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultBoolValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (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); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(boolFlagEvaluationDetails); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(boolFlagEvaluationDetails); + (await client.GetBooleanDetailsAsync(flagName, defaultBoolValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(boolFlagEvaluationDetails); var integerFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultIntegerValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (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); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(integerFlagEvaluationDetails); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(integerFlagEvaluationDetails); + (await client.GetIntegerDetailsAsync(flagName, defaultIntegerValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(integerFlagEvaluationDetails); var doubleFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultDoubleValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (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); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); + (await client.GetDoubleDetailsAsync(flagName, defaultDoubleValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(doubleFlagEvaluationDetails); var stringFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStringValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (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); + (await client.GetStringDetailsAsync(flagName, defaultStringValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(stringFlagEvaluationDetails); + (await client.GetStringDetailsAsync(flagName, defaultStringValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(stringFlagEvaluationDetails); + (await client.GetStringDetailsAsync(flagName, defaultStringValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(stringFlagEvaluationDetails); var structureFlagEvaluationDetails = new FlagEvaluationDetails(flagName, defaultStructureValue, ErrorType.None, NoOpProvider.ReasonNoOp, NoOpProvider.Variant); - (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); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(structureFlagEvaluationDetails); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue, EvaluationContext.Empty, cancellationToken: this.TestCancellationToken)).Should().BeEquivalentTo(structureFlagEvaluationDetails); + (await client.GetObjectDetailsAsync(flagName, defaultStructureValue, EvaluationContext.Empty, emptyFlagOptions, this.TestCancellationToken)).Should().BeEquivalentTo(structureFlagEvaluationDetails); } [Fact] @@ -171,18 +171,18 @@ public async Task OpenFeatureClient_Should_Return_DefaultValue_When_Type_Mismatc var mockedLogger = Substitute.For>(); // This will fail to case a String to TestStructure - mockedFeatureProvider.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Throws(); + mockedFeatureProvider.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Throws(); mockedFeatureProvider.GetMetadata().Returns(new Metadata(fixture.Create())); mockedFeatureProvider.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(mockedFeatureProvider); var client = Api.Instance.GetClient(domain, clientVersion, mockedLogger); - var evaluationDetails = await client.GetObjectDetailsAsync(flagName, defaultValue); + var evaluationDetails = await client.GetObjectDetailsAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken); evaluationDetails.ErrorType.Should().Be(ErrorType.TypeMismatch); evaluationDetails.ErrorMessage.Should().Be(new InvalidCastException().Message); - _ = mockedFeatureProvider.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); + _ = mockedFeatureProvider.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); mockedLogger.Received(1).IsEnabled(LogLevel.Error); } @@ -248,7 +248,7 @@ public async Task Must_Short_Circuit_Not_Ready() Assert.Equal(ProviderStatus.NotReady, provider.Status); _ = Api.Instance.SetProviderAsync(name, provider); - var details = await client.GetStringDetailsAsync("some-flag", defaultStr); + var details = await client.GetStringDetailsAsync("some-flag", defaultStr, cancellationToken: this.TestCancellationToken); Assert.Equal(defaultStr, details.Value); Assert.Equal(ErrorType.ProviderNotReady, details.ErrorType); Assert.Equal(Reason.Error, details.Reason); @@ -267,7 +267,7 @@ public async Task Must_Short_Circuit_Fatal() Assert.Equal(ProviderStatus.NotReady, provider.Status); _ = Api.Instance.SetProviderAsync(name, provider); - var details = await client.GetStringDetailsAsync("some-flag", defaultStr); + var details = await client.GetStringDetailsAsync("some-flag", defaultStr, cancellationToken: this.TestCancellationToken); Assert.Equal(defaultStr, details.Value); Assert.Equal(ErrorType.ProviderFatal, details.ErrorType); Assert.Equal(Reason.Error, details.Reason); @@ -283,16 +283,16 @@ public async Task Should_Resolve_BooleanValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - (await client.GetBooleanValueAsync(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetBooleanValueAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveBooleanValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -305,16 +305,16 @@ public async Task Should_Resolve_StringValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStringValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveStringValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - (await client.GetStringValueAsync(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetStringValueAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveStringValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStringValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -327,16 +327,16 @@ public async Task Should_Resolve_IntegerValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - (await client.GetIntegerValueAsync(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetIntegerValueAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveIntegerValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -349,16 +349,16 @@ public async Task Should_Resolve_DoubleValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - (await client.GetDoubleValueAsync(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetDoubleValueAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveDoubleValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -371,16 +371,16 @@ public async Task Should_Resolve_StructureValue() var defaultValue = fixture.Create(); var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Returns(new ResolutionDetails(flagName, defaultValue)); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails(flagName, defaultValue)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - (await client.GetObjectValueAsync(flagName, defaultValue)).Should().Be(defaultValue); + (await client.GetObjectValueAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken)).Should().Be(defaultValue); - _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -394,18 +394,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.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, "ERROR", null, testMessage))); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).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.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - var response = await client.GetObjectDetailsAsync(flagName, defaultValue); + var response = await client.GetObjectDetailsAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -419,18 +419,18 @@ public async Task When_Exception_Occurs_During_Evaluation_Should_Return_Error() const string testMessage = "Couldn't parse flag data."; var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()).Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Throws(new FeatureProviderException(ErrorType.ParseError, testMessage)); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); await Api.Instance.SetProviderAsync(featureProviderMock); var client = Api.Instance.GetClient(domain, clientVersion); - var response = await client.GetObjectDetailsAsync(flagName, defaultValue); + var response = await client.GetObjectDetailsAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); - _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); + _ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -444,8 +444,7 @@ public async Task When_Error_Is_Returned_From_Provider_Should_Not_Run_After_Hook const string testMessage = "Couldn't parse flag data."; var featureProviderMock = Substitute.For(); - featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()) - .Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, + featureProviderMock.ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken).Returns(Task.FromResult(new ResolutionDetails(flagName, defaultValue, ErrorType.ParseError, "ERROR", null, testMessage))); featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create())); featureProviderMock.GetProviderHooks().Returns(ImmutableList.Empty); @@ -454,13 +453,13 @@ public async Task When_Error_Is_Returned_From_Provider_Should_Not_Run_After_Hook var client = Api.Instance.GetClient(domain, clientVersion); var testHook = new TestHook(); client.AddHooks(testHook); - var response = await client.GetObjectDetailsAsync(flagName, defaultValue); + var response = await client.GetObjectDetailsAsync(flagName, defaultValue, cancellationToken: this.TestCancellationToken); response.ErrorType.Should().Be(ErrorType.ParseError); response.Reason.Should().Be(Reason.Error); response.ErrorMessage.Should().Be(testMessage); _ = featureProviderMock.Received(1) - .ResolveStructureValueAsync(flagName, defaultValue, Arg.Any()); + .ResolveStructureValueAsync(flagName, defaultValue, Arg.Any(), this.TestCancellationToken); Assert.Equal(1, testHook.BeforeCallCount); Assert.Equal(0, testHook.AfterCallCount); @@ -487,7 +486,7 @@ public async Task Cancellation_Token_Added_Is_Passed_To_Provider() var token = args.ArgAt(3); while (!token.IsCancellationRequested) { - await Task.Delay(10); // artificially delay until cancelled + await Task.Delay(10, token); // artificially delay until cancelled } return new ResolutionDetails(flagName, defaultString, ErrorType.None, cancelledReason); diff --git a/test/OpenFeature.Tests/OpenFeatureEventTests.cs b/test/OpenFeature.Tests/OpenFeatureEventTests.cs index a4b0d111..97c5a1f2 100644 --- a/test/OpenFeature.Tests/OpenFeatureEventTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureEventTests.cs @@ -78,9 +78,9 @@ public async Task API_Level_Event_Handlers_Should_Be_Registered() var testProvider = new TestProvider(); await Api.Instance.SetProviderAsync(testProvider); - await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); - await testProvider.SendEventAsync(ProviderEventTypes.ProviderError); - await testProvider.SendEventAsync(ProviderEventTypes.ProviderStale); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderError, this.TestCancellationToken); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderStale, this.TestCancellationToken); await Utils.AssertUntilAsync(_ => eventHandler .Received() @@ -226,12 +226,12 @@ public async Task API_Level_Event_Handlers_Should_Be_Exchangeable() var testProvider = new TestProvider(); await Api.Instance.SetProviderAsync(testProvider); - await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); var newTestProvider = new TestProvider(); await Api.Instance.SetProviderAsync(newTestProvider); - await newTestProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await newTestProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); await Utils.AssertUntilAsync( _ => eventHandler.Received(2).Invoke(Arg.Is(payload => payload.ProviderName == testProvider.GetMetadata().Name && payload.Type == ProviderEventTypes.ProviderReady)) @@ -405,7 +405,7 @@ public async Task Client_Level_Event_Handlers_Should_Be_Receive_Events_From_Name client.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, clientEventHandler); - await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); // verify that the client received the event from the default provider as there is no named provider registered yet await Utils.AssertUntilAsync( @@ -417,8 +417,8 @@ await Utils.AssertUntilAsync( await Api.Instance.SetProviderAsync(client.GetMetadata().Name!, clientProvider); // now, send another event for the default handler - await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); - await clientProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged); + await defaultProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); + await clientProvider.SendEventAsync(ProviderEventTypes.ProviderConfigurationChanged, this.TestCancellationToken); // now the client should have received only the event from the named provider await Utils.AssertUntilAsync( @@ -481,7 +481,7 @@ await Utils.AssertUntilAsync( myClient.RemoveHandler(ProviderEventTypes.ProviderReady, eventHandler); // send another event from the provider - this one should not be received - await testProvider.SendEventAsync(ProviderEventTypes.ProviderReady); + await testProvider.SendEventAsync(ProviderEventTypes.ProviderReady, this.TestCancellationToken); // wait a bit and make sure we only have received the first event, but nothing after removing the event handler await Utils.AssertUntilAsync( @@ -513,7 +513,7 @@ public async Task Provider_Events_Should_Update_ProviderStatus(ProviderEventType { var provider = new TestProvider(); await Api.Instance.SetProviderAsync("5.3.5", provider); - _ = provider.SendEventAsync(type); + _ = provider.SendEventAsync(type, this.TestCancellationToken); await Utils.AssertUntilAsync(_ => Assert.True(provider.Status == status)); } } diff --git a/test/OpenFeature.Tests/OpenFeatureHookTests.cs b/test/OpenFeature.Tests/OpenFeatureHookTests.cs index cc8b08a1..3883370e 100644 --- a/test/OpenFeature.Tests/OpenFeatureHookTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureHookTests.cs @@ -37,18 +37,18 @@ public async Task Hooks_Should_Be_Called_In_Order() var providerHook = Substitute.For(); // Sequence - 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()); + apiHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); + clientHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); + invocationHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); + providerHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); + providerHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + invocationHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + clientHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + apiHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + providerHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + invocationHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + clientHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); + apiHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); var testProvider = new TestProvider(); testProvider.AddHook(providerHook); @@ -57,37 +57,36 @@ public async Task Hooks_Should_Be_Called_In_Order() var client = Api.Instance.GetClient(domain, clientVersion); client.AddHooks(clientHook); - await client.GetBooleanValueAsync(flagName, defaultValue, EvaluationContext.Empty, - new FlagEvaluationOptions(invocationHook, ImmutableDictionary.Empty)); + await client.GetBooleanValueAsync(flagName, defaultValue, EvaluationContext.Empty, new FlagEvaluationOptions(invocationHook, ImmutableDictionary.Empty), this.TestCancellationToken); Received.InOrder(() => { - 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.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + clientHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + invocationHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + providerHook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + providerHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + invocationHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + clientHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + apiHook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + providerHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + invocationHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + clientHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + apiHook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); }); - _ = 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>()); + _ = apiHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = clientHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = invocationHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = providerHook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = providerHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = invocationHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = clientHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = apiHook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = providerHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = invocationHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = clientHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = apiHook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); } [Fact] @@ -141,15 +140,14 @@ public async Task Evaluation_Context_Must_Be_Mutable_Before_Hook() FlagValueType.Boolean, new ClientMetadata("test", "1.0.0"), new Metadata(NoOpProvider.NoOpProviderName), evaluationContext); - hook1.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(evaluationContext); - hook2.BeforeAsync(hookContext, Arg.Any>()).Returns(evaluationContext); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(evaluationContext); + hook2.BeforeAsync(hookContext, Arg.Any>(), this.TestCancellationToken).Returns(evaluationContext); var client = Api.Instance.GetClient("test", "1.0.0"); - await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, - new FlagEvaluationOptions(ImmutableList.Create(hook1, hook2), ImmutableDictionary.Empty)); + await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, new FlagEvaluationOptions(ImmutableList.Create(hook1, hook2), ImmutableDictionary.Empty), this.TestCancellationToken); - _ = hook1.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>()); - _ = hook2.Received(1).BeforeAsync(Arg.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), Arg.Any>()); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = hook2.Received(1).BeforeAsync(Arg.Is>(a => a.EvaluationContext.GetValue("test").AsString == "test"), Arg.Any>(), this.TestCancellationToken); } [Fact] @@ -197,16 +195,16 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order() provider.GetProviderHooks().Returns(ImmutableList.Empty); - provider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new ResolutionDetails("test", true)); + provider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails("test", true)); await Api.Instance.SetProviderAsync(provider); var hook = Substitute.For(); - hook.BeforeAsync(Arg.Any>(), Arg.Any>()).Returns(hookContext); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(hookContext); var client = Api.Instance.GetClient("test", "1.0.0", null, clientContext); - await client.GetBooleanValueAsync("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook), ImmutableDictionary.Empty)); + await client.GetBooleanValueAsync("test", false, invocationContext, new FlagEvaluationOptions(ImmutableList.Create(hook), ImmutableDictionary.Empty), this.TestCancellationToken); // after proper merging, all properties should equal true _ = provider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Is(y => @@ -217,7 +215,7 @@ public async Task Evaluation_Context_Must_Be_Merged_In_Correct_Order() && (y.GetValue(propClientToOverwrite).AsBoolean ?? false) && (y.GetValue(propHook).AsBoolean ?? false) && (y.GetValue(propInvocationToOverwrite).AsBoolean ?? false) - )); + ), this.TestCancellationToken); } [Fact] @@ -240,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.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); + await hook.BeforeAsync(hookContext, hookHints, this.TestCancellationToken); + await hook.AfterAsync(hookContext, new FlagEvaluationDetails("test", false, ErrorType.None, "testing", "testing"), hookHints, this.TestCancellationToken); + await hook.FinallyAsync(hookContext, hookHints, this.TestCancellationToken); + await hook.ErrorAsync(hookContext, new Exception(), hookHints, this.TestCancellationToken); hookContext.ClientMetadata.Name.Should().BeNull(); hookContext.ClientMetadata.Version.Should().BeNull(); @@ -266,29 +264,30 @@ public async Task Hook_Should_Execute_In_Correct_Order() featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - 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>()); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails("test", false)); + _ = hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); client.AddHooks(hook); - await client.GetBooleanValueAsync("test", false); + await client.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken); Received.InOrder(() => { - 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.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); }); - _ = 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()); + _ = hook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = hook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.Received(1); + _ = hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = featureProvider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken); } [Fact] @@ -306,8 +305,7 @@ public async Task Register_Hooks_Should_Be_Available_At_All_Levels() await Api.Instance.SetProviderAsync(testProvider); var client = Api.Instance.GetClient(); client.AddHooks(hook2); - await client.GetBooleanValueAsync("test", false, null, - new FlagEvaluationOptions(hook3, ImmutableDictionary.Empty)); + await client.GetBooleanValueAsync("test", false, null, new FlagEvaluationOptions(hook3, ImmutableDictionary.Empty), this.TestCancellationToken); Assert.Single(Api.Instance.GetHooks()); client.GetHooks().Count().Should().Be(1); @@ -326,39 +324,39 @@ public async Task Finally_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - 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()); + hook1.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(EvaluationContext.Empty); + hook2.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(EvaluationContext.Empty); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails("test", false)); + hook2.AfterAsync(Arg.Any>(), Arg.Any>(), null, this.TestCancellationToken).Returns(new ValueTask()); + hook1.AfterAsync(context: Arg.Any>(), Arg.Any>(), null, cancellationToken: this.TestCancellationToken).Returns(new ValueTask()); + hook2.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(new ValueTask()); + hook1.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken).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.GetBooleanValueAsync("test", false); + await client.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken); Received.InOrder(() => { - 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.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + hook2.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken); + hook2.AfterAsync(Arg.Any>(), Arg.Any>(), null, this.TestCancellationToken); + hook1.AfterAsync(Arg.Any>(), Arg.Any>(), null, this.TestCancellationToken); + hook2.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); + hook1.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); }); - _ = 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); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = hook2.Received(1).BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = featureProvider.Received(1).ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken); + _ = hook2.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), null, this.TestCancellationToken); + _ = hook1.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), null, this.TestCancellationToken); + _ = hook2.Received(1).FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = hook1.Received(1).FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); } [Fact] @@ -373,31 +371,31 @@ public async Task Error_Hook_Should_Be_Executed_Even_If_Abnormal_Termination() featureProvider1.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - 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()); + hook1.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(EvaluationContext.Empty); + hook2.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(EvaluationContext.Empty); + featureProvider1.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken).Throws(new Exception()); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken).Returns(new ValueTask()); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken).Returns(new ValueTask()); await Api.Instance.SetProviderAsync(featureProvider1); var client = Api.Instance.GetClient(); client.AddHooks(new[] { hook1, hook2 }); - await client.GetBooleanValueAsync("test", false); + await client.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken); Received.InOrder(() => { - 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.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + hook2.BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + featureProvider1.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); }); - _ = 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); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = hook2.Received(1).BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = hook1.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + _ = hook2.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); } [Fact] @@ -412,27 +410,27 @@ public async Task Error_Occurs_During_Before_After_Evaluation_Should_Not_Invoke_ featureProvider.GetProviderHooks().Returns(ImmutableList.Empty); // Sequence - hook1.BeforeAsync(Arg.Any>(), Arg.Any>()).Throws(new Exception()); - _ = hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null); - _ = hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Throws(new Exception()); + _ = hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + _ = hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); client.AddHooks(new[] { hook1, hook2 }); - await client.GetBooleanValueAsync("test", false); + await client.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken); Received.InOrder(() => { - hook1.BeforeAsync(Arg.Any>(), Arg.Any>()); - hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null); - hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null); + hook1.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook2.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + hook1.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); }); - _ = 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); + _ = hook1.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = hook2.DidNotReceive().BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + _ = hook1.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + _ = hook2.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); } [Fact] @@ -449,29 +447,25 @@ public async Task Hook_Hints_May_Be_Optional() featureProvider.GetProviderHooks() .Returns(ImmutableList.Empty); - hook.BeforeAsync(Arg.Any>(), Arg.Any>()) - .Returns(EvaluationContext.Empty); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValueAsync("test", false, Arg.Any()) - .Returns(new ResolutionDetails("test", false)); + featureProvider.ResolveBooleanValueAsync("test", false, Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails("test", false)); - hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()) - .Returns(new ValueTask()); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); - hook.FinallyAsync(Arg.Any>(), Arg.Any>()) - .Returns(new ValueTask()); + hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); - await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, flagOptions); + await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, flagOptions, this.TestCancellationToken); Received.InOrder(() => { - 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>()); + hook.Received().BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + featureProvider.Received().ResolveBooleanValueAsync("test", false, Arg.Any(), this.TestCancellationToken); + hook.Received().AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.Received().FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); }); } @@ -487,26 +481,26 @@ public async Task When_Error_Occurs_In_Before_Hook_Should_Return_Default_Value() featureProvider.GetMetadata().Returns(new Metadata(null)); // Sequence - 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()); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Throws(exceptionToThrow); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken).Returns(new ValueTask()); + hook.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken).Returns(new ValueTask()); var client = Api.Instance.GetClient(); client.AddHooks(hook); - var resolvedFlag = await client.GetBooleanValueAsync("test", true); + var resolvedFlag = await client.GetBooleanValueAsync("test", true, cancellationToken: this.TestCancellationToken); Received.InOrder(() => { - hook.BeforeAsync(Arg.Any>(), Arg.Any>()); - hook.ErrorAsync(Arg.Any>(), Arg.Any(), null); - hook.FinallyAsync(Arg.Any>(), null); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), null, this.TestCancellationToken); + hook.FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); }); resolvedFlag.Should().BeTrue(); - _ = hook.Received(1).BeforeAsync(Arg.Any>(), null); - _ = hook.Received(1).ErrorAsync(Arg.Any>(), exceptionToThrow, null); - _ = hook.Received(1).FinallyAsync(Arg.Any>(), null); + _ = hook.Received(1).BeforeAsync(Arg.Any>(), null, this.TestCancellationToken); + _ = hook.Received(1).ErrorAsync(Arg.Any>(), exceptionToThrow, null, this.TestCancellationToken); + _ = hook.Received(1).FinallyAsync(Arg.Any>(), null, this.TestCancellationToken); } [Fact] @@ -524,36 +518,31 @@ public async Task When_Error_Occurs_In_After_Hook_Should_Invoke_Error_Hook() featureProvider.GetProviderHooks() .Returns(ImmutableList.Empty); - hook.BeforeAsync(Arg.Any>(), Arg.Any>()) - .Returns(EvaluationContext.Empty); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()) - .Returns(new ResolutionDetails("test", false)); + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), this.TestCancellationToken).Returns(new ResolutionDetails("test", false)); - hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>()) - .Throws(exceptionToThrow); + hook.AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Throws(exceptionToThrow); - hook.ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>()) - .Returns(new ValueTask()); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); - hook.FinallyAsync(Arg.Any>(), Arg.Any>()) - .Returns(new ValueTask()); + hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); - var resolvedFlag = await client.GetBooleanValueAsync("test", true, config: flagOptions); + var resolvedFlag = await client.GetBooleanValueAsync("test", true, config: flagOptions, cancellationToken: this.TestCancellationToken); resolvedFlag.Should().BeTrue(); Received.InOrder(() => { - 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>()); + hook.Received(1).BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.Received(1).AfterAsync(Arg.Any>(), Arg.Any>(), Arg.Any>(), this.TestCancellationToken); + hook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken); }); - await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any()); + await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any(), this.TestCancellationToken); } [Fact] @@ -597,17 +586,14 @@ public async Task Failed_Resolution_Should_Pass_Cancellation_Token() featureProvider.GetProviderHooks() .Returns(ImmutableList.Empty); - hook.BeforeAsync(Arg.Any>(), Arg.Any>()) - .Returns(EvaluationContext.Empty); + hook.BeforeAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(EvaluationContext.Empty); - featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any()) + featureProvider.ResolveBooleanValueAsync(Arg.Any(), Arg.Any(), Arg.Any(), cts.Token) .Throws(exceptionToThrow); - hook.ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>()) - .Returns(new ValueTask()); + hook.ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); - hook.FinallyAsync(Arg.Any>(), Arg.Any>()) - .Returns(new ValueTask()); + hook.FinallyAsync(Arg.Any>(), Arg.Any>(), this.TestCancellationToken).Returns(new ValueTask()); await Api.Instance.SetProviderAsync(featureProvider); var client = Api.Instance.GetClient(); @@ -618,7 +604,7 @@ public async Task Failed_Resolution_Should_Pass_Cancellation_Token() _ = hook.Received(1).ErrorAsync(Arg.Any>(), Arg.Any(), Arg.Any>(), cts.Token); _ = hook.Received(1).FinallyAsync(Arg.Any>(), Arg.Any>(), cts.Token); - await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any()); + await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any(), this.TestCancellationToken); } [Fact] diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index acc53b61..9f9c778e 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -31,13 +31,13 @@ public async Task OpenFeature_Should_Initialize_Provider() providerMockDefault.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync(providerMockDefault); - await providerMockDefault.Received(1).InitializeAsync(Api.Instance.GetContext()); + await providerMockDefault.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); var providerMockNamed = Substitute.For(); providerMockNamed.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync("the-name", providerMockNamed); - await providerMockNamed.Received(1).InitializeAsync(Api.Instance.GetContext()); + await providerMockNamed.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); } [Fact] @@ -49,27 +49,27 @@ public async Task OpenFeature_Should_Shutdown_Unused_Provider() providerA.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync(providerA); - await providerA.Received(1).InitializeAsync(Api.Instance.GetContext()); + await providerA.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); var providerB = Substitute.For(); providerB.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync(providerB); - await providerB.Received(1).InitializeAsync(Api.Instance.GetContext()); - await providerA.Received(1).ShutdownAsync(); + await providerB.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); + await providerA.Received(1).ShutdownAsync(this.TestCancellationToken); var providerC = Substitute.For(); providerC.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync("named", providerC); - await providerC.Received(1).InitializeAsync(Api.Instance.GetContext()); + await providerC.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); var providerD = Substitute.For(); providerD.Status.Returns(ProviderStatus.NotReady); await Api.Instance.SetProviderAsync("named", providerD); - await providerD.Received(1).InitializeAsync(Api.Instance.GetContext()); - await providerC.Received(1).ShutdownAsync(); + await providerD.Received(1).InitializeAsync(Api.Instance.GetContext(), this.TestCancellationToken); + await providerC.Received(1).ShutdownAsync(this.TestCancellationToken); } [Fact] @@ -87,8 +87,8 @@ public async Task OpenFeature_Should_Support_Shutdown() await Api.Instance.ShutdownAsync(); - await providerA.Received(1).ShutdownAsync(); - await providerB.Received(1).ShutdownAsync(); + await providerA.Received(1).ShutdownAsync(this.TestCancellationToken); + await providerB.Received(1).ShutdownAsync(this.TestCancellationToken); } [Fact] @@ -241,8 +241,8 @@ public async Task OpenFeature_Should_Allow_Multiple_Client_Mapping() client1.GetMetadata().Name.Should().Be("client1"); client2.GetMetadata().Name.Should().Be("client2"); - (await client1.GetBooleanValueAsync("test", false)).Should().BeTrue(); - (await client2.GetBooleanValueAsync("test", false)).Should().BeFalse(); + (await client1.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken)).Should().BeTrue(); + (await client2.GetBooleanValueAsync("test", false, cancellationToken: this.TestCancellationToken)).Should().BeFalse(); } } } diff --git a/test/OpenFeature.Tests/ProviderRepositoryTests.cs b/test/OpenFeature.Tests/ProviderRepositoryTests.cs index e88de6e9..29052fad 100644 --- a/test/OpenFeature.Tests/ProviderRepositoryTests.cs +++ b/test/OpenFeature.Tests/ProviderRepositoryTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; using NSubstitute; using OpenFeature.Constant; @@ -14,6 +15,13 @@ namespace OpenFeature.Tests [SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task")] public class ProviderRepositoryTests { + private readonly CancellationToken _testCancellationToken; + + public ProviderRepositoryTests() + { + this._testCancellationToken = TestContext.Current.CancellationToken; + } + [Fact] public async Task Default_Provider_Is_Set_Without_Await() { @@ -32,8 +40,8 @@ public async Task Initialization_Provider_Method_Is_Invoked_For_Setting_Default_ providerMock.Status.Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); await repository.SetProviderAsync(providerMock, context); - providerMock.Received(1).InitializeAsync(context); - providerMock.DidNotReceive().ShutdownAsync(); + providerMock.Received(1).InitializeAsync(context, _testCancellationToken); + providerMock.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -60,7 +68,7 @@ public async Task AfterError_Is_Invoked_If_Initialization_Errors_Default_Provide var providerMock = Substitute.For(); providerMock.Status.Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - providerMock.When(x => x.InitializeAsync(context)).Throw(new Exception("BAD THINGS")); + providerMock.When(x => x.InitializeAsync(context, _testCancellationToken)).Throw(new Exception("BAD THINGS")); var callCount = 0; Exception? receivedError = null; await repository.SetProviderAsync(providerMock, context, afterInitError: (theProvider, error) => @@ -85,7 +93,7 @@ internal async Task Initialize_Is_Not_Called_For_Ready_Provider(ProviderStatus s providerMock.Status.Returns(status); var context = new EvaluationContextBuilder().Build(); await repository.SetProviderAsync(providerMock, context); - providerMock.DidNotReceive().InitializeAsync(context); + providerMock.DidNotReceive().InitializeAsync(context, _testCancellationToken); } [Theory] @@ -120,8 +128,8 @@ public async Task Replaced_Default_Provider_Is_Shutdown() var context = new EvaluationContextBuilder().Build(); await repository.SetProviderAsync(provider1, context); await repository.SetProviderAsync(provider2, context); - provider1.Received(1).ShutdownAsync(); - provider2.DidNotReceive().ShutdownAsync(); + provider1.Received(1).ShutdownAsync(_testCancellationToken); + provider2.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -131,7 +139,7 @@ public async Task Named_Provider_Provider_Is_Set_Without_Await() var provider = new NoOpFeatureProvider(); var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("the-name", provider, context); + await repository.SetProviderAsync("the-name", provider, context, cancellationToken: _testCancellationToken); Assert.Equal(provider, repository.GetProvider("the-name")); } @@ -142,9 +150,9 @@ public async Task Initialization_Provider_Method_Is_Invoked_For_Setting_Named_Pr var providerMock = Substitute.For(); providerMock.Status.Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("the-name", providerMock, context); - providerMock.Received(1).InitializeAsync(context); - providerMock.DidNotReceive().ShutdownAsync(); + await repository.SetProviderAsync("the-name", providerMock, context, cancellationToken: _testCancellationToken); + providerMock.Received(1).InitializeAsync(context, _testCancellationToken); + providerMock.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -160,7 +168,7 @@ await repository.SetProviderAsync("the-name", providerMock, context, afterInitSu Assert.Equal(providerMock, theProvider); callCount++; return Task.CompletedTask; - }); + }, cancellationToken: _testCancellationToken); Assert.Equal(1, callCount); } @@ -171,7 +179,7 @@ public async Task AfterError_Is_Invoked_If_Initialization_Errors_Named_Provider( var providerMock = Substitute.For(); providerMock.Status.Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - providerMock.When(x => x.InitializeAsync(context)).Throw(new Exception("BAD THINGS")); + providerMock.When(x => x.InitializeAsync(context, _testCancellationToken)).Throw(new Exception("BAD THINGS")); var callCount = 0; Exception? receivedError = null; await repository.SetProviderAsync("the-provider", providerMock, context, afterInitError: (theProvider, error) => @@ -180,7 +188,7 @@ await repository.SetProviderAsync("the-provider", providerMock, context, afterIn callCount++; receivedError = error; return Task.CompletedTask; - }); + }, cancellationToken: _testCancellationToken); Assert.Equal("BAD THINGS", receivedError?.Message); Assert.Equal(1, callCount); } @@ -195,8 +203,8 @@ internal async Task Initialize_Is_Not_Called_For_Ready_Named_Provider(ProviderSt var providerMock = Substitute.For(); providerMock.Status.Returns(status); var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("the-name", providerMock, context); - providerMock.DidNotReceive().InitializeAsync(context); + await repository.SetProviderAsync("the-name", providerMock, context, cancellationToken: _testCancellationToken); + providerMock.DidNotReceive().InitializeAsync(context, _testCancellationToken); } [Theory] @@ -210,12 +218,11 @@ internal async Task AfterInitialize_Is_Not_Called_For_Ready_Named_Provider(Provi providerMock.Status.Returns(status); var context = new EvaluationContextBuilder().Build(); var callCount = 0; - await repository.SetProviderAsync("the-name", providerMock, context, - afterInitSuccess: provider => + await repository.SetProviderAsync("the-name", providerMock, context, afterInitSuccess: provider => { callCount++; return Task.CompletedTask; - }); + }, cancellationToken: _testCancellationToken); Assert.Equal(0, callCount); } @@ -230,10 +237,10 @@ public async Task Replaced_Named_Provider_Is_Shutdown() provider2.Status.Returns(ProviderStatus.NotReady); var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("the-name", provider1, context); - await repository.SetProviderAsync("the-name", provider2, context); - provider1.Received(1).ShutdownAsync(); - provider2.DidNotReceive().ShutdownAsync(); + await repository.SetProviderAsync("the-name", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("the-name", provider2, context, cancellationToken: _testCancellationToken); + provider1.Received(1).ShutdownAsync(_testCancellationToken); + provider2.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -249,11 +256,11 @@ public async Task In_Use_Provider_Named_And_Default_Is_Not_Shutdown() var context = new EvaluationContextBuilder().Build(); await repository.SetProviderAsync(provider1, context); - await repository.SetProviderAsync("A", provider1, context); + await repository.SetProviderAsync("A", provider1, context, cancellationToken: _testCancellationToken); // Provider one is replaced for "A", but not default. - await repository.SetProviderAsync("A", provider2, context); + await repository.SetProviderAsync("A", provider2, context, cancellationToken: _testCancellationToken); - provider1.DidNotReceive().ShutdownAsync(); + provider1.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -268,12 +275,12 @@ public async Task In_Use_Provider_Two_Named_Is_Not_Shutdown() var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("B", provider1, context); - await repository.SetProviderAsync("A", provider1, context); + await repository.SetProviderAsync("B", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("A", provider1, context, cancellationToken: _testCancellationToken); // Provider one is replaced for "A", but not "B". - await repository.SetProviderAsync("A", provider2, context); + await repository.SetProviderAsync("A", provider2, context, cancellationToken: _testCancellationToken); - provider1.DidNotReceive().ShutdownAsync(); + provider1.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -288,13 +295,13 @@ public async Task When_All_Instances_Are_Removed_Shutdown_Is_Called() var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("B", provider1, context); - await repository.SetProviderAsync("A", provider1, context); + await repository.SetProviderAsync("B", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("A", provider1, context, cancellationToken: _testCancellationToken); - await repository.SetProviderAsync("A", provider2, context); - await repository.SetProviderAsync("B", provider2, context); + await repository.SetProviderAsync("A", provider2, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("B", provider2, context, cancellationToken: _testCancellationToken); - provider1.Received(1).ShutdownAsync(); + provider1.Received(1).ShutdownAsync(_testCancellationToken); } [Fact] @@ -309,8 +316,8 @@ public async Task Can_Get_Providers_By_Name() var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("A", provider1, context); - await repository.SetProviderAsync("B", provider2, context); + await repository.SetProviderAsync("A", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("B", provider2, context, cancellationToken: _testCancellationToken); Assert.Equal(provider1, repository.GetProvider("A")); Assert.Equal(provider2, repository.GetProvider("B")); @@ -328,8 +335,8 @@ public async Task Replaced_Named_Provider_Gets_Latest_Set() var context = new EvaluationContextBuilder().Build(); - await repository.SetProviderAsync("A", provider1, context); - await repository.SetProviderAsync("A", provider2, context); + await repository.SetProviderAsync("A", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("A", provider2, context, cancellationToken: _testCancellationToken); Assert.Equal(provider2, repository.GetProvider("A")); } @@ -350,16 +357,16 @@ public async Task Can_Shutdown_All_Providers() var context = new EvaluationContextBuilder().Build(); 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.SetProviderAsync("provider1", provider1, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("provider2", provider2, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("provider2a", provider2, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("provider3", provider3, context, cancellationToken: _testCancellationToken); - await repository.ShutdownAsync(); + await repository.ShutdownAsync(cancellationToken: _testCancellationToken); - provider1.Received(1).ShutdownAsync(); - provider2.Received(1).ShutdownAsync(); - provider3.Received(1).ShutdownAsync(); + provider1.Received(1).ShutdownAsync(_testCancellationToken); + provider2.Received(1).ShutdownAsync(_testCancellationToken); + provider3.Received(1).ShutdownAsync(_testCancellationToken); } [Fact] @@ -373,8 +380,8 @@ public async Task Setting_Same_Default_Provider_Has_No_Effect() await repository.SetProviderAsync(provider, context); Assert.Equal(provider, repository.GetProvider()); - provider.Received(1).InitializeAsync(context); - provider.DidNotReceive().ShutdownAsync(); + provider.Received(1).InitializeAsync(context, _testCancellationToken); + provider.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -388,8 +395,8 @@ public async Task Setting_Null_Default_Provider_Has_No_Effect() await repository.SetProviderAsync(null, context); Assert.Equal(provider, repository.GetProvider()); - provider.Received(1).InitializeAsync(context); - provider.DidNotReceive().ShutdownAsync(); + provider.Received(1).InitializeAsync(context, _testCancellationToken); + provider.DidNotReceive().ShutdownAsync(_testCancellationToken); } [Fact] @@ -406,8 +413,8 @@ public async Task Setting_Null_Named_Provider_Removes_It() var context = new EvaluationContextBuilder().Build(); await repository.SetProviderAsync(defaultProvider, context); - await repository.SetProviderAsync("named-provider", namedProvider, context); - await repository.SetProviderAsync("named-provider", null, context); + await repository.SetProviderAsync("named-provider", namedProvider, context, cancellationToken: _testCancellationToken); + await repository.SetProviderAsync("named-provider", null, context, cancellationToken: _testCancellationToken); Assert.Equal(defaultProvider, repository.GetProvider("named-provider")); } diff --git a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs index c83ce0ce..e4b44418 100644 --- a/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs +++ b/test/OpenFeature.Tests/Providers/Memory/InMemoryProviderTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; using OpenFeature.Constant; using OpenFeature.Error; @@ -13,9 +14,11 @@ namespace OpenFeature.Tests.Providers.Memory public class InMemoryProviderTests { private FeatureProvider commonProvider; + private readonly CancellationToken _testCancellationToken; public InMemoryProviderTests() { + this._testCancellationToken = TestContext.Current.CancellationToken; var provider = new InMemoryProvider(new Dictionary(){ { "boolean-flag", new Flag( @@ -112,7 +115,7 @@ public InMemoryProviderTests() [Fact] public async Task GetBoolean_ShouldEvaluateWithReasonAndVariant() { - ResolutionDetails details = await this.commonProvider.ResolveBooleanValueAsync("boolean-flag", false, EvaluationContext.Empty); + ResolutionDetails details = await this.commonProvider.ResolveBooleanValueAsync("boolean-flag", false, EvaluationContext.Empty, _testCancellationToken); Assert.True(details.Value); Assert.Equal(Reason.Static, details.Reason); Assert.Equal("on", details.Variant); @@ -121,7 +124,7 @@ public async Task GetBoolean_ShouldEvaluateWithReasonAndVariant() [Fact] public async Task GetString_ShouldEvaluateWithReasonAndVariant() { - ResolutionDetails details = await this.commonProvider.ResolveStringValueAsync("string-flag", "nope", EvaluationContext.Empty); + ResolutionDetails details = await this.commonProvider.ResolveStringValueAsync("string-flag", "nope", EvaluationContext.Empty, _testCancellationToken); Assert.Equal("hi", details.Value); Assert.Equal(Reason.Static, details.Reason); Assert.Equal("greeting", details.Variant); @@ -130,7 +133,7 @@ public async Task GetString_ShouldEvaluateWithReasonAndVariant() [Fact] public async Task GetInt_ShouldEvaluateWithReasonAndVariant() { - ResolutionDetails details = await this.commonProvider.ResolveIntegerValueAsync("integer-flag", 13, EvaluationContext.Empty); + ResolutionDetails details = await this.commonProvider.ResolveIntegerValueAsync("integer-flag", 13, EvaluationContext.Empty, _testCancellationToken); Assert.Equal(10, details.Value); Assert.Equal(Reason.Static, details.Reason); Assert.Equal("ten", details.Variant); @@ -139,7 +142,7 @@ public async Task GetInt_ShouldEvaluateWithReasonAndVariant() [Fact] public async Task GetDouble_ShouldEvaluateWithReasonAndVariant() { - ResolutionDetails details = await this.commonProvider.ResolveDoubleValueAsync("float-flag", 13, EvaluationContext.Empty); + ResolutionDetails details = await this.commonProvider.ResolveDoubleValueAsync("float-flag", 13, EvaluationContext.Empty, _testCancellationToken); Assert.Equal(0.5, details.Value); Assert.Equal(Reason.Static, details.Reason); Assert.Equal("half", details.Variant); @@ -148,7 +151,7 @@ public async Task GetDouble_ShouldEvaluateWithReasonAndVariant() [Fact] public async Task GetStruct_ShouldEvaluateWithReasonAndVariant() { - ResolutionDetails details = await this.commonProvider.ResolveStructureValueAsync("object-flag", new Value(), EvaluationContext.Empty); + ResolutionDetails details = await this.commonProvider.ResolveStructureValueAsync("object-flag", new Value(), EvaluationContext.Empty, _testCancellationToken); Assert.Equal(true, details.Value.AsStructure?["showImages"].AsBoolean); Assert.Equal("Check out these pics!", details.Value.AsStructure?["title"].AsString); Assert.Equal(100, details.Value.AsStructure?["imagesPerPage"].AsInteger); @@ -160,7 +163,7 @@ public async Task GetStruct_ShouldEvaluateWithReasonAndVariant() public async Task GetString_ContextSensitive_ShouldEvaluateWithReasonAndVariant() { EvaluationContext context = EvaluationContext.Builder().Set("email", "me@faas.com").Build(); - ResolutionDetails details = await this.commonProvider.ResolveStringValueAsync("context-aware", "nope", context); + ResolutionDetails details = await this.commonProvider.ResolveStringValueAsync("context-aware", "nope", context, _testCancellationToken); Assert.Equal("INTERNAL", details.Value); Assert.Equal(Reason.TargetingMatch, details.Reason); Assert.Equal("internal", details.Variant); @@ -177,25 +180,25 @@ public async Task EmptyFlags_ShouldWork() [Fact] public async Task MissingFlag_ShouldThrow() { - await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("missing-flag", false, EvaluationContext.Empty)); + await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("missing-flag", false, EvaluationContext.Empty, _testCancellationToken)); } [Fact] public async Task MismatchedFlag_ShouldThrow() { - await Assert.ThrowsAsync(() => this.commonProvider.ResolveStringValueAsync("boolean-flag", "nope", EvaluationContext.Empty)); + await Assert.ThrowsAsync(() => this.commonProvider.ResolveStringValueAsync("boolean-flag", "nope", EvaluationContext.Empty, _testCancellationToken)); } [Fact] public async Task MissingDefaultVariant_ShouldThrow() { - await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("invalid-flag", false, EvaluationContext.Empty)); + await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("invalid-flag", false, EvaluationContext.Empty, _testCancellationToken)); } [Fact] public async Task MissingEvaluatedVariant_ShouldThrow() { - await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("invalid-evaluator-flag", false, EvaluationContext.Empty)); + await Assert.ThrowsAsync(() => this.commonProvider.ResolveBooleanValueAsync("invalid-evaluator-flag", false, EvaluationContext.Empty, _testCancellationToken)); } [Fact] @@ -212,7 +215,7 @@ public async Task PutConfiguration_shouldUpdateConfigAndRunHandlers() ) }}); - ResolutionDetails details = await provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty); + ResolutionDetails details = await provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty, _testCancellationToken); Assert.True(details.Value); // update flags @@ -227,13 +230,13 @@ await provider.UpdateFlagsAsync(new Dictionary(){ ) }}); - var res = await provider.GetEventChannel().Reader.ReadAsync() as ProviderEventPayload; + var res = await provider.GetEventChannel().Reader.ReadAsync(_testCancellationToken) as ProviderEventPayload; Assert.Equal(ProviderEventTypes.ProviderConfigurationChanged, res?.Type); - await Assert.ThrowsAsync(() => provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty)); + await Assert.ThrowsAsync(() => provider.ResolveBooleanValueAsync("old-flag", false, EvaluationContext.Empty, _testCancellationToken)); // new flag should be present, old gone (defaults), handler run. - ResolutionDetails detailsAfter = await provider.ResolveStringValueAsync("new-flag", "nope", EvaluationContext.Empty); + ResolutionDetails detailsAfter = await provider.ResolveStringValueAsync("new-flag", "nope", EvaluationContext.Empty, _testCancellationToken); Assert.True(details.Value); Assert.Equal("hi", detailsAfter.Value); } From e2bed96a1f55820cc36848f28f91745c0b95e7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:00:32 +0000 Subject: [PATCH 4/4] Removed controller. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- .../ClearOpenFeatureInstanceFixture.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs index 13fdb33f..09184cca 100644 --- a/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs +++ b/test/OpenFeature.Tests/ClearOpenFeatureInstanceFixture.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Xunit; using System.Threading; using Xunit; @@ -7,22 +6,18 @@ namespace OpenFeature.Tests; public class ClearOpenFeatureInstanceFixture : IAsyncLifetime { - protected readonly CancellationToken TestCancellationToken; + protected CancellationToken TestCancellationToken; - protected ClearOpenFeatureInstanceFixture() + public ValueTask InitializeAsync() { this.TestCancellationToken = TestContext.Current.CancellationToken; - } - - public Task InitializeAsync() - { Api.ResetApi(); - return Task.CompletedTask; + return ValueTask.CompletedTask; } // Make sure the singleton is cleared between tests - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { await Api.Instance.ShutdownAsync().ConfigureAwait(false); }