Skip to content

Commit 2de3a97

Browse files
kylejuliandevrenovate[bot]askpt
authored
test: Add E2E Steps for contextMerging.feature tests (#422)
<!-- Please use this template for your pull request. --> <!-- Please use the sections that you need and delete other sections --> ## This PR - Add new Reqnroll steps for contextMerging.feature ahead of merging #395 ### Related Issues <!-- add here the GitHub issue that this PR resolves if applicable --> Fixes #399 ### Notes <!-- any additional notes for this PR --> I was following the [requirement 3.2.3](https://openfeature.dev/specification/sections/evaluation-context#requirement-323) I'm assuming with these additions that "Before Hook" is the same as "Invocation" in the dotnet-sdk. If this is not the case then please let me know! In order to fetch the merged context I use a test hook with a function that can set the EvaluationContext on the state. There are probably better abstractions to use I haven't updated the spec submodule in these changes either, although I can add that if needed Let me know if you have any concerns or feedback 🏗️ ### Follow-up Tasks <!-- anything that is related to this PR but not done here should be noted under this section --> <!-- if there is a need for a new issue, please link it here --> ### How to test <!-- if applicable, add testing instructions under this section --> --------- Signed-off-by: Kyle Julian <[email protected]> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: André Silva <[email protected]>
1 parent c692ec2 commit 2de3a97

File tree

5 files changed

+200
-3
lines changed

5 files changed

+200
-3
lines changed

test/OpenFeature.E2ETests/Steps/BaseStepDefinitions.cs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading;
24
using System.Threading.Tasks;
35
using OpenFeature.E2ETests.Utils;
46
using OpenFeature.Model;
@@ -18,10 +20,10 @@ public BaseStepDefinitions(State state)
1820
}
1921

2022
[Given(@"a stable provider")]
21-
public void GivenAStableProvider()
23+
public async Task GivenAStableProvider()
2224
{
2325
var memProvider = new InMemoryProvider(E2EFlagConfig);
24-
Api.Instance.SetProviderAsync(memProvider).Wait();
26+
await Api.Instance.SetProviderAsync(memProvider).ConfigureAwait(false);
2527
this.State.Client = Api.Instance.GetClient("TestClient", "1.0.0");
2628
}
2729

@@ -57,6 +59,54 @@ public void GivenAString_FlagWithKeyAndADefaultValue(string key, string defaultT
5759
this.State.Flag = flagState;
5860
}
5961

62+
[Given("a stable provider with retrievable context is registered")]
63+
public async Task GivenAStableProviderWithRetrievableContextIsRegistered()
64+
{
65+
this.State.ContextStoringProvider = new ContextStoringProvider();
66+
67+
await Api.Instance.SetProviderAsync(this.State.ContextStoringProvider).ConfigureAwait(false);
68+
69+
Api.Instance.SetTransactionContextPropagator(new AsyncLocalTransactionContextPropagator());
70+
71+
this.State.Client = Api.Instance.GetClient("TestClient", "1.0.0");
72+
}
73+
74+
[Given(@"A context entry with key ""(.*)"" and value ""(.*)"" is added to the ""(.*)"" level")]
75+
public void GivenAContextEntryWithKeyAndValueIsAddedToTheLevel(string key, string value, string level)
76+
{
77+
var context = EvaluationContext.Builder()
78+
.Set(key, value)
79+
.Build();
80+
81+
this.InitializeContext(level, context);
82+
}
83+
84+
[Given("A table with levels of increasing precedence")]
85+
public void GivenATableWithLevelsOfIncreasingPrecedence(DataTable dataTable)
86+
{
87+
var items = dataTable.Rows.ToList();
88+
89+
var levels = items.Select(r => r.Values.First());
90+
91+
this.State.ContextPrecedenceLevels = levels.ToArray();
92+
}
93+
94+
[Given(@"Context entries for each level from API level down to the ""(.*)"" level, with key ""(.*)"" and value ""(.*)""")]
95+
public void GivenContextEntriesForEachLevelFromAPILevelDownToTheLevelWithKeyAndValue(string currentLevel, string key, string value)
96+
{
97+
if (this.State.ContextPrecedenceLevels == null)
98+
this.State.ContextPrecedenceLevels = new string[0];
99+
100+
foreach (var level in this.State.ContextPrecedenceLevels)
101+
{
102+
var context = EvaluationContext.Builder()
103+
.Set(key, value)
104+
.Build();
105+
106+
this.InitializeContext(level, context);
107+
}
108+
}
109+
60110
[When(@"the flag was evaluated with details")]
61111
public async Task WhenTheFlagWasEvaluatedWithDetails()
62112
{
@@ -82,6 +132,54 @@ public async Task WhenTheFlagWasEvaluatedWithDetails()
82132
break;
83133
}
84134
}
135+
private void InitializeContext(string level, EvaluationContext context)
136+
{
137+
switch (level)
138+
{
139+
case "API":
140+
{
141+
Api.Instance.SetContext(context);
142+
break;
143+
}
144+
case "Transaction":
145+
{
146+
Api.Instance.SetTransactionContext(context);
147+
break;
148+
}
149+
case "Client":
150+
{
151+
if (this.State.Client != null)
152+
{
153+
this.State.Client.SetContext(context);
154+
}
155+
else
156+
{
157+
throw new PendingStepException("You must initialise a FeatureClient before adding some EvaluationContext");
158+
}
159+
break;
160+
}
161+
case "Invocation":
162+
{
163+
this.State.InvocationEvaluationContext = context;
164+
break;
165+
}
166+
case "Before Hooks": // Assumed before hooks is the same as Invocation
167+
{
168+
if (this.State.Client != null)
169+
{
170+
this.State.Client.AddHooks(new BeforeHook(context));
171+
}
172+
else
173+
{
174+
throw new PendingStepException("You must initialise a FeatureClient before adding some EvaluationContext");
175+
}
176+
177+
break;
178+
}
179+
default:
180+
throw new PendingStepException("Context level not defined");
181+
}
182+
}
85183

