Skip to content

Commit 036d7a4

Browse files
committed
fixup: add ct specific tests
Signed-off-by: Todd Baert <[email protected]>
1 parent 8bbf69d commit 036d7a4

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

test/OpenFeature.Tests/OpenFeatureClientTests.cs

+38
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Immutable;
33
using System.Diagnostics.CodeAnalysis;
44
using System.Linq;
5+
using System.Threading;
56
using System.Threading.Tasks;
67
using AutoFixture;
78
using FluentAssertions;
@@ -344,6 +345,43 @@ public async Task When_Exception_Occurs_During_Evaluation_Should_Return_Error()
344345
_ = featureProviderMock.Received(1).ResolveStructureValueAsync(flagName, defaultValue, Arg.Any<EvaluationContext>());
345346
}
346347

348+
[Fact]
349+
public async Task Cancellation_Token_Added_Is_Passed_To_Provider()
350+
{
351+
var fixture = new Fixture();
352+
var clientName = fixture.Create<string>();
353+
var clientVersion = fixture.Create<string>();
354+
var flagName = fixture.Create<string>();
355+
var defaultString = fixture.Create<string>();
356+
var cancelledReason = "cancelled";
357+
358+
var cts = new CancellationTokenSource();
359+
360+
361+
var featureProviderMock = Substitute.For<FeatureProvider>();
362+
featureProviderMock.ResolveStringValueAsync(flagName, defaultString, Arg.Any<EvaluationContext>(), Arg.Any<CancellationToken>()).Returns(async args =>
363+
{
364+
var token = args.ArgAt<CancellationToken>(3);
365+
while (!token.IsCancellationRequested)
366+
{
367+
await Task.Delay(10); // artificially delay until cancelled
368+
}
369+
return new ResolutionDetails<string>(flagName, defaultString, ErrorType.None, cancelledReason);
370+
});
371+
featureProviderMock.GetMetadata().Returns(new Metadata(fixture.Create<string>()));
372+
featureProviderMock.GetProviderHooks().Returns(ImmutableList<Hook>.Empty);
373+
374+
await Api.Instance.SetProviderAsync(clientName, featureProviderMock);
375+
var client = Api.Instance.GetClient(clientName, clientVersion);
376+
var task = client.GetStringDetailsAsync(flagName, defaultString, EvaluationContext.Empty, null, cts.Token);
377+
cts.Cancel(); // cancel before awaiting
378+
379+
var response = await task;
380+
response.Value.Should().Be(defaultString);
381+
response.Reason.Should().Be(cancelledReason);
382+
_ = featureProviderMock.Received(1).ResolveStringValueAsync(flagName, defaultString, Arg.Any<EvaluationContext>(), cts.Token);
383+
}
384+
347385
[Fact]
348386
public void Should_Get_And_Set_Context()
349387
{

test/OpenFeature.Tests/OpenFeatureHookTests.cs

+67
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
using System.Collections.Immutable;
44
using System.Diagnostics.CodeAnalysis;
55
using System.Linq;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using AutoFixture;
89
using FluentAssertions;
910
using NSubstitute;
1011
using NSubstitute.ExceptionExtensions;
1112
using OpenFeature.Constant;
13+
using OpenFeature.Error;
1214
using OpenFeature.Model;
1315
using OpenFeature.Tests.Internal;
1416
using Xunit;
@@ -554,6 +556,71 @@ public async Task When_Error_Occurs_In_After_Hook_Should_Invoke_Error_Hook()
554556
await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any<EvaluationContext>());
555557
}
556558

559+
[Fact]
560+
public async Task Successful_Resolution_Should_Pass_Cancellation_Token()
561+
{
562+
var featureProvider = Substitute.For<FeatureProvider>();
563+
var hook = Substitute.For<Hook>();
564+
var cts = new CancellationTokenSource();
565+
566+
featureProvider.GetMetadata().Returns(new Metadata(null));
567+
featureProvider.GetProviderHooks().Returns(ImmutableList<Hook>.Empty);
568+
569+
hook.BeforeAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token).Returns(EvaluationContext.Empty);
570+
featureProvider.ResolveBooleanValueAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<EvaluationContext>(), cts.Token).Returns(new ResolutionDetails<bool>("test", false));
571+
_ = hook.AfterAsync(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token);
572+
_ = hook.FinallyAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token);
573+
574+
await Api.Instance.SetProviderAsync(featureProvider);
575+
var client = Api.Instance.GetClient();
576+
client.AddHooks(hook);
577+
578+
await client.GetBooleanValueAsync("test", false, EvaluationContext.Empty, null, cts.Token);
579+
580+
_ = hook.Received(1).BeforeAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token);
581+
_ = hook.Received(1).AfterAsync(Arg.Any<HookContext<bool>>(), Arg.Any<FlagEvaluationDetails<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token);
582+
_ = hook.Received(1).FinallyAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Dictionary<string, object>>(), cts.Token);
583+
}
584+
585+
[Fact]
586+
public async Task Failed_Resolution_Should_Pass_Cancellation_Token()
587+
{
588+
var featureProvider = Substitute.For<FeatureProvider>();
589+
var hook = Substitute.For<Hook>();
590+
var flagOptions = new FlagEvaluationOptions(hook);
591+
var exceptionToThrow = new GeneralException("Fake Exception");
592+
var cts = new CancellationTokenSource();
593+
594+
featureProvider.GetMetadata()
595+
.Returns(new Metadata(null));
596+
597+
featureProvider.GetProviderHooks()
598+
.Returns(ImmutableList<Hook>.Empty);
599+
600+
hook.BeforeAsync(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
601+
.Returns(EvaluationContext.Empty);
602+
603+
featureProvider.ResolveBooleanValueAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<EvaluationContext>())
604+
.Throws(exceptionToThrow);
605+
606+
hook.ErrorAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Exception>(), Arg.Any<ImmutableDictionary<string, object>>())
607+
.Returns(new ValueTask());
608+
609+
hook.FinallyAsync(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>())
610+
.Returns(new ValueTask());
611+
612+
await Api.Instance.SetProviderAsync(featureProvider);
613+
var client = Api.Instance.GetClient();
614+
615+
await client.GetBooleanValueAsync("test", true, EvaluationContext.Empty, flagOptions, cts.Token);
616+
617+
_ = hook.Received(1).BeforeAsync(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>(), cts.Token);
618+
_ = hook.Received(1).ErrorAsync(Arg.Any<HookContext<bool>>(), Arg.Any<Exception>(), Arg.Any<ImmutableDictionary<string, object>>(), cts.Token);
619+
_ = hook.Received(1).FinallyAsync(Arg.Any<HookContext<bool>>(), Arg.Any<ImmutableDictionary<string, object>>(), cts.Token);
620+
621+
await featureProvider.DidNotReceive().ResolveBooleanValueAsync("test", false, Arg.Any<EvaluationContext>());
622+
}
623+
557624
[Fact]
558625
public void Add_hooks_should_accept_empty_enumerable()
559626
{

0 commit comments

Comments
 (0)