Skip to content

Commit

Permalink
test: Update Evaluation Definitions (#394)
Browse files Browse the repository at this point in the history
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

This pull request introduces several changes to the
`OpenFeature.E2ETests` project, focusing on enhancing configuration and
refactoring the step definitions for better state management and code
clarity. The most important changes include updates to the VS Code
configuration files, refactoring of the `BaseStepDefinitions` and
`EvaluationStepDefinitions` classes, and enhancements to the feature
flag evaluation logic.

Configuration updates:

*
[`.vscode/extensions.json`](diffhunk://#diff-c16655a98a3ee89a7636a59c59a72b0e93649e3a1e947327cfc43a1336b4e912R1-R6):
Added recommendations for the `cucumberopen.cucumber-official` and
`ms-dotnettools.csdevkit` extensions.
*
[`.vscode/settings.json`](diffhunk://#diff-a5de3e5871ffcc383a2294845bd3df25d3eeff6c29ad46e3a396577c413bf357R1-R22):
Enabled file nesting, added patterns for feature files, and excluded
compilation results and other unnecessary files.

Refactoring step definitions:

*
[`test/OpenFeature.E2ETests/Steps/BaseStepDefinitions.cs`](diffhunk://#diff-d602b805929d1f2c0a4e819949274cd996296363676f8f59efd1b2997845f46aL13-R80):
Refactored to use a shared `State` class for managing feature flag state
and evaluation results, replacing internal variables with properties of
the `State` class.
*
[`test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs`](diffhunk://#diff-9ca6e89533e4b3f7a2deaf8de6d6f07a80b7eab2afa6f2e8bfc682b9ca60dc6bL1-R167):
Refactored to inherit from `BaseStepDefinitions`, using the shared
`State` class for managing state and results, and converting synchronous
methods to asynchronous.

Enhancements to feature flag evaluation:

*
[`test/OpenFeature.E2ETests/Steps/BaseStepDefinitions.cs`](diffhunk://#diff-d602b805929d1f2c0a4e819949274cd996296363676f8f59efd1b2997845f46aL13-R80):
Improved the flag evaluation logic by encapsulating flag properties in a
`FlagState` class and updating the evaluation methods to use this new
structure.
*
[`test/OpenFeature.E2ETests/Steps/BaseStepDefinitions.cs`](diffhunk://#diff-d602b805929d1f2c0a4e819949274cd996296363676f8f59efd1b2997845f46aR104-R158):
Added new flag variants and context-aware flag evaluation logic to
handle more complex scenarios.

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->

Fixes #391

---------

Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
  • Loading branch information
askpt authored Mar 7, 2025
1 parent b30350b commit 074de3d
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 399 deletions.
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"cucumberopen.cucumber-official",
"ms-dotnettools.csdevkit"
]
}
22 changes: 22 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
// shows *.feature.cs files as nested items
"*.feature": "${capture}.feature.cs"
},
"files.exclude": {
// excludes compilation result
"**/obj/": true,
"**/bin/": true,
"BenchmarkDotNet.Artifacts/": true,
".idea/": true
},
"cucumber.glue": [
// sets the location of the step definition classes
"test/OpenFeature.E2ETests/Steps/*.cs"
],
"cucumber.features": [
// sets the location of the feature files
"test/OpenFeature.E2ETests/Features/*.feature"
]
}
104 changes: 71 additions & 33 deletions test/OpenFeature.E2ETests/Steps/BaseStepDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,75 +10,74 @@ namespace OpenFeature.E2ETests.Steps;
[Binding]
public class BaseStepDefinitions
{
internal FeatureClient? Client;
private string _flagKey = null!;
private string _defaultValue = null!;
internal FlagType? FlagTypeEnum;
internal object Result = null!;
protected readonly State State;

public BaseStepDefinitions(State state)
{
this.State = state;
}

[Given(@"a stable provider")]
public void GivenAStableProvider()
{
var memProvider = new InMemoryProvider(E2EFlagConfig);
Api.Instance.SetProviderAsync(memProvider).Wait();
this.Client = Api.Instance.GetClient("TestClient", "1.0.0");
this.State.Client = Api.Instance.GetClient("TestClient", "1.0.0");
}

[Given(@"a Boolean-flag with key ""(.*)"" and a default value ""(.*)""")]
[Given(@"a boolean-flag with key ""(.*)"" and a default value ""(.*)""")]
public void GivenABoolean_FlagWithKeyAndADefaultValue(string key, string defaultType)
{
this._flagKey = key;
this._defaultValue = defaultType;
this.FlagTypeEnum = FlagType.Boolean;
var flagState = new FlagState(key, defaultType, FlagType.Boolean);
this.State.Flag = flagState;
}

[Given(@"a Float-flag with key ""(.*)"" and a default value ""(.*)""")]
[Given(@"a float-flag with key ""(.*)"" and a default value ""(.*)""")]
public void GivenAFloat_FlagWithKeyAndADefaultValue(string key, string defaultType)
{
this._flagKey = key;
this._defaultValue = defaultType;
this.FlagTypeEnum = FlagType.Float;
var flagState = new FlagState(key, defaultType, FlagType.Float);
this.State.Flag = flagState;
}

[Given(@"a Integer-flag with key ""(.*)"" and a default value ""(.*)""")]
[Given(@"a integer-flag with key ""(.*)"" and a default value ""(.*)""")]
public void GivenAnInteger_FlagWithKeyAndADefaultValue(string key, string defaultType)
{
this._flagKey = key;
this._defaultValue = defaultType;
this.FlagTypeEnum = FlagType.Integer;
var flagState = new FlagState(key, defaultType, FlagType.Integer);
this.State.Flag = flagState;
}

[Given(@"a String-flag with key ""(.*)"" and a default value ""(.*)""")]
[Given(@"a string-flag with key ""(.*)"" and a default value ""(.*)""")]
public void GivenAString_FlagWithKeyAndADefaultValue(string key, string defaultType)
{
this._flagKey = key;
this._defaultValue = defaultType;
this.FlagTypeEnum = FlagType.String;
var flagState = new FlagState(key, defaultType, FlagType.String);
this.State.Flag = flagState;
}

[When(@"the flag was evaluated with details")]
public async Task WhenTheFlagWasEvaluatedWithDetails()
{
switch (this.FlagTypeEnum)
var flag = this.State.Flag!;

switch (flag.Type)
{
case FlagType.Boolean:
this.Result = await this.Client!
.GetBooleanDetailsAsync(this._flagKey, bool.Parse(this._defaultValue)).ConfigureAwait(false);
this.State.FlagEvaluationDetailsResult = await this.State.Client!
.GetBooleanDetailsAsync(flag.Key, bool.Parse(flag.DefaultValue)).ConfigureAwait(false);
break;
case FlagType.Float:
this.Result = await this.Client!
.GetDoubleDetailsAsync(this._flagKey, double.Parse(this._defaultValue)).ConfigureAwait(false);
this.State.FlagEvaluationDetailsResult = await this.State.Client!
.GetDoubleDetailsAsync(flag.Key, double.Parse(flag.DefaultValue)).ConfigureAwait(false);
break;
case FlagType.Integer:
this.Result = await this.Client!
.GetIntegerDetailsAsync(this._flagKey, int.Parse(this._defaultValue)).ConfigureAwait(false);
this.State.FlagEvaluationDetailsResult = await this.State.Client!
.GetIntegerDetailsAsync(flag.Key, int.Parse(flag.DefaultValue)).ConfigureAwait(false);
break;
case FlagType.String:
this.Result = await this.Client!.GetStringDetailsAsync(this._flagKey, this._defaultValue)
this.State.FlagEvaluationDetailsResult = await this.State.Client!.GetStringDetailsAsync(flag.Key, flag.DefaultValue)
.ConfigureAwait(false);
break;
}
Expand All @@ -102,22 +101,61 @@ public async Task WhenTheFlagWasEvaluatedWithDetails()
defaultVariant: "on"
)
},
{
"string-flag", new Flag<string>(
variants: new Dictionary<string, string>() { { "greeting", "hi" }, { "parting", "bye" } },
defaultVariant: "greeting"
)
},
{
"integer-flag", new Flag<int>(
variants: new Dictionary<string, int> { { "23", 23 }, { "42", 42 } },
defaultVariant: "23"
variants: new Dictionary<string, int>() { { "one", 1 }, { "ten", 10 } },
defaultVariant: "ten"
)
},
{
"float-flag", new Flag<double>(
variants: new Dictionary<string, double> { { "2.3", 2.3 }, { "4.2", 4.2 } },
defaultVariant: "2.3"
variants: new Dictionary<string, double>() { { "tenth", 0.1 }, { "half", 0.5 } },
defaultVariant: "half"
)
},
{
"string-flag", new Flag<string>(
variants: new Dictionary<string, string> { { "value", "value" }, { "value2", "value2" } },
defaultVariant: "value"
"object-flag", new Flag<Value>(
variants: new Dictionary<string, Value>()
{
{ "empty", new Value() },
{
"template", new Value(Structure.Builder()
.Set("showImages", true)
.Set("title", "Check out these pics!")
.Set("imagesPerPage", 100).Build()
)
}
},
defaultVariant: "template"
)
},
{
"context-aware", new Flag<string>(
variants: new Dictionary<string, string>() { { "internal", "INTERNAL" }, { "external", "EXTERNAL" } },
defaultVariant: "external",
(context) =>
{
if (context.GetValue("fn").AsString == "Sulisław"
&& context.GetValue("ln").AsString == "Świętopełk"
&& context.GetValue("age").AsInteger == 29
&& context.GetValue("customer").AsBoolean == false)
{
return "internal";
}
else return "external";
}
)
},
{
"wrong-flag", new Flag<string>(
variants: new Dictionary<string, string>() { { "one", "uno" }, { "two", "dos" } },
defaultVariant: "one"
)
}
};
Expand Down
Loading

0 comments on commit 074de3d

Please sign in to comment.