86184
private static readonly IDictionary<string, Flag> E2EFlagConfig = new Dictionary<string, Flag>
87185
{
@@ -159,4 +257,19 @@ public async Task WhenTheFlagWasEvaluatedWithDetails()
159257
)
160258
}
161259
};
260+
261+
public class BeforeHook : Hook
262+
{
263+
private readonly EvaluationContext context;
264+
265+
public BeforeHook(EvaluationContext context)
266+
{
267+
this.context = context;
268+
}
269+
270+
public override ValueTask<EvaluationContext> BeforeAsync<T>(HookContext<T> context, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default)
271+
{
272+
return new ValueTask<EvaluationContext>(this.context);
273+
}
274+
}
162275
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Threading.Tasks;
2+
using OpenFeature.E2ETests.Utils;
3+
using Reqnroll;
4+
using Xunit;
5+
6+
namespace OpenFeature.E2ETests.Steps;
7+
8+
[Binding]
9+
[Scope(Feature = "Context merging precedence")]
10+
public class ContextMergingPrecedenceStepDefinitions : BaseStepDefinitions
11+
{
12+
public ContextMergingPrecedenceStepDefinitions(State state) : base(state)
13+
{
14+
}
15+
16+
[When("Some flag was evaluated")]
17+
public async Task WhenSomeFlagWasEvaluated()
18+
{
19+
this.State.Flag = new FlagState("boolean-flag", "true", FlagType.Boolean);
20+
this.State.FlagResult = await this.State.Client!.GetBooleanValueAsync("boolean-flag", true, this.State.InvocationEvaluationContext).ConfigureAwait(false);
21+
}
22+
23+
[Then(@"The merged context contains an entry with key ""(.*)"" and value ""(.*)""")]
24+
public void ThenTheMergedContextContainsAnEntryWithKeyAndValue(string key, string value)
25+
{
26+
var provider = this.State.ContextStoringProvider;
27+
28+
var mergedContext = provider!.EvaluationContext!;
29+
30+
Assert.NotNull(mergedContext);
31+
32+
var actualValue = mergedContext.GetValue(key);
33+
Assert.Contains(value, actualValue.AsString);
34+
}
35+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using OpenFeature.Model;
4+
5+
namespace OpenFeature.E2ETests.Utils;
6+
7+
public class ContextStoringProvider : FeatureProvider
8+
{
9+
private EvaluationContext? evaluationContext;
10+
public EvaluationContext? EvaluationContext { get => this.evaluationContext; }
11+
12+
public override Metadata? GetMetadata()
13+
{
14+
return new Metadata("ContextStoringProvider");
15+
}
16+
17+
public override Task<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
18+
{
19+
this.evaluationContext = context;
20+
return Task.FromResult(new ResolutionDetails<bool>(flagKey, defaultValue));
21+
}
22+
23+
public override Task<ResolutionDetails<double>> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
24+
{
25+
this.evaluationContext = context;
26+
return Task.FromResult(new ResolutionDetails<double>(flagKey, defaultValue));
27+
}
28+
29+
public override Task<ResolutionDetails<int>> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
30+
{
31+
this.evaluationContext = context;
32+
return Task.FromResult(new ResolutionDetails<int>(flagKey, defaultValue));
33+
}
34+
35+
public override Task<ResolutionDetails<string>> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
36+
{
37+
this.evaluationContext = context;
38+
return Task.FromResult(new ResolutionDetails<string>(flagKey, defaultValue));
39+
}
40+
41+
public override Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
42+
{
43+
this.evaluationContext = context;
44+
return Task.FromResult(new ResolutionDetails<Value>(flagKey, defaultValue));
45+
}
46+
}

test/OpenFeature.E2ETests/Utils/State.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ public class State
1010
public TestHook? TestHook;
1111
public object? FlagResult;
1212
public EvaluationContext? EvaluationContext;
13+
public ContextStoringProvider? ContextStoringProvider;
14+
public EvaluationContext? InvocationEvaluationContext;
15+
public string[]? ContextPrecedenceLevels;
1316
}

0 commit comments

Comments
 (0